tableInfoNew.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. <template>
  2. <div>
  3. <el-form :ref="'form'" :model="tableForm">
  4. <ele-pro-table
  5. ref="table"
  6. :needPage="false"
  7. :columns="columns"
  8. :datasource="tableForm.detailList"
  9. row-key="id"
  10. class="time-form"
  11. >
  12. <template v-slot:toolbar>
  13. <span>本次开票合计:{{ totalAmountTotalPrice }}</span>
  14. </template>
  15. <template v-slot:invoiceAmount="scope">
  16. <el-form-item
  17. style="width: 100%;"
  18. :prop="'detailList.' + scope.$index + '.invoiceAmount'"
  19. >
  20. <span v-if="dialogType === 'view'"> {{ scope.row.invoiceAmount }}</span>
  21. <el-input v-else v-model="scope.row.invoiceAmount" type="number" @input="(val) => invoiceAmountChange(val, scope.row, scope.$index)"></el-input>
  22. </el-form-item>
  23. </template>
  24. <!-- :rules="[
  25. {
  26. required: false,
  27. message: '请输入开票金额',
  28. trigger: 'blur'
  29. },
  30. {
  31. required: false,
  32. validator: (rule, value, callback) => {
  33. if (value === undefined || value === null || value === '') {
  34. callback('请输入开票金额');
  35. } else if (parseFloat(value) < 0 || parseFloat(value) > scope.row.defaultAmountTotalPrice) {
  36. callback('开票金额不能小于0且小于等于本次开票合计');
  37. } else {
  38. callback();
  39. }
  40. },
  41. trigger: 'blur'
  42. }
  43. ]" -->
  44. <template v-slot:taxRate="scope">
  45. <el-form-item
  46. style="width: 100%;"
  47. :prop="'detailList.' + scope.$index + '.taxRate'"
  48. :rules="[
  49. {
  50. required: true,
  51. message: '请输入税率',
  52. trigger: 'blur'
  53. },
  54. {
  55. validator: (rule, value, callback) => {
  56. if (value === undefined || value === null || value === '') {
  57. callback('请输入税率');
  58. } else if (parseFloat(value) < 0) {
  59. callback('税率不能小于0');
  60. } else {
  61. callback();
  62. }
  63. },
  64. trigger: 'blur'
  65. }
  66. ]"
  67. >
  68. <span v-if="dialogType === 'view'"> {{ scope.row.taxRate }}</span>
  69. <el-input v-else v-model="scope.row.taxRate" type="number" @input="getNotaxSinglePrice(scope.row, 0, scope.$index)" style="width: 80%;"></el-input>%
  70. </el-form-item>
  71. </template>
  72. </ele-pro-table>
  73. </el-form>
  74. </div>
  75. </template>
  76. <script>
  77. export default {
  78. name: 'tableInfo',
  79. components: {},
  80. props: {
  81. dialogType: {
  82. type: String,
  83. default: ''
  84. },
  85. isOtherSourceFlag: {
  86. type: Boolean,
  87. default: false
  88. },
  89. contactData: {
  90. type: Object,
  91. default: () => {
  92. return {};
  93. }
  94. },
  95. invoiceAmount: {
  96. type: Number,
  97. default: 0
  98. }
  99. },
  100. data() {
  101. return {
  102. columns: [
  103. {
  104. width: 45,
  105. type: 'index',
  106. columnKey: 'index',
  107. align: 'center',
  108. fixed: 'left'
  109. },
  110. {
  111. width: 100,
  112. prop: 'key',
  113. label: '编码',
  114. align: 'center'
  115. },
  116. {
  117. width: 100,
  118. prop: 'typeName',
  119. label: '类型',
  120. slot: 'typeName',
  121. align: 'center'
  122. },
  123. // {
  124. // minWidth: 100,
  125. // prop: 'sourceCode',
  126. // label: '类型编码',
  127. // slot: 'sourceCode',
  128. // align: 'center'
  129. // },
  130. // {
  131. // width: 100,
  132. // prop: 'productCategoryName',
  133. // label: '分类',
  134. // slot: 'productCategoryName',
  135. // align: "center"
  136. // },
  137. // {
  138. // width: 100,
  139. // prop: 'productCode',
  140. // label: '编码',
  141. // slot: 'productCode',
  142. // align: "center"
  143. // },
  144. {
  145. minWidth: 100,
  146. prop: 'productName',
  147. label: '产品名称',
  148. slot: 'productName',
  149. align: 'center'
  150. },
  151. {
  152. minWidth: 100,
  153. prop: 'modelType',
  154. label: '型号',
  155. slot: 'modelType',
  156. align: 'center'
  157. },
  158. {
  159. minWidth: 100,
  160. prop: 'specification',
  161. label: '规格',
  162. slot: 'specification',
  163. align: 'center'
  164. },
  165. // {
  166. // width: 100,
  167. // prop: 'unInvoiceAmount',
  168. // label: '未开票金额',
  169. // slot: 'unInvoiceAmount',
  170. // align: "center"
  171. // },
  172. // {
  173. // width: 80,
  174. // prop: 'singleWeight',
  175. // label: '单量',
  176. // align: 'center'
  177. // },
  178. {
  179. minWidth: 150,
  180. prop: 'invoiceAmount',
  181. label: '本次开票金额',
  182. slot: 'invoiceAmount',
  183. align: 'center'
  184. },
  185. {
  186. minWidth: 150,
  187. prop: 'invoicedAmount',
  188. label: '已开票金额',
  189. align: 'center'
  190. },
  191. {
  192. minWidth: 150,
  193. prop: 'unInvoiceAmount',
  194. label: '未开票金额',
  195. align: 'center'
  196. },
  197. {
  198. minWidth: 150,
  199. prop: 'taxRate',
  200. label: '税率',
  201. align: 'center',
  202. slot: 'taxRate',
  203. // formatter: (_row, _column, cellValue) => {
  204. // return _row.taxRate
  205. // ? _row.taxRate+'%'
  206. // : '';
  207. // },
  208. },
  209. {
  210. minWidth: 100,
  211. prop: 'noTaxSinglePrice',
  212. label: '不含税单价',
  213. slot: 'noTaxSinglePrice',
  214. align: 'center'
  215. },
  216. {
  217. width: 100,
  218. prop: 'totalCount',
  219. label: '订单数量',
  220. // slot: 'totalCount',
  221. align: 'center',
  222. },
  223. {
  224. width: 150,
  225. prop: 'quoteWay',
  226. label: '报价方式',
  227. formatter: (row, column) => {
  228. return row.quoteWay == 1
  229. ? '常规价'
  230. : row.quoteWay == 2
  231. ? '内部价'
  232. : row.quoteWay == 3 ? '议价' : '';
  233. },
  234. align: 'center'
  235. },
  236. {
  237. minWidth: 80,
  238. prop: 'singlePrice',
  239. label: '单价',
  240. slot: 'singlePrice',
  241. align: 'center'
  242. },
  243. {
  244. width: 100,
  245. prop: 'totalPrice',
  246. label: '合计',
  247. align: 'center'
  248. },
  249. {
  250. width: 100,
  251. prop: 'discountRatio',
  252. label: '折让比例',
  253. align: 'center'
  254. },
  255. {
  256. width: 100,
  257. prop: 'discountSinglePrice',
  258. label: '折让单价',
  259. align: 'center'
  260. },
  261. {
  262. width: 100,
  263. prop: 'discountAmount',
  264. label: '折让合计',
  265. align: 'center'
  266. },
  267. // {
  268. // width: 100,
  269. // prop: 'sourceType',
  270. // label: '来源类型',
  271. // slot: 'sourceType',
  272. // align: "center",
  273. // formatter: (row, column) => {
  274. // return row.sourceType == 2 ? '对账销售订单' : '对账采购订单';
  275. // }
  276. // },
  277. ],
  278. tableForm: {
  279. detailList: [
  280. {
  281. key: '',
  282. amountTotalPrice: 0,
  283. details: [{
  284. invoicedAmount: '',
  285. invoiceAmount: '',
  286. taxRate: '',
  287. noTaxSinglePrice: '',
  288. totalCount: '',
  289. quoteWay: '',
  290. singlePrice: '',
  291. totalPrice: '',
  292. discountRatio: 100,
  293. discountSinglePrice: '',
  294. discountAmount: '',
  295. }]
  296. }
  297. ],
  298. productMap: [],
  299. link: []
  300. },
  301. typeList: [
  302. {
  303. label: '销售发货',
  304. value: '10'
  305. },
  306. {
  307. label: '销售退货',
  308. value: '11'
  309. },
  310. {
  311. label: '采购收货',
  312. value: '20'
  313. },
  314. {
  315. label: '采购退货',
  316. value: '21'
  317. }
  318. ],
  319. };
  320. },
  321. computed: {
  322. totalAmountTotalPrice() {
  323. let tempTotal = this.tableForm.detailList.reduce((total, item) => {
  324. return total + (parseFloat(item.invoiceAmount) || 0);
  325. }, 0);
  326. return tempTotal.toFixed(2);
  327. }
  328. },
  329. mounted() {
  330. // this.tableForm = this.form;
  331. },
  332. methods: {
  333. convertToArrayFormat(data, type) {
  334. const result = [];
  335. Object.keys(data.productMap).forEach(key => {
  336. console.log('key!!!', key);
  337. const paymentItem = data.receiptPayments?.filter(item => item?.relatedOrderNo == key) || [];
  338. console.log('paymentItem', paymentItem);
  339. const amountTotalPrice = paymentItem.length ? paymentItem.reduce((acc, cur) => acc + cur.invoiceAmount, 0) : 0;
  340. const defaultAmountTotalPrice = paymentItem.length ? paymentItem.reduce((acc, cur) => acc + cur.unInvoiceAmount, 0) : 0;
  341. const details = data.productMap[key] || [];
  342. details.forEach(detail => {
  343. result.push({
  344. key: key,
  345. amountTotalPrice: amountTotalPrice,
  346. defaultAmountTotalPrice: defaultAmountTotalPrice,
  347. ...detail,
  348. invoiceAmount: detail.invoiceAmount || 0,
  349. });
  350. });
  351. });
  352. if(type == 'setValue') {
  353. const uniqueKeys = [...new Set(result.map(item => item.key))];
  354. uniqueKeys.forEach(key => {
  355. const sameKeyItems = result.filter(item => item.key === key);
  356. if (sameKeyItems.length > 0) {
  357. const defaultAmountTotalPrice = parseFloat(sameKeyItems[0].defaultAmountTotalPrice) || 0;
  358. let remainingTotal = defaultAmountTotalPrice;
  359. sameKeyItems.forEach(item => {
  360. if (remainingTotal <= 0) {
  361. item.invoiceAmount = 0;
  362. } else {
  363. const unInvoiceAmount = parseFloat(item.unInvoiceAmount) || 0;
  364. const allocateAmount = Math.min(remainingTotal, unInvoiceAmount);
  365. item.invoiceAmount = allocateAmount.toFixed(2);
  366. remainingTotal -= allocateAmount;
  367. }
  368. });
  369. }
  370. });
  371. }
  372. return result;
  373. },
  374. // 校验发票金额
  375. invoiceAmountChange(val, item, index) {
  376. console.log('invoiceAmountChange', val, item, index);
  377. console.log('this.tableForm.detailList', this.tableForm.detailList);
  378. if (index != null) {
  379. const key = item.key;
  380. const sameKeyItems = this.tableForm.detailList.filter(d => d.key === key);
  381. console.log('sameKeyItems', sameKeyItems);
  382. let sum = 0;
  383. sameKeyItems.forEach((r) => {
  384. if (r.invoiceAmount) {
  385. sum += Number(r.invoiceAmount);
  386. }
  387. });
  388. // if (val && +val > +item.unInvoiceAmount) {
  389. // this.$message.warning('物品开票金额不能大于未开票金额');
  390. // this.$set(item, 'invoiceAmount', '');
  391. // return;
  392. // }
  393. // if (sum > item.defaultAmountTotalPrice) {
  394. // this.$message.warning('物品开票金额不能大于本次开票合计');
  395. // this.$set(item, 'invoiceAmount', '');
  396. // return;
  397. // }
  398. this.$set(item, 'invoiceAmount', val);
  399. const amountTotalPrice = this.fromPrecision(sum);
  400. const targetItems = this.tableForm.detailList.filter(d => d.key === key);
  401. targetItems.forEach(targetItem => {
  402. this.$set(targetItem, 'amountTotalPrice', amountTotalPrice);
  403. });
  404. console.log('sum', sum, item.invoiceAmount);
  405. this.$emit('invoiceAmountChange', item, sum);
  406. }
  407. },
  408. //计算不含税单价
  409. getNotaxSinglePrice(item, idx, index) {
  410. if (item.singlePrice && item.taxRate) {
  411. this.$set(
  412. item,
  413. 'noTaxSinglePrice',
  414. parseFloat(
  415. (item.singlePrice / (1 + item.taxRate / 100)).toFixed(2)
  416. )
  417. );
  418. } else {
  419. this.$set(item, 'noTaxSinglePrice', '');
  420. }
  421. },
  422. putValue(data) {
  423. let tempData = JSON.parse(JSON.stringify(data));
  424. console.log('tableInfoNew putValue', tempData);
  425. tempData.detailList = this.convertToArrayFormat(tempData, 'putValue');
  426. this.tableForm = tempData;
  427. console.log('tableInfoNew putValue~~~', this.tableForm);
  428. },
  429. setValue(data) {
  430. let tempData = JSON.parse(JSON.stringify(data));
  431. tempData.detailList = this.convertToArrayFormat(tempData, 'setValue');
  432. console.log('tempData.detailList', tempData.detailList);
  433. this.tableForm = tempData;
  434. },
  435. /**
  436. * 分配开票金额到明细项(根据未开票金额unInvoiceAmount限制)
  437. * @param {Array|Object} data - 明细列表或单个明细对象
  438. * @returns {Array|Object} 处理后的数据
  439. */
  440. allocateInvoiceAmount(data) {
  441. if (Array.isArray(data)) {
  442. data.forEach(item => {
  443. if (item && typeof item === 'object') {
  444. this.allocateSingleInvoiceAmount(item);
  445. }
  446. });
  447. return data;
  448. } else if (data && typeof data === 'object') {
  449. this.allocateSingleInvoiceAmount(data);
  450. return data;
  451. }
  452. return data;
  453. },
  454. allocateSingleInvoiceAmount(item) {
  455. console.log('allocateSingleInvoiceAmount~~~', item);
  456. const key = item.key;
  457. const defaultAmountTotalPrice = this.toPrecision(item.defaultAmountTotalPrice);
  458. const sameKeyItems = this.tableForm.detailList.filter(d => d.key === key);
  459. let remainingTotal = defaultAmountTotalPrice;
  460. sameKeyItems.forEach((detail) => {
  461. if (remainingTotal <= 0) {
  462. detail.invoiceAmount = 0;
  463. } else {
  464. const unInvoiceAmount = this.toPrecision(detail.unInvoiceAmount);
  465. if (remainingTotal > unInvoiceAmount) {
  466. detail.invoiceAmount = this.fromPrecision(unInvoiceAmount);
  467. remainingTotal = this.subPrecision(remainingTotal, unInvoiceAmount);
  468. } else {
  469. detail.invoiceAmount = this.fromPrecision(remainingTotal);
  470. remainingTotal = 0;
  471. }
  472. }
  473. });
  474. },
  475. toPrecision(value) {
  476. const num = parseFloat(value) || 0;
  477. return Math.round(num * 100);
  478. },
  479. fromPrecision(value) {
  480. const num = parseFloat(value) || 0;
  481. return (num / 100).toFixed(2);
  482. },
  483. subPrecision(a, b) {
  484. return a - b;
  485. },
  486. setSinglePrice(row, data) {
  487. let index = this.tableForm.detailList.findIndex(item => item.key == row.relatedOrderNo);
  488. // let detail = this.tableForm.detailList[index];
  489. // console.log('tableInfoNew setPrice', row, data, index);
  490. const paymentItem = data.filter(item => item?.relatedOrderNo == row.relatedOrderNo);
  491. console.log('tableInfoNew~~~', this.tableForm.detailList[index]);
  492. this.tableForm.detailList[index].amountTotalPrice = paymentItem.length ? paymentItem.reduce((acc, cur) => acc + +cur.invoiceAmount, 0) : 0;
  493. this.tableForm.detailList[index].defaultAmountTotalPrice = paymentItem.length ? paymentItem.reduce((acc, cur) => acc + +cur.invoiceAmount, 0) : 0;
  494. // console.log('tableInfoNew tableForm', this.tableForm.detailList);
  495. // 直接修改对象,不需要赋值
  496. this.allocateInvoiceAmount(this.tableForm.detailList[index]);
  497. this.tableForm = {...this.tableForm};
  498. this.$forceUpdate();
  499. },
  500. clearTable() {
  501. this.tableForm = {
  502. detailList: [],
  503. productMap: [],
  504. receiptPayments: [],
  505. link: []
  506. };
  507. },
  508. // 当数量变化时更新金额
  509. updateTotalPrice(row) {
  510. if (row.totalCount && row.singlePrice) {
  511. row.totalPrice = (parseFloat(row.totalCount) * parseFloat(row.singlePrice)).toFixed(2);
  512. }
  513. },
  514. getTableValidate() {
  515. return new Promise(async (resolve, reject) => {
  516. // 2. 验证单个表格表单
  517. const formInstance = this.$refs['form'];
  518. if (formInstance && typeof formInstance.validate === 'function') {
  519. formInstance.validate(async (valid, errors) => {
  520. if (!valid && errors) {
  521. // 显示第一个错误信息
  522. const firstError = Object.values(errors)[0][0];
  523. if (firstError) {
  524. this.$message.warning(firstError.message);
  525. }
  526. reject(false);
  527. } else {
  528. // 3. 校验 amountTotalPrice 和 defaultAmountTotalPrice 是否相等
  529. // for (let i = 0; i < this.tableForm.detailList.length; i++) {
  530. // const item = this.tableForm.detailList[i];
  531. // const amountTotalPrice = parseFloat(item.amountTotalPrice) || 0;
  532. // const defaultAmountTotalPrice = parseFloat(item.defaultAmountTotalPrice) || 0;
  533. // if (amountTotalPrice !== defaultAmountTotalPrice) {
  534. // this.$message.warning(`第${i + 1}条本次开票合计(${amountTotalPrice})必须等于本次开票金额合计(${defaultAmountTotalPrice})`);
  535. // reject(false);
  536. // return;
  537. // }
  538. // }
  539. resolve(this.tableForm);
  540. console.log('this.tableForm~~~',this.tableForm);
  541. }
  542. });
  543. } else {
  544. // 4. 校验 amountTotalPrice 和 defaultAmountTotalPrice 是否相等
  545. // for (let i = 0; i < this.tableForm.detailList.length; i++) {
  546. // const item = this.tableForm.detailList[i];
  547. // const amountTotalPrice = parseFloat(item.amountTotalPrice) || 0;
  548. // const defaultAmountTotalPrice = parseFloat(item.defaultAmountTotalPrice) || 0;
  549. // // if (amountTotalPrice !== defaultAmountTotalPrice) {
  550. // // this.$message.warning(`第${i + 1}条本次开票合计(${amountTotalPrice})必须等于本次开票金额合计(${defaultAmountTotalPrice})`);
  551. // // reject(false);
  552. // // return;
  553. // // }
  554. // }
  555. resolve(this.tableForm);
  556. console.log('this.tableForm~~~',this.tableForm);
  557. }
  558. });
  559. },
  560. validateForm(callback) {
  561. //开始表单校验
  562. this.$refs.form.validate((valid, obj) => {
  563. if (obj) {
  564. let messages = Object.keys(obj).map((key) => obj[key][0]);
  565. if (messages.length > 0) {
  566. this.$message.warning(messages[0].message);
  567. }
  568. }
  569. callback(valid);
  570. });
  571. },
  572. setSelectData(val) {
  573. this.tableForm.link = [{}];
  574. this.$set(this.tableForm.link[0], 'linkId', val.id);
  575. this.$set(this.tableForm.link[0], 'linkName', val.name);
  576. this.$set(this.tableForm.link[0], 'linkCode', val.code);
  577. this.$set(this.tableForm.link[0], 'linkType', val.linkType);
  578. this.$set(this.tableForm.link[0], 'linkTypeName', val.linkTypeName);
  579. },
  580. getTableData() {
  581. const data = JSON.parse(JSON.stringify(this.tableForm.detailList));
  582. const transformedData = data.reduce((result, item) => {
  583. if (!result[item.key]) {
  584. result[item.key] = [];
  585. }
  586. const { key, amountTotalPrice, defaultAmountTotalPrice, ...detail } = item;
  587. result[item.key].push(detail);
  588. return result;
  589. }, {});
  590. return transformedData;
  591. }
  592. }
  593. };
  594. </script>
  595. <style scoped lang="scss">
  596. .time-form .el-form-item {
  597. margin-bottom: 0 !important;
  598. }
  599. </style>