PurchasePayment.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. <template>
  2. <div style="width: 100%; padding: 0px 15px">
  3. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
  4. <template #deadline>
  5. <div>
  6. <el-date-picker v-model="formData.data.deadline" type="date" placeholder="请选择付款期限" value-format="YYYY-MM-DD" />
  7. </div>
  8. </template>
  9. <template #receiptsNum>
  10. <div>
  11. <el-input-number
  12. onmousewheel="return false;"
  13. v-model="formData.data.receiptsNum"
  14. placeholder="请输入单据数量"
  15. :min="0"
  16. :precision="0"
  17. :controls="false" />
  18. </div>
  19. </template>
  20. <template #fileList>
  21. <div style="width: 100%">
  22. <el-upload
  23. v-model:fileList="fileList"
  24. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  25. multiple
  26. :data="uploadData"
  27. :before-upload="uploadFile"
  28. :on-preview="onPreviewFile">
  29. <el-button>选择</el-button>
  30. </el-upload>
  31. </div>
  32. </template>
  33. <template #payDetailList>
  34. <div style="width: 100%">
  35. <el-button type="primary" @click="clickAdd()">添加行</el-button>
  36. <el-table :data="formData.data.payDetailList" style="width: 100%; margin-top: 16px">
  37. <el-table-column label="采购合同" width="220">
  38. <template #default="{ row, $index }">
  39. <div style="width: 100%">
  40. <el-form-item :prop="'payDetailList.' + $index + '.purchaseId'" :rules="rules.purchaseId" :inline-message="true">
  41. <el-select v-model="row.purchaseId" placeholder="请选择采购合同" style="width: 100%" @change="changePurchaseId(row, true)">
  42. <el-option v-for="item in contractList" :key="item.value" :label="item.label" :value="item.value" />
  43. </el-select>
  44. </el-form-item>
  45. </div>
  46. </template>
  47. </el-table-column>
  48. <el-table-column prop="amount" label="合同金额" width="140" />
  49. <el-table-column prop="sumPayMoney" label="已付款金额" width="140" />
  50. <el-table-column prop="sumInvoiceMoney" label="已收发票金额" width="140" />
  51. <el-table-column label="款项说明">
  52. <template #default="{ row, $index }">
  53. <div style="width: 100%">
  54. <el-form-item :prop="'payDetailList.' + $index + '.remark'" :rules="rules.remark" :inline-message="true">
  55. <el-input v-model="row.remark" placeholder="请输入款项说明" style="width: 100%" />
  56. </el-form-item>
  57. </div>
  58. </template>
  59. </el-table-column>
  60. <el-table-column label="付款金额" width="180">
  61. <template #default="{ row, $index }">
  62. <div style="width: 100%">
  63. <el-form-item :prop="'payDetailList.' + $index + '.money'" :rules="rules.money" :inline-message="true">
  64. <el-input-number
  65. onmousewheel="return false;"
  66. v-model="row.money"
  67. placeholder="请输入金额"
  68. style="width: 100%"
  69. :precision="2"
  70. :controls="false"
  71. :min="0"
  72. :disabled="row.id"
  73. @change="changeMoney()" />
  74. </el-form-item>
  75. </div>
  76. </template>
  77. </el-table-column>
  78. <el-table-column label="操作" width="80">
  79. <template #default="{ row, $index }">
  80. <el-button type="primary" link @click="handleRemove($index)">删除</el-button>
  81. </template>
  82. </el-table-column>
  83. </el-table>
  84. <br />
  85. </div>
  86. </template>
  87. </byForm>
  88. </div>
  89. </template>
  90. <script setup>
  91. import byForm from "@/components/byForm/index";
  92. import { ElMessage } from "element-plus";
  93. import useUserStore from "@/store/modules/user";
  94. import { useRoute } from "vue-router";
  95. const route = useRoute();
  96. const { proxy } = getCurrentInstance();
  97. const supplierList = ref([]);
  98. const invoiceType = ref([]);
  99. const fundsPaymentMethod = ref([]);
  100. const accountList = ref([]);
  101. const contractList = ref([]);
  102. let formData = reactive({
  103. data: {
  104. supplyId: "",
  105. amount: "",
  106. invoiceType: "",
  107. remark: "",
  108. deadline: "",
  109. receiptsNum: undefined,
  110. payType: "",
  111. currency: "",
  112. accountManagementId: "",
  113. userName: "",
  114. payDetailList: [],
  115. fileList: [],
  116. },
  117. });
  118. const submit = ref(null);
  119. const judgeStatus = () => {
  120. if (route.query.processType == 20 || route.query.processType == 10) {
  121. return true;
  122. }
  123. if (props.queryData.recordList && props.queryData.recordList.length > 0) {
  124. let data = props.queryData.recordList.filter((item) => item.status === 2 && item.nodeType !== 1);
  125. if (data && data.length > 0) {
  126. return true;
  127. }
  128. }
  129. return false;
  130. };
  131. const formOption = reactive({
  132. inline: true,
  133. labelWidth: 100,
  134. itemWidth: 100,
  135. rules: [],
  136. disabled: false,
  137. });
  138. const formConfig = computed(() => {
  139. return [
  140. {
  141. type: "title",
  142. title: "基础信息",
  143. label: "",
  144. },
  145. {
  146. type: "select",
  147. label: "供应商",
  148. prop: "supplyId",
  149. data: supplierList.value,
  150. fn: (val) => {
  151. changeSupply(val);
  152. },
  153. },
  154. {
  155. type: "slot",
  156. prop: "deadline",
  157. slotName: "deadline",
  158. label: "付款期限",
  159. itemWidth: 50,
  160. },
  161. {
  162. type: "select",
  163. label: "发票类型",
  164. prop: "invoiceType",
  165. data: invoiceType.value,
  166. itemWidth: 50,
  167. },
  168. {
  169. type: "input",
  170. prop: "remark",
  171. label: "付款说明",
  172. itemType: "textarea",
  173. },
  174. {
  175. type: "slot",
  176. prop: "receiptsNum",
  177. slotName: "receiptsNum",
  178. label: "单据数量",
  179. },
  180. {
  181. type: "slot",
  182. prop: "fileList",
  183. slotName: "fileList",
  184. label: "上传附件",
  185. },
  186. {
  187. type: "slot",
  188. prop: "payDetailList",
  189. slotName: "payDetailList",
  190. label: "付款明细",
  191. },
  192. {
  193. type: "input",
  194. prop: "amount",
  195. label: "付款总额",
  196. required: true,
  197. itemType: "text",
  198. disabled: true,
  199. itemWidth: 50,
  200. },
  201. {
  202. label: "收款信息",
  203. },
  204. {
  205. type: "select",
  206. label: "付款方式",
  207. prop: "payType",
  208. data: fundsPaymentMethod.value,
  209. itemWidth: 50,
  210. },
  211. {
  212. type: "select",
  213. label: "付款账户",
  214. prop: "accountManagementId",
  215. data: accountList.value,
  216. itemWidth: 50,
  217. fn: (val) => {
  218. changeAccount(val);
  219. },
  220. },
  221. {
  222. type: "input",
  223. prop: "name",
  224. label: "户名",
  225. required: true,
  226. itemType: "text",
  227. itemWidth: 50,
  228. },
  229. {
  230. type: "input",
  231. prop: "accountOpening",
  232. label: "银行账号",
  233. required: true,
  234. itemType: "text",
  235. itemWidth: 50,
  236. },
  237. {
  238. type: "input",
  239. prop: "openingBank",
  240. label: "开户银行",
  241. required: true,
  242. itemType: "text",
  243. itemWidth: 50,
  244. },
  245. {
  246. type: "input",
  247. prop: "interbankNumber",
  248. label: "联行号",
  249. required: true,
  250. itemType: "text",
  251. itemWidth: 50,
  252. },
  253. ];
  254. });
  255. const rules = ref({
  256. supplyId: [{ required: true, message: "请选择供应商", trigger: "change" }],
  257. invoiceType: [{ required: true, message: "请选择发票类型", trigger: "change" }],
  258. payType: [{ required: true, message: "请选择付款方式", trigger: "change" }],
  259. // accountManagementId: [{ required: true, message: "请选择付款账户", trigger: "change" }],
  260. purchaseId: [{ required: true, message: "请选择采购合同", trigger: "change" }],
  261. money: [{ required: true, message: "请输入付款金额", trigger: "blur" }],
  262. name: [{ required: true, message: "请输入户名", trigger: "blur" }],
  263. });
  264. const fileList = ref([]);
  265. const uploadData = ref({});
  266. const getDict = () => {
  267. proxy
  268. .post("/dictTenantData/page", {
  269. pageNum: 1,
  270. pageSize: 999,
  271. dictCode: "invoice_type",
  272. tenantId: useUserStore().user.tenantId,
  273. })
  274. .then((res) => {
  275. if (res.rows && res.rows.length > 0) {
  276. invoiceType.value = res.rows.map((item) => {
  277. return {
  278. label: item.dictValue,
  279. value: item.dictKey,
  280. };
  281. });
  282. }
  283. });
  284. proxy
  285. .post("/dictTenantData/page", {
  286. pageNum: 1,
  287. pageSize: 999,
  288. dictCode: "funds_payment_method",
  289. tenantId: useUserStore().user.tenantId,
  290. })
  291. .then((res) => {
  292. if (res.rows && res.rows.length > 0) {
  293. fundsPaymentMethod.value = res.rows.map((item) => {
  294. return {
  295. label: item.dictValue,
  296. value: item.dictKey,
  297. };
  298. });
  299. }
  300. });
  301. proxy.post("/supplierInfo/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  302. if (res.rows && res.rows.length > 0) {
  303. supplierList.value = res.rows.map((item) => {
  304. return {
  305. label: item.name,
  306. value: item.id,
  307. };
  308. });
  309. }
  310. });
  311. proxy.post("/accountManagement/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  312. accountList.value = res.rows.map((item) => {
  313. return {
  314. bankName: item.name,
  315. accountOpening: item.accountOpening,
  316. openingBank: item.openingBank,
  317. interbankNumber: item.interbankNumber,
  318. label: item.alias,
  319. value: item.id,
  320. };
  321. });
  322. });
  323. };
  324. getDict();
  325. const uploadFile = async (file) => {
  326. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  327. uploadData.value = res.uploadBody;
  328. file.id = res.id;
  329. file.fileName = res.fileName;
  330. file.fileUrl = res.fileUrl;
  331. return true;
  332. };
  333. const onPreviewFile = (file) => {
  334. window.open(file.raw.fileUrl, "_blank");
  335. };
  336. const changeSupply = async (val) => {
  337. if (val) {
  338. await proxy.get("/purchase/getListBySupplyId", { supplyId: val }).then((res) => {
  339. if (res.data && res.data.length > 0) {
  340. contractList.value = res.data.map((item) => {
  341. return {
  342. value: item.id,
  343. label: item.code,
  344. amount: item.amount,
  345. sumPayMoney: item.sumPayMoney,
  346. sumInvoiceMoney: item.sumInvoiceMoney,
  347. };
  348. });
  349. } else {
  350. contractList.value = [];
  351. }
  352. });
  353. } else {
  354. contractList.value = [];
  355. }
  356. formData.data.payDetailList = [];
  357. getDecisionAids();
  358. };
  359. const clickAdd = () => {
  360. if (formData.data.payDetailList && formData.data.payDetailList.length > 0) {
  361. formData.data.payDetailList.push({
  362. purchaseId: "",
  363. money: undefined,
  364. remark: "",
  365. });
  366. } else {
  367. formData.data.payDetailList = [{ purchaseId: "", money: undefined, remark: "" }];
  368. }
  369. };
  370. const handleRemove = (index) => {
  371. formData.data.payDetailList.splice(index, 1);
  372. getDecisionAids();
  373. };
  374. const changeAccount = (val) => {
  375. if (val) {
  376. let data = accountList.value.filter((item) => item.value === val);
  377. if (data && data.length > 0) {
  378. formData.data.bankName = data[0].bankName;
  379. formData.data.accountOpening = data[0].accountOpening;
  380. formData.data.openingBank = data[0].openingBank;
  381. formData.data.interbankNumber = data[0].interbankNumber;
  382. }
  383. }
  384. };
  385. const changePurchaseId = (row, status) => {
  386. let data = contractList.value.filter((item) => item.value === row.purchaseId);
  387. if (data && data.length > 0) {
  388. row.amount = data[0].amount;
  389. row.sumPayMoney = data[0].sumPayMoney;
  390. row.sumInvoiceMoney = data[0].sumInvoiceMoney;
  391. } else {
  392. row.amount = "";
  393. row.sumPayMoney = "";
  394. row.sumInvoiceMoney = "";
  395. }
  396. if (status) {
  397. getDecisionAids();
  398. }
  399. };
  400. const changeMoney = () => {
  401. let money = 0;
  402. for (let i = 0; i < formData.data.payDetailList.length; i++) {
  403. if (formData.data.payDetailList[i].money) {
  404. money = parseFloat(Number(money) + Number(formData.data.payDetailList[i].money)).toFixed(2);
  405. }
  406. }
  407. formData.data.amount = money;
  408. };
  409. const handleSubmit = async () => {
  410. let status = await submit.value.handleSubmit(() => {});
  411. if (status) {
  412. if (formData.data.payDetailList && formData.data.payDetailList.length > 0) {
  413. if (fileList.value && fileList.value.length > 0) {
  414. formData.data.fileList = fileList.value.map((item) => {
  415. return {
  416. id: item.raw.id,
  417. fileName: item.raw.fileName,
  418. fileUrl: item.raw.fileUrl,
  419. };
  420. });
  421. }
  422. return true;
  423. } else {
  424. ElMessage("请添加至少一条付款明细");
  425. }
  426. return false;
  427. }
  428. return status;
  429. };
  430. // 接收父组件的传值
  431. const props = defineProps({
  432. queryData: Object,
  433. });
  434. watch(
  435. props.queryData,
  436. () => {
  437. formOption.disabled = judgeStatus();
  438. if (props.queryData && ["10", "20", "30"].includes(route.query.processType)) {
  439. for (var text in props.queryData) {
  440. formData.data[text] = props.queryData[text];
  441. }
  442. if (formData.data.fileList && formData.data.fileList.length > 0) {
  443. fileList.value = formData.data.fileList.map((item) => {
  444. return {
  445. raw: item,
  446. name: item.fileName,
  447. url: item.fileUrl,
  448. };
  449. });
  450. } else {
  451. fileList.value = [];
  452. }
  453. if (route.query.processType !== "30" && formData.data.payDetailList && formData.data.payDetailList.length) {
  454. let ids = formData.data.payDetailList.map((item) => item.purchaseId);
  455. if (ids && ids.length > 0) {
  456. proxy.post("/purchase/getListInId", ids).then((res) => {
  457. if (res && res.length > 0) {
  458. contractList.value = res.map((item) => {
  459. return {
  460. value: item.id,
  461. label: item.code,
  462. };
  463. });
  464. } else {
  465. contractList.value = [];
  466. }
  467. });
  468. }
  469. }
  470. getDecisionAids();
  471. }
  472. },
  473. {
  474. deep: true,
  475. }
  476. );
  477. onMounted(async () => {
  478. if (props.queryData.supplyIdTwo) {
  479. formData.data.supplyIdTwo = props.queryData.supplyIdTwo;
  480. await changeSupply(formData.data.supplyIdTwo);
  481. if (props.queryData.ids) {
  482. let ids = props.queryData.ids.split(",");
  483. if (ids && ids.length > 0) {
  484. for (let i = 0; i < ids.length; i++) {
  485. if (contractList.value && contractList.value.length > 0) {
  486. let data = contractList.value.filter((item) => item.value === ids[i]);
  487. if (data && data.length > 0) {
  488. if (formData.data.payDetailList && formData.data.payDetailList.length > 0) {
  489. formData.data.payDetailList.push({
  490. purchaseId: ids[i],
  491. money: undefined,
  492. remark: "",
  493. });
  494. } else {
  495. formData.data.payDetailList = [{ purchaseId: ids[i], money: undefined, remark: "" }];
  496. }
  497. }
  498. }
  499. }
  500. if (formData.data.payDetailList && formData.data.payDetailList.length > 0) {
  501. for (let i = 0; i < formData.data.payDetailList.length; i++) {
  502. changePurchaseId(formData.data.payDetailList[i]);
  503. }
  504. getDecisionAids();
  505. }
  506. }
  507. }
  508. }
  509. });
  510. const getFormData = () => {
  511. return formData.data;
  512. };
  513. // 向父组件暴露
  514. defineExpose({
  515. getFormData,
  516. handleSubmit,
  517. });
  518. let auxiliaryData = ref([
  519. {
  520. label: "关联销售合同",
  521. data: [],
  522. },
  523. {
  524. label: "到货数据",
  525. data: [],
  526. },
  527. {
  528. label: "质检数据",
  529. data: [],
  530. },
  531. ]);
  532. const emit = defineEmits(["auxiliaryChange"]);
  533. const getDecisionAids = () => {
  534. let data = {
  535. purchaseIdList: [],
  536. };
  537. if (formData.data.payDetailList && formData.data.payDetailList.length > 0) {
  538. data.purchaseIdList = formData.data.payDetailList.map((item) => item.purchaseId);
  539. }
  540. proxy.post("/contract/payDecisionAid", data).then((res) => {
  541. if (res.contractList && res.contractList.length > 0) {
  542. auxiliaryData.value[0].data = res.contractList.map((item) => {
  543. return [
  544. {
  545. label: "合同编号",
  546. value: item.code,
  547. style: {
  548. color: "#0084FF",
  549. },
  550. id: item.id,
  551. num: 1,
  552. },
  553. {
  554. label: "下单日期",
  555. value: item.createTime,
  556. id: item.id,
  557. num: 1,
  558. },
  559. ];
  560. });
  561. } else {
  562. auxiliaryData.value[0].data = [];
  563. }
  564. if (res.purchaseInfoList && res.purchaseInfoList.length > 0) {
  565. auxiliaryData.value[1].data = res.purchaseInfoList.map((item) => {
  566. return [
  567. {
  568. label: "物品编码",
  569. value: item.productCode,
  570. id: item.productId,
  571. num: 1,
  572. },
  573. {
  574. label: "物品名称",
  575. value: item.productName,
  576. id: item.productId,
  577. num: 1,
  578. },
  579. {
  580. label: "采购数量",
  581. value: item.purchaseQuantity,
  582. id: item.productId,
  583. num: 1,
  584. },
  585. {
  586. label: "到货数量",
  587. value: item.invoiceQuantity,
  588. id: item.productId,
  589. num: 1,
  590. },
  591. ];
  592. });
  593. auxiliaryData.value[2].data = res.purchaseInfoList.map((item) => {
  594. let odds = 0;
  595. if (item.invoiceQuantity && item.qualifiedCount) {
  596. odds = parseFloat((Number(item.qualifiedCount) / Number(item.invoiceQuantity)) * 100).toFixed(2);
  597. }
  598. return [
  599. {
  600. label: "物品编码",
  601. value: item.productCode,
  602. id: item.productId,
  603. num: 1,
  604. },
  605. {
  606. label: "物品名称",
  607. value: item.productName,
  608. id: item.productId,
  609. num: 1,
  610. },
  611. {
  612. label: "到货数量",
  613. value: item.invoiceQuantity,
  614. id: item.productId,
  615. num: 1,
  616. },
  617. {
  618. label: "合格率",
  619. value: odds + "%",
  620. id: item.productId,
  621. num: 1,
  622. },
  623. ];
  624. });
  625. } else {
  626. auxiliaryData.value[1].data = [];
  627. auxiliaryData.value[2].data = [];
  628. }
  629. let list = proxy.deepClone(auxiliaryData.value);
  630. if (!(list[0].data && list[0].data.length > 0)) {
  631. list.splice(0, 1);
  632. }
  633. emit("auxiliaryChange", list);
  634. });
  635. };
  636. </script>
  637. <style lang="scss" scoped>
  638. ::v-deep(.el-input-number .el-input__inner) {
  639. text-align: left;
  640. }
  641. </style>