index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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. :action-list="[
  12. {
  13. text: '创建合同',
  14. action: () => newContract(),
  15. },
  16. ]"
  17. @get-list="getList">
  18. <template #code="{ item }">
  19. <div style="width: 100%">
  20. <a style="color: #409eff; cursor: pointer; word-break: break-all" @click="openDetails(item)">{{ item.code }}</a>
  21. </div>
  22. </template>
  23. <template #amount="{ item }">
  24. <div>
  25. <span style="padding-right: 4px">{{ item.currency }}</span>
  26. <span>{{ moneyFormat(item.amount, 2) }}</span>
  27. </div>
  28. </template>
  29. <template #amountCNY="{ item }">
  30. <div>
  31. <span>{{ moneyFormat(item.amountCNY, 2) }}</span>
  32. </div>
  33. </template>
  34. <template #sumClaimMoney="{ item }">
  35. <div>
  36. <el-popover placement="bottom" :width="400" trigger="hover" @show="recordShow(item)">
  37. <template #reference>
  38. <a style="color: #409eff; cursor: pointer; word-break: break-all">{{ moneyFormat(item.sumClaimMoney, 2) }}</a>
  39. </template>
  40. <template #default>
  41. <div style="width: 100%; max-height: 60vh; overflow-y: auto; overflow-x: hidden" v-if="item.claimRecord && item.claimRecord.length > 0">
  42. <div v-for="(record, index) in item.claimRecord" :key="index" style="margin-bottom: 20px">
  43. <div style="display: flex; justify-content: space-between">
  44. <span style="color: #909399">{{ record.createTime }}</span>
  45. <span style="color: #909399">{{ dictValueLabel(record.createUser, userList) }}</span>
  46. </div>
  47. <div style="display: flex; justify-content: space-between; padding: 8px 0">
  48. <span>认领金额: {{ record.currency }} {{ record.money }}</span>
  49. <span>汇率: {{ item.rate }}</span>
  50. </div>
  51. </div>
  52. </div>
  53. </template>
  54. </el-popover>
  55. </div>
  56. </template>
  57. <template #scale="{ item }">
  58. <div>
  59. {{ computeScale(item) }}
  60. </div>
  61. </template>
  62. <template #refundStatusNew="{ item }">
  63. <div>
  64. <span v-if="item.refundStatus && item.refundStatus !== 0">
  65. {{ dictValueLabel(item.refundStatus, refundStatusNew) }}
  66. </span>
  67. <span v-else>{{ dictValueLabel(item.refundStatusNew, refundStatusNew) }}</span>
  68. </div>
  69. </template>
  70. <template #status="{ item }">
  71. <div>
  72. <span :style="getStyle(item.status)">{{ dictValueLabel(item.status, status) }}</span>
  73. </div>
  74. </template>
  75. <template #buyCorporationId="{ item }">
  76. <div style="cursor: pointer; color: #409eff; word-break: break-all" @click="handleClickName(item)">
  77. {{ item.buyCorporationName }}
  78. </div>
  79. </template>
  80. </byTable>
  81. </div>
  82. <el-dialog title="打印" v-if="openPrint" v-model="openPrint" width="860">
  83. <ContractPDF :rowData="rowData"></ContractPDF>
  84. <template #footer>
  85. <el-button @click="openPrint = false" size="large">取消</el-button>
  86. <el-button v-print="printObj" size="large">打印</el-button>
  87. <el-button type="primary" @click="clickDownload()" size="large">下载PDF</el-button>
  88. </template>
  89. </el-dialog>
  90. <el-dialog title="合同详情" v-if="openDetailsDialog" v-model="openDetailsDialog" width="1000">
  91. <ContractDetails :contractId="currentContractId"></ContractDetails>
  92. </el-dialog>
  93. </div>
  94. </template>
  95. <script setup>
  96. import { computed, ref } from "vue";
  97. import byTable from "@/components/byTable/index";
  98. import useUserStore from "@/store/modules/user";
  99. import { ElMessage, ElMessageBox } from "element-plus";
  100. import ContractDetails from "@/components/contractCom/contractDetails.vue";
  101. import ContractPDF from "@/components/PDF/contractPDF.vue";
  102. const route = useRoute();
  103. const { proxy } = getCurrentInstance();
  104. const contractType = ref([]);
  105. const accountCurrency = ref([]);
  106. const tradeMethods = ref([]);
  107. const corporationList = ref([]);
  108. const customerList = ref([]);
  109. const shippingMethod = ref([]);
  110. const productUnit = ref([]);
  111. const userList = ref([]);
  112. const openDetailsDialog = ref(false);
  113. const status = ref([
  114. {
  115. label: "草稿",
  116. value: 0,
  117. },
  118. {
  119. label: "审批中",
  120. value: 10,
  121. },
  122. {
  123. label: "驳回",
  124. value: 20,
  125. },
  126. {
  127. label: "审批通过",
  128. value: 30,
  129. },
  130. {
  131. label: "终止",
  132. value: 99,
  133. },
  134. ]);
  135. const refundStatusNew = ref([
  136. {
  137. label: "未到款",
  138. value: 0,
  139. },
  140. {
  141. label: "部分到款",
  142. value: 10,
  143. },
  144. {
  145. label: "已到款",
  146. value: 20,
  147. },
  148. ]);
  149. const sourceList = ref({
  150. data: [],
  151. pagination: {
  152. total: 0,
  153. pageNum: 1,
  154. pageSize: 10,
  155. keyword: "",
  156. status: "",
  157. sellCorporationId: "",
  158. },
  159. });
  160. const loading = ref(false);
  161. const selectConfig = computed(() => {
  162. return [
  163. {
  164. label: "审批状态",
  165. prop: "status",
  166. data: status.value,
  167. },
  168. {
  169. label: "到款状态",
  170. prop: "refundStatusNew",
  171. data: refundStatusNew.value,
  172. },
  173. {
  174. label: "合同类型",
  175. prop: "contractType",
  176. data: contractType.value,
  177. },
  178. ];
  179. });
  180. const config = computed(() => {
  181. return [
  182. {
  183. attrs: {
  184. label: "归属公司",
  185. prop: "sellCorporationId",
  186. "min-width": 160,
  187. },
  188. render(type) {
  189. return proxy.dictValueLabel(type, corporationList.value);
  190. },
  191. },
  192. {
  193. attrs: {
  194. label: "合同类型",
  195. prop: "contractType",
  196. width: 100,
  197. },
  198. render(type) {
  199. return proxy.dictValueLabel(type, contractType.value);
  200. },
  201. },
  202. {
  203. attrs: {
  204. label: "合同编码",
  205. slot: "code",
  206. width: 160,
  207. },
  208. },
  209. {
  210. attrs: {
  211. label: "客户",
  212. slot: "buyCorporationId",
  213. "min-width": 180,
  214. },
  215. },
  216. {
  217. attrs: {
  218. label: "合同金额",
  219. slot: "amount",
  220. width: 140,
  221. align: "right",
  222. },
  223. },
  224. {
  225. attrs: {
  226. label: "合同汇率",
  227. prop: "rate",
  228. width: 100,
  229. },
  230. },
  231. {
  232. attrs: {
  233. label: "合同金额(CNY)",
  234. slot: "amountCNY",
  235. width: 120,
  236. align: "right",
  237. },
  238. },
  239. {
  240. attrs: {
  241. label: "已到账金额(CNY)",
  242. slot: "sumClaimMoney",
  243. width: 140,
  244. align: "right",
  245. },
  246. },
  247. {
  248. attrs: {
  249. label: "到账比例",
  250. slot: "scale",
  251. width: 100,
  252. },
  253. },
  254. {
  255. attrs: {
  256. label: "业务员",
  257. prop: "createUser",
  258. width: 140,
  259. },
  260. render(type) {
  261. return proxy.dictValueLabel(type, userList.value);
  262. },
  263. },
  264. {
  265. attrs: {
  266. label: "创建时间",
  267. prop: "createTime",
  268. width: 160,
  269. },
  270. },
  271. {
  272. attrs: {
  273. label: "审批状态",
  274. prop: "status",
  275. width: 120,
  276. slot: "status",
  277. },
  278. // render(type) {
  279. // return proxy.dictValueLabel(type, status.value);
  280. // },
  281. },
  282. {
  283. attrs: {
  284. label: "到款状态",
  285. slot: "refundStatusNew",
  286. width: 120,
  287. },
  288. },
  289. {
  290. attrs: {
  291. label: "操作",
  292. width: "180",
  293. align: "center",
  294. fixed: "right",
  295. },
  296. renderHTML(row) {
  297. return [
  298. !row.refundStatus && row.refundStatusNew == 10
  299. ? {
  300. attrs: {
  301. label: "到款完成",
  302. type: "primary",
  303. text: true,
  304. },
  305. el: "button",
  306. click() {
  307. clickAccomplish(row);
  308. },
  309. }
  310. : {},
  311. row.issue && row.issue === "0" && row.status == 30
  312. ? {
  313. attrs: {
  314. label: "下发交接单",
  315. type: "primary",
  316. text: true,
  317. },
  318. el: "button",
  319. click() {
  320. ElMessageBox.confirm("是否确认下发交接单?", "提示", {
  321. confirmButtonText: "确定",
  322. cancelButtonText: "取消",
  323. type: "warning",
  324. }).then(() => {
  325. proxy.post("/contract/edit", { id: row.id, issue: 1 }).then(() => {
  326. ElMessage({ message: "下发成功", type: "success" });
  327. getList();
  328. });
  329. });
  330. },
  331. }
  332. : {},
  333. row.status == 30
  334. ? {
  335. attrs: {
  336. label: "变更",
  337. type: "primary",
  338. text: true,
  339. },
  340. el: "button",
  341. click() {
  342. clickAlteration(row);
  343. },
  344. }
  345. : {},
  346. {
  347. attrs: {
  348. label: "打印",
  349. type: "primary",
  350. text: true,
  351. },
  352. el: "button",
  353. click() {
  354. clickPrint(row);
  355. },
  356. },
  357. {
  358. attrs: {
  359. label: "作废",
  360. type: "primary",
  361. text: true,
  362. },
  363. el: "button",
  364. click() {
  365. ElMessageBox.confirm("此操作将作废该数据, 是否继续?", "提示", {
  366. confirmButtonText: "确定",
  367. cancelButtonText: "取消",
  368. type: "warning",
  369. }).then(() => {
  370. proxy
  371. .post("/contract/edit", {
  372. id: row.id,
  373. status: 88,
  374. })
  375. .then(() => {
  376. ElMessage({
  377. message: "作废成功",
  378. type: "success",
  379. });
  380. getList();
  381. });
  382. });
  383. },
  384. },
  385. ];
  386. },
  387. },
  388. ];
  389. });
  390. const getDict = () => {
  391. proxy.getDictOne(["contract_type", "account_currency", "trade_mode", "shipping_method", "unit"]).then((res) => {
  392. if (res.contract_type && res.contract_type.length > 0) {
  393. contractType.value = res.contract_type.map((x) => ({
  394. label: x.dictValue,
  395. value: x.dictKey,
  396. }));
  397. }
  398. if (res.account_currency && res.account_currency.length > 0) {
  399. accountCurrency.value = res.account_currency.map((x) => ({
  400. label: x.dictValue,
  401. value: x.dictKey,
  402. }));
  403. }
  404. if (res.trade_mode && res.trade_mode.length > 0) {
  405. tradeMethods.value = res.trade_mode.map((x) => ({
  406. label: x.dictValue,
  407. value: x.dictKey,
  408. }));
  409. }
  410. if (res.shipping_method && res.shipping_method.length > 0) {
  411. shippingMethod.value = res.shipping_method.map((x) => ({
  412. label: x.dictValue,
  413. value: x.dictKey,
  414. }));
  415. }
  416. if (res.unit && res.unit.length > 0) {
  417. productUnit.value = res.unit.map((x) => ({
  418. label: x.dictValue,
  419. value: x.dictKey,
  420. }));
  421. }
  422. });
  423. proxy.post("/corporation/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  424. corporationList.value = res.rows.map((item) => {
  425. return {
  426. ...item,
  427. label: item.name,
  428. value: item.id,
  429. };
  430. });
  431. });
  432. proxy.post("/customer/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  433. customerList.value = res.rows.map((item) => {
  434. return {
  435. ...item,
  436. label: item.name,
  437. value: item.id,
  438. };
  439. });
  440. });
  441. proxy
  442. .post("/dictTenantData/page", {
  443. pageNum: 1,
  444. pageSize: 999,
  445. dictCode: "shipping_method",
  446. tenantId: useUserStore().user.tenantId,
  447. })
  448. .then((res) => {
  449. if (res.rows && res.rows.length > 0) {
  450. shippingMethod.value = res.rows.map((item) => {
  451. return {
  452. label: item.dictValue,
  453. value: item.dictKey,
  454. };
  455. });
  456. }
  457. });
  458. proxy
  459. .get("/tenantUser/list", {
  460. pageNum: 1,
  461. pageSize: 10000,
  462. tenantId: useUserStore().user.tenantId,
  463. })
  464. .then((res) => {
  465. userList.value = res.rows.map((item) => {
  466. return {
  467. label: item.nickName,
  468. value: item.userId,
  469. };
  470. });
  471. });
  472. };
  473. const getList = async (req) => {
  474. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  475. loading.value = true;
  476. proxy.post("/contract/page", sourceList.value.pagination).then((res) => {
  477. sourceList.value.data = res.rows;
  478. sourceList.value.pagination.total = res.total;
  479. setTimeout(() => {
  480. loading.value = false;
  481. }, 200);
  482. });
  483. };
  484. getDict();
  485. if (route.query.code) {
  486. sourceList.value.pagination.keyword = route.query.code;
  487. }
  488. getList();
  489. const newContract = () => {
  490. proxy.$router.replace({
  491. path: "/platform_manage/process/processApproval",
  492. query: {
  493. flowKey: "contract_flow",
  494. flowName: "销售合同审批流程",
  495. random: proxy.random(),
  496. },
  497. });
  498. };
  499. const openPrint = ref(false);
  500. const printDetails = ref({});
  501. const rowData = ref({});
  502. const clickPrint = (row) => {
  503. // printDetails.value = {};
  504. // openPrint.value = true;
  505. // proxy.post("/contract/getContractPdfInfo", { id: row.id }).then((res) => {
  506. // printDetails.value = res;
  507. // });
  508. rowData.value = {
  509. id: row.id,
  510. };
  511. openPrint.value = true;
  512. };
  513. const clickDownload = () => {
  514. proxy.getPdf("外销合同PDF文件");
  515. };
  516. const statistics = (label, index) => {
  517. let num = 0;
  518. if (printDetails.value.productInfoList && printDetails.value.productInfoList.length > 0) {
  519. printDetails.value.productInfoList.map((item) => {
  520. if (item[label]) {
  521. num = parseFloat(Number(num) + Number(item[label])).toFixed(index);
  522. }
  523. });
  524. }
  525. return num;
  526. };
  527. const statisticsTwo = (label, index) => {
  528. let num = 0;
  529. if (printDetails.value.contractProjectList && printDetails.value.contractProjectList.length > 0) {
  530. printDetails.value.contractProjectList.map((item) => {
  531. if (item[label]) {
  532. num = parseFloat(Number(num) + Number(item[label])).toFixed(index);
  533. }
  534. });
  535. }
  536. return num;
  537. };
  538. const computeScale = (item) => {
  539. let text = 0;
  540. if (item.sumClaimMoney && Number(item.sumClaimMoney) > 0 && item.amountCNY && Number(item.amountCNY) > 0) {
  541. text = parseFloat((Number(item.sumClaimMoney) / Number(item.amountCNY)) * 100).toFixed(2);
  542. }
  543. return text + "%";
  544. };
  545. const clickAccomplish = (row) => {
  546. proxy.post("/contract/toTheAccount", { id: row.id }).then(() => {
  547. getList();
  548. });
  549. };
  550. const pushProcessApproval = (row) => {
  551. proxy.$router.push({
  552. path: "/platform_manage/process/processApproval",
  553. query: {
  554. flowKey: "contract_flow",
  555. id: row.flowId,
  556. processType: 20,
  557. random: proxy.random(),
  558. flowName: "销售合同详情",
  559. },
  560. });
  561. return;
  562. };
  563. const currentContractId = ref("");
  564. const openDetails = (row) => {
  565. currentContractId.value = row.id;
  566. openDetailsDialog.value = true;
  567. };
  568. const printObj = ref({
  569. id: "printMe",
  570. popTitle: "",
  571. extraCss: "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",
  572. extraHead: '<meta http-equiv="Content-Language"content="zh-cn"/>',
  573. });
  574. const clickAlteration = (row) => {
  575. proxy.$router.push({
  576. path: "/platform_manage/process/processApproval",
  577. query: {
  578. flowKey: "contract_update_flow",
  579. flowName: "销售合同变更流程",
  580. contractId: row.id,
  581. random: proxy.random(),
  582. },
  583. });
  584. };
  585. const getStyle = (status) => {
  586. if (status == 10) {
  587. return {
  588. color: "#FF9315",
  589. };
  590. } else if (status == 30) {
  591. return {
  592. color: "#39C55A",
  593. };
  594. } else if (status == 20 || status == 99) {
  595. return {
  596. color: "#FF655B",
  597. };
  598. } else {
  599. return {};
  600. }
  601. };
  602. const handleClickName = (row) => {
  603. proxy.$router.push({
  604. path: "/ERP/customer/portrait",
  605. query: {
  606. id: row.buyCorporationId,
  607. },
  608. });
  609. };
  610. const recordShow = (item) => {
  611. if (!(item.claimRecord && item.claimRecord.length > 0)) {
  612. proxy.post("/claimContract/getListByContractIds", [item.id]).then((res) => {
  613. if (res && res.length > 0) {
  614. item.claimRecord = res;
  615. }
  616. });
  617. }
  618. };
  619. </script>
  620. <style lang="scss" scoped>
  621. .tenant {
  622. padding: 20px;
  623. }
  624. ::v-deep(.el-input-number .el-input__inner) {
  625. text-align: left;
  626. }
  627. .baseRow {
  628. min-height: 24px;
  629. border-top: 1px solid black;
  630. border-left: 1px solid black;
  631. }
  632. .contentRow {
  633. border-right: 1px solid black;
  634. line-height: 24px;
  635. padding-left: 4px;
  636. }
  637. </style>