index.vue 17 KB

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