index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. <template>
  2. <div class="tenant">
  3. <div class="content">
  4. <byTable
  5. :source="sourceList.data"
  6. :pagination="sourceList.pagination"
  7. :config="config"
  8. :loading="loading"
  9. :selectConfig="selectConfig"
  10. highlight-current-row
  11. :statConfig='statConfig'
  12. :action-list="[
  13. {
  14. text: '创建合同',
  15. action: () => newContract(),
  16. },
  17. ]"
  18. @get-list="getList"
  19. >
  20. <template #code="{ item }">
  21. <div style="width: 100%">
  22. <a
  23. style="color: #409eff; cursor: pointer; word-break: break-all"
  24. @click="openDetails(item)"
  25. >{{ item.code }}</a
  26. >
  27. </div>
  28. </template>
  29. <template #amount="{ item }">
  30. <div>
  31. <span style="padding-right: 4px">{{ item.currency }}</span>
  32. <span>{{ moneyFormat(item.amount, 4) }}</span>
  33. </div>
  34. </template>
  35. <template #amountCNY="{ item }">
  36. <div>
  37. <span>{{ moneyFormat(item.amountCNY, 4) }}</span>
  38. </div>
  39. </template>
  40. <template #sumClaimMoney="{ item }">
  41. <div>
  42. <el-popover :width="520" trigger="hover">
  43. <template #reference>
  44. <a
  45. style="color: #409eff; cursor: pointer; word-break: break-all"
  46. >{{ moneyFormat(item.sumClaimMoney, 4) }}</a
  47. >
  48. </template>
  49. <template #default>
  50. <div
  51. style="width: 100%; padding: 10px"
  52. v-if="
  53. item.claimContractList && item.claimContractList.length > 0
  54. "
  55. >
  56. <div
  57. v-for="(record, index) in item.claimContractList"
  58. :key="index"
  59. style="background: #f1f1f1; border-radius: 2px"
  60. :style="{
  61. 'margin-bottom':
  62. index !== item.claimContractList.length - 1
  63. ? '20px'
  64. : '0px',
  65. }"
  66. :class="{ 'act-class': record.id === currentId }"
  67. @mouseenter="handleMoveEnter(record)"
  68. >
  69. <div
  70. style="
  71. display: flex;
  72. justify-content: space-around;
  73. padding: 5px 0;
  74. "
  75. >
  76. <div style="color: #333333">
  77. <div style="color: #999999; margin-bottom: 4px">
  78. 认领金额:
  79. </div>
  80. {{ record.currency }} {{ record.money }}
  81. </div>
  82. <div style="color: #333333">
  83. <div style="color: #999999; margin-bottom: 4px">
  84. 汇率:
  85. </div>
  86. {{ record.rate }}
  87. </div>
  88. <div style="color: #333333">
  89. <div style="color: #999999; margin-bottom: 4px">
  90. 时间:
  91. </div>
  92. {{ record.createTime }}
  93. </div>
  94. <div style="color: #333333">
  95. <div style="color: #999999; margin-bottom: 4px">
  96. 认领人:
  97. </div>
  98. {{ dictValueLabel(record.createUser, userList) }}
  99. </div>
  100. </div>
  101. </div>
  102. </div>
  103. </template>
  104. </el-popover>
  105. </div>
  106. </template>
  107. <template #scale="{ item }">
  108. <div>
  109. {{ computeScale(item) }}
  110. </div>
  111. </template>
  112. <template #refundStatusNew="{ item }">
  113. <div>
  114. <span v-if="item.refundStatus && item.refundStatus !== 0">
  115. {{ dictValueLabel(item.refundStatus, refundStatusNew) }}
  116. </span>
  117. <span v-else>{{
  118. dictValueLabel(item.refundStatusNew, refundStatusNew)
  119. }}</span>
  120. </div>
  121. </template>
  122. <template #status="{ item }">
  123. <div>
  124. <span :style="getStyle(item.status)">{{
  125. dictValueLabel(item.status, status)
  126. }}</span>
  127. </div>
  128. </template>
  129. <template #buyCorporationId="{ item }">
  130. <div
  131. style="cursor: pointer; color: #409eff; word-break: break-all"
  132. @click="handleClickName(item)"
  133. >
  134. {{ item.buyCorporationName }}
  135. </div>
  136. </template>
  137. </byTable>
  138. </div>
  139. <el-dialog title="打印" v-if="openPrint" v-model="openPrint" width="640">
  140. <ContractPDF :rowData="rowData"></ContractPDF>
  141. <template #footer>
  142. <el-button @click="openPrint = false" size="large">取消</el-button>
  143. <el-button v-print="printObj" size="large">打印</el-button>
  144. <el-button type="primary" @click="clickDownload()" size="large"
  145. >下载PDF</el-button
  146. >
  147. </template>
  148. </el-dialog>
  149. <el-dialog
  150. title="合同详情"
  151. v-if="openDetailsDialog"
  152. v-model="openDetailsDialog"
  153. width="1100"
  154. >
  155. <ContractDetails :contractId="currentContractId"></ContractDetails>
  156. </el-dialog>
  157. </div>
  158. </template>
  159. <script setup>
  160. import { computed, ref } from "vue";
  161. import byTable from "@/components/byTable/index";
  162. import useUserStore from "@/store/modules/user";
  163. import { ElMessage, ElMessageBox } from "element-plus";
  164. import ContractDetails from "@/components/contractCom/contractDetails.vue";
  165. import ContractPDF from "@/components/PDF/contractPDF.vue";
  166. const route = useRoute();
  167. const { proxy } = getCurrentInstance();
  168. const contractType = ref([]);
  169. const accountCurrency = ref([]);
  170. const tradeMethods = ref([]);
  171. const corporationList = ref([]);
  172. const customerList = ref([]);
  173. const shippingMethod = ref([]);
  174. const productUnit = ref([]);
  175. const userList = ref([]);
  176. const openDetailsDialog = ref(false);
  177. const status = ref([
  178. {
  179. label: "草稿",
  180. value: 0,
  181. },
  182. {
  183. label: "审批中",
  184. value: 10,
  185. },
  186. {
  187. label: "驳回",
  188. value: 20,
  189. },
  190. {
  191. label: "审批通过",
  192. value: 30,
  193. },
  194. {
  195. label: "变更中",
  196. value: 60,
  197. },
  198. {
  199. label: "已变更",
  200. value: 70,
  201. },
  202. {
  203. label: "作废",
  204. value: 88,
  205. },
  206. {
  207. label: "终止",
  208. value: 99,
  209. },
  210. ]);
  211. const refundStatusNew = ref([
  212. {
  213. label: "未到款",
  214. value: 0,
  215. },
  216. {
  217. label: "部分到款",
  218. value: 10,
  219. },
  220. {
  221. label: "已到款",
  222. value: 20,
  223. },
  224. ]);
  225. const sourceList = ref({
  226. data: [],
  227. pagination: {
  228. total: 0,
  229. pageNum: 1,
  230. pageSize: 10,
  231. keyword: "",
  232. status: "",
  233. sellCorporationId: "",
  234. },
  235. });
  236. const loading = ref(false);
  237. const selectConfig = computed(() => {
  238. return [
  239. {
  240. label: "审批状态",
  241. prop: "status",
  242. data: status.value,
  243. },
  244. {
  245. label: "到款状态",
  246. prop: "refundStatusNew",
  247. data: refundStatusNew.value,
  248. },
  249. {
  250. label: "合同类型",
  251. prop: "contractType",
  252. data: contractType.value,
  253. },
  254. ];
  255. });
  256. const config = computed(() => {
  257. return [
  258. {
  259. attrs: {
  260. label: "归属公司",
  261. prop: "sellCorporationId",
  262. "min-width": 160,
  263. },
  264. render(type) {
  265. return proxy.dictValueLabel(type, corporationList.value);
  266. },
  267. },
  268. {
  269. attrs: {
  270. label: "合同类型",
  271. prop: "contractType",
  272. width: 100,
  273. },
  274. render(type) {
  275. return proxy.dictValueLabel(type, contractType.value);
  276. },
  277. },
  278. {
  279. attrs: {
  280. label: "合同编码",
  281. slot: "code",
  282. width: 160,
  283. },
  284. },
  285. {
  286. attrs: {
  287. label: "客户",
  288. slot: "buyCorporationId",
  289. "min-width": 180,
  290. },
  291. },
  292. {
  293. attrs: {
  294. label: "合同金额",
  295. slot: "amount",
  296. width: 140,
  297. align: "right",
  298. },
  299. },
  300. {
  301. attrs: {
  302. label: "合同汇率",
  303. prop: "rate",
  304. width: 100,
  305. },
  306. },
  307. {
  308. attrs: {
  309. label: "合同金额(CNY)",
  310. slot: "amountCNY",
  311. width: 120,
  312. align: "right",
  313. },
  314. },
  315. {
  316. attrs: {
  317. label: "已到账金额(CNY)",
  318. slot: "sumClaimMoney",
  319. width: 140,
  320. align: "right",
  321. },
  322. },
  323. {
  324. attrs: {
  325. label: "到账比例",
  326. slot: "scale",
  327. width: 100,
  328. },
  329. },
  330. {
  331. attrs: {
  332. label: "业务员",
  333. prop: "createUser",
  334. width: 140,
  335. },
  336. render(type) {
  337. return proxy.dictValueLabel(type, userList.value);
  338. },
  339. },
  340. {
  341. attrs: {
  342. label: "创建时间",
  343. prop: "createTime",
  344. width: 160,
  345. },
  346. },
  347. {
  348. attrs: {
  349. label: "审批状态",
  350. prop: "status",
  351. width: 120,
  352. slot: "status",
  353. },
  354. // render(type) {
  355. // return proxy.dictValueLabel(type, status.value);
  356. // },
  357. },
  358. {
  359. attrs: {
  360. label: "到款状态",
  361. slot: "refundStatusNew",
  362. width: 120,
  363. },
  364. },
  365. {
  366. attrs: {
  367. label: "操作",
  368. width: "180",
  369. align: "center",
  370. fixed: "right",
  371. },
  372. renderHTML(row) {
  373. return [
  374. row.status == 0
  375. ? {
  376. attrs: {
  377. label: "修改",
  378. type: "primary",
  379. text: true,
  380. },
  381. el: "button",
  382. click() {
  383. clickUpdate(row);
  384. },
  385. }
  386. : {},
  387. !row.refundStatus && row.refundStatusNew == 10
  388. ? {
  389. attrs: {
  390. label: "到款完成",
  391. type: "primary",
  392. text: true,
  393. },
  394. el: "button",
  395. click() {
  396. clickAccomplish(row);
  397. },
  398. }
  399. : {},
  400. row.issue && row.issue === "0" && row.status == 30
  401. ? {
  402. attrs: {
  403. label: "下发交接单",
  404. type: "primary",
  405. text: true,
  406. },
  407. el: "button",
  408. click() {
  409. ElMessageBox.confirm("是否确认下发交接单?", "提示", {
  410. confirmButtonText: "确定",
  411. cancelButtonText: "取消",
  412. type: "warning",
  413. }).then(() => {
  414. proxy
  415. .post("/contract/edit", { id: row.id, issue: 1 })
  416. .then(() => {
  417. ElMessage({ message: "下发成功", type: "success" });
  418. getList();
  419. });
  420. });
  421. },
  422. }
  423. : {},
  424. row.status == 30
  425. ? {
  426. attrs: {
  427. label: "变更",
  428. type: "primary",
  429. text: true,
  430. },
  431. el: "button",
  432. click() {
  433. clickAlteration(row);
  434. },
  435. }
  436. : {},
  437. {
  438. attrs: {
  439. label: "打印",
  440. type: "primary",
  441. text: true,
  442. },
  443. el: "button",
  444. click() {
  445. clickPrint(row);
  446. },
  447. },
  448. {
  449. attrs: {
  450. label: "作废",
  451. type: "primary",
  452. text: true,
  453. },
  454. el: "button",
  455. click() {
  456. ElMessageBox.confirm("此操作将作废该数据, 是否继续?", "提示", {
  457. confirmButtonText: "确定",
  458. cancelButtonText: "取消",
  459. type: "warning",
  460. }).then(() => {
  461. proxy
  462. .post("/contract/edit", {
  463. id: row.id,
  464. status: 88,
  465. })
  466. .then(() => {
  467. ElMessage({
  468. message: "作废成功",
  469. type: "success",
  470. });
  471. getList();
  472. });
  473. });
  474. },
  475. },
  476. ];
  477. },
  478. },
  479. ];
  480. });
  481. const getDict = () => {
  482. proxy
  483. .getDictOne([
  484. "contract_type",
  485. "account_currency",
  486. "trade_mode",
  487. "shipping_method",
  488. "unit",
  489. ])
  490. .then((res) => {
  491. if (res.contract_type && res.contract_type.length > 0) {
  492. contractType.value = res.contract_type.map((x) => ({
  493. label: x.dictValue,
  494. value: x.dictKey,
  495. }));
  496. }
  497. if (res.account_currency && res.account_currency.length > 0) {
  498. accountCurrency.value = res.account_currency.map((x) => ({
  499. label: x.dictValue,
  500. value: x.dictKey,
  501. }));
  502. }
  503. if (res.trade_mode && res.trade_mode.length > 0) {
  504. tradeMethods.value = res.trade_mode.map((x) => ({
  505. label: x.dictValue,
  506. value: x.dictKey,
  507. }));
  508. }
  509. if (res.shipping_method && res.shipping_method.length > 0) {
  510. shippingMethod.value = res.shipping_method.map((x) => ({
  511. label: x.dictValue,
  512. value: x.dictKey,
  513. }));
  514. }
  515. if (res.unit && res.unit.length > 0) {
  516. productUnit.value = res.unit.map((x) => ({
  517. label: x.dictValue,
  518. value: x.dictKey,
  519. }));
  520. }
  521. });
  522. proxy.post("/corporation/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  523. corporationList.value = res.rows.map((item) => {
  524. return {
  525. ...item,
  526. label: item.name,
  527. value: item.id,
  528. };
  529. });
  530. });
  531. proxy.post("/customer/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  532. customerList.value = res.rows.map((item) => {
  533. return {
  534. ...item,
  535. label: item.name,
  536. value: item.id,
  537. };
  538. });
  539. });
  540. proxy
  541. .post("/dictTenantData/page", {
  542. pageNum: 1,
  543. pageSize: 999,
  544. dictCode: "shipping_method",
  545. tenantId: useUserStore().user.tenantId,
  546. })
  547. .then((res) => {
  548. if (res.rows && res.rows.length > 0) {
  549. shippingMethod.value = res.rows.map((item) => {
  550. return {
  551. label: item.dictValue,
  552. value: item.dictKey,
  553. };
  554. });
  555. }
  556. });
  557. proxy
  558. .get("/tenantUser/list", {
  559. pageNum: 1,
  560. pageSize: 10000,
  561. tenantId: useUserStore().user.tenantId,
  562. })
  563. .then((res) => {
  564. userList.value = res.rows.map((item) => {
  565. return {
  566. label: item.nickName,
  567. value: item.userId,
  568. };
  569. });
  570. });
  571. };
  572. const getList = async (req) => {
  573. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  574. loading.value = true;
  575. getTotal()
  576. proxy.post("/contract/page", sourceList.value.pagination).then((res) => {
  577. sourceList.value.data = res.rows;
  578. sourceList.value.pagination.total = res.total;
  579. setTimeout(() => {
  580. loading.value = false;
  581. }, 200);
  582. });
  583. };
  584. const statConfig = computed(() => {
  585. return [
  586. {
  587. label: '合计',
  588. data:[],
  589. },
  590. ]
  591. })
  592. getDict();
  593. if (route.query.code) {
  594. sourceList.value.pagination.keyword = route.query.code;
  595. }
  596. onMounted(() => {
  597. getList()
  598. })
  599. const getTotal = () => {
  600. proxy.post("/contract/getHeadCustomerStatistics",sourceList.value.pagination).then((res) => {
  601. statConfig.value[0].data.push({
  602. label: "合计",
  603. data: [
  604. {
  605. label: "数量",
  606. num: res.sumQuantity,
  607. color: "rgb(171 0 255)",
  608. },
  609. {
  610. label: "金额",
  611. num: res.sumArrivalAmount.toFixed(2),
  612. color: "rgb(171 0 255)",
  613. },
  614. {
  615. label: "到账金额",
  616. num: res.sumAmount.toFixed(2),
  617. color: "rgb(171 0 255)",
  618. },
  619. ],
  620. });
  621. for (let i = 0; i < res.list.length; i++) {
  622. statConfig.value[0].data.push({
  623. label: res.list[i].sellCorporationName,
  624. data: [
  625. {
  626. label: "数量",
  627. num: res.list[i].sumQuantity,
  628. color: "blue",
  629. },
  630. {
  631. label: "金额",
  632. num: res.list[i].sumAmount.toFixed(2),
  633. color: "blue",
  634. },
  635. {
  636. label: "到账金额",
  637. num: res.list[i].sumClaimMoney.toFixed(2),
  638. color: "blue",
  639. },
  640. ],
  641. });
  642. }
  643. console.log(res);
  644. });
  645. };
  646. const newContract = () => {
  647. proxy.$router.replace({
  648. path: "/platform_manage/process/processApproval",
  649. query: {
  650. flowKey: "contract_flow",
  651. flowName: "销售合同流程发起",
  652. random: proxy.random(),
  653. },
  654. });
  655. };
  656. const openPrint = ref(false);
  657. const printDetails = ref({});
  658. const rowData = ref({});
  659. const clickPrint = (row) => {
  660. // printDetails.value = {};
  661. // openPrint.value = true;
  662. // proxy.post("/contract/getContractPdfInfo", { id: row.id }).then((res) => {
  663. // printDetails.value = res;
  664. // });
  665. rowData.value = {
  666. id: row.id,
  667. };
  668. openPrint.value = true;
  669. };
  670. const clickDownload = () => {
  671. proxy.getPdf("外销合同PDF文件");
  672. };
  673. const statistics = (label, index) => {
  674. let num = 0;
  675. if (
  676. printDetails.value.productInfoList &&
  677. printDetails.value.productInfoList.length > 0
  678. ) {
  679. printDetails.value.productInfoList.map((item) => {
  680. if (item[label]) {
  681. num = parseFloat(Number(num) + Number(item[label])).toFixed(index);
  682. }
  683. });
  684. }
  685. return num;
  686. };
  687. const statisticsTwo = (label, index) => {
  688. let num = 0;
  689. if (
  690. printDetails.value.contractProjectList &&
  691. printDetails.value.contractProjectList.length > 0
  692. ) {
  693. printDetails.value.contractProjectList.map((item) => {
  694. if (item[label]) {
  695. num = parseFloat(Number(num) + Number(item[label])).toFixed(index);
  696. }
  697. });
  698. }
  699. return num;
  700. };
  701. const computeScale = (item) => {
  702. let text = 0;
  703. if (
  704. item.sumClaimMoney &&
  705. Number(item.sumClaimMoney) > 0 &&
  706. item.amountCNY &&
  707. Number(item.amountCNY) > 0
  708. ) {
  709. text = parseFloat(
  710. (Number(item.sumClaimMoney) / Number(item.amountCNY)) * 100
  711. ).toFixed(2);
  712. }
  713. return text + "%";
  714. };
  715. const clickAccomplish = (row) => {
  716. proxy.post("/contract/toTheAccount", { id: row.id }).then(() => {
  717. getList();
  718. });
  719. };
  720. const pushProcessApproval = (row) => {
  721. proxy.$router.push({
  722. path: "/platform_manage/process/processApproval",
  723. query: {
  724. flowKey: "contract_flow",
  725. id: row.flowId,
  726. processType: 20,
  727. random: proxy.random(),
  728. flowName: "销售合同详情",
  729. },
  730. });
  731. return;
  732. };
  733. const currentContractId = ref("");
  734. const openDetails = (row) => {
  735. currentContractId.value = row.id;
  736. openDetailsDialog.value = true;
  737. };
  738. const printObj = ref({
  739. id: "printMe",
  740. popTitle: "",
  741. extraCss:
  742. "https://cdn.bootcdn.net/ajax/libs/animate.css/4.1.1/animate.compat.css, https://cdn.bootcdn.net/ajax/libs/hover.css/2.3.1/css/hover-min.css",
  743. extraHead: '<meta http-equiv="Content-Language"content="zh-cn"/>',
  744. });
  745. const clickAlteration = (row) => {
  746. proxy.$router.push({
  747. path: "/platform_manage/process/processApproval",
  748. query: {
  749. flowKey: "contract_update_flow",
  750. flowName: "销售合同变更流程",
  751. contractId: row.id,
  752. random: proxy.random(),
  753. },
  754. });
  755. };
  756. const clickUpdate = (row) => {
  757. proxy.$router.replace({
  758. path: "/platform_manage/process/processApproval",
  759. query: {
  760. flowKey: "contract_flow",
  761. flowName: "销售合同流程发起",
  762. contractId: row.id,
  763. random: proxy.random(),
  764. },
  765. });
  766. };
  767. const getStyle = (status) => {
  768. if (status == 10) {
  769. return {
  770. color: "#FF9315",
  771. };
  772. } else if (status == 30) {
  773. return {
  774. color: "#39C55A",
  775. };
  776. } else if (status == 20 || status == 99) {
  777. return {
  778. color: "#FF655B",
  779. };
  780. } else {
  781. return {};
  782. }
  783. };
  784. const handleClickName = (row) => {
  785. proxy.$router.push({
  786. path: "/ERP/customer/portrait",
  787. query: {
  788. id: row.buyCorporationId,
  789. },
  790. });
  791. };
  792. const currentId = ref("");
  793. const handleMoveEnter = (item) => {
  794. currentId.value = item.id;
  795. };
  796. </script>
  797. <style lang="scss" scoped>
  798. .tenant {
  799. padding: 20px;
  800. }
  801. ::v-deep(.el-input-number .el-input__inner) {
  802. text-align: left;
  803. }
  804. .baseRow {
  805. min-height: 24px;
  806. border-top: 1px solid black;
  807. border-left: 1px solid black;
  808. }
  809. .contentRow {
  810. border-right: 1px solid black;
  811. line-height: 24px;
  812. padding-left: 4px;
  813. }
  814. .act-class {
  815. background: #eff6ff !important;
  816. }
  817. </style>