inventoryTable.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. <template>
  2. <el-form ref="form" :model="form" :rules="rules">
  3. <ele-pro-table
  4. ref="table"
  5. :needPage="false"
  6. :columns="columns"
  7. @columns-change="handleColumnChange"
  8. :cache-key="cacheKeyUrl"
  9. :datasource="form.datasource"
  10. class="time-form"
  11. >
  12. <!-- 表头工具栏 -->
  13. <template v-slot:toolbar>
  14. <div class="headbox" v-if="!contractId && isShowAdd">
  15. <el-button
  16. size="small"
  17. type="primary"
  18. icon="el-icon-plus"
  19. class="ele-btn-icon"
  20. @click="handlAdd"
  21. >
  22. 新增
  23. </el-button>
  24. </div>
  25. </template>
  26. <template v-slot:technicalDrawings="{ row, $index }">
  27. <el-form-item
  28. style="margin-bottom: 20px"
  29. :prop="'datasource.' + $index + '.technicalDrawings'"
  30. >
  31. <fileMain v-model="row.technicalDrawings" type="view"></fileMain>
  32. </el-form-item>
  33. </template>
  34. <template v-slot:industryArtFiles="{ row, $index }">
  35. <el-form-item
  36. style="margin-bottom: 20px"
  37. :prop="'datasource.' + $index + '.industryArtFiles'"
  38. >
  39. <fileMain v-model="row.industryArtFiles" type="view"></fileMain>
  40. </el-form-item>
  41. </template>
  42. <template v-slot:otherFiles="{ row, $index }">
  43. <el-form-item
  44. style="margin-bottom: 20px"
  45. :prop="'datasource.' + $index + '.otherFiles'"
  46. >
  47. <fileMain v-model="row.otherFiles" type="view"></fileMain>
  48. </el-form-item>
  49. </template>
  50. <template v-slot:customerReqFiles="{ row, $index }">
  51. <el-form-item
  52. style="margin-bottom: 20px"
  53. :prop="'datasource.' + $index + '.customerReqFiles'"
  54. >
  55. <fileMain v-model="row.customerReqFiles" type="view"></fileMain>
  56. </el-form-item>
  57. </template>
  58. <template v-slot:remark="{ row, $index }">
  59. <el-form-item
  60. style="margin-bottom: 20px"
  61. :prop="'datasource.' + $index + '.remark'"
  62. >
  63. <el-input
  64. clearable
  65. v-model="row.remark"
  66. type="textarea"
  67. placeholder="请输入"
  68. />
  69. </el-form-item>
  70. </template>
  71. <template v-slot:totalCount="scope">
  72. <el-form-item
  73. style="margin-bottom: 20px"
  74. :prop="'datasource.' + scope.$index + '.totalCount'"
  75. :rules="[
  76. { required: true, message: '请输入本次收货数量', trigger: 'blur' },
  77. { validator: validateTotalCount(scope.row), trigger: 'blur' }
  78. ]"
  79. >
  80. <!-- { validator: validateTotalCount(scope.row), trigger: 'blur' }-->
  81. <el-input
  82. v-model="scope.row.totalCount"
  83. @input="(val) => changeNum(val, scope.row, scope.$index)"
  84. ></el-input>
  85. </el-form-item>
  86. </template>
  87. <template v-slot:warehouseId="scope">
  88. <el-form-item
  89. style="margin-bottom: 20px"
  90. :prop="'datasource.' + scope.$index + '.warehouseId'"
  91. :rules="[{ required: true, message: '请选择仓库', trigger: 'blur' }]"
  92. >
  93. <el-select
  94. v-model="scope.row.warehouseId"
  95. placeholder="请选择"
  96. @change="warehouseChange(scope.$index, scope.row)"
  97. >
  98. <el-option
  99. v-for="item in warehouseList"
  100. :key="item.id"
  101. :label="item.name"
  102. :value="item.id"
  103. >
  104. </el-option>
  105. </el-select>
  106. </el-form-item>
  107. </template>
  108. <template v-slot:warehouseNum="scope">
  109. <el-form-item style="margin-bottom: 20px">
  110. {{ scope.row.warehouseNum }}
  111. </el-form-item>
  112. </template>
  113. <template v-slot:headerTotalCount="{ column }">
  114. <span class="is-required">{{ column.label }}</span>
  115. </template>
  116. <template v-slot:headerWarehouseId="{ column }">
  117. <span class="is-required">{{ column.label }}</span>
  118. </template>
  119. <!-- 操作列 -->
  120. <template v-slot:action="scope">
  121. <el-popconfirm
  122. class="ele-action"
  123. title="确定要删除吗?"
  124. @confirm="remove(scope.$index)"
  125. >
  126. <template v-slot:reference>
  127. <el-link type="danger" :underline="false" icon="el-icon-delete">
  128. 删除
  129. </el-link>
  130. </template>
  131. </el-popconfirm>
  132. </template>
  133. </ele-pro-table>
  134. <product-list
  135. ref="productListRef"
  136. :orderId="orderId"
  137. @changeParent="changeParent"
  138. ></product-list>
  139. </el-form>
  140. </template>
  141. <script>
  142. import { emailReg, phoneReg, numberReg } from 'ele-admin';
  143. import dictMixins from '@/mixins/dictMixins';
  144. import productList from './product-list.vue';
  145. import {
  146. getWarehouseList,
  147. getWarehouseOutStock
  148. } from '@/api/saleManage/saleorder';
  149. import { copyObj } from '@/utils/util';
  150. import { getFile } from '@/api/system/file';
  151. // import fileMain from '@/components/addDoc/index.vue';
  152. import tabMixins from '@/mixins/tableColumnsMixin';
  153. export default {
  154. mixins: [dictMixins,tabMixins],
  155. props: {
  156. orderId: String,
  157. isShowAdd: {
  158. type: Boolean,
  159. default: true
  160. },
  161. pricingWay: [Number, String]
  162. },
  163. components: {
  164. // fileMain,
  165. productList
  166. },
  167. data() {
  168. const defaultForm = {
  169. key: null,
  170. endTime: '',
  171. isFirst: 0,
  172. name: '',
  173. startTime: '',
  174. workHour: '',
  175. technicalDrawings: []
  176. // warehouseCode:"",
  177. // warehouseId:'',
  178. // warehouseName:'',
  179. };
  180. return {
  181. discountTotalPrice: 0.0,
  182. allPrice: 0.0,
  183. numberReg,
  184. defaultForm,
  185. cacheKeyUrl:'eos-saleManage-entrustedReceive-inventoryTable',
  186. warehouseList: [],
  187. form: {
  188. datasource: []
  189. },
  190. rules: {},
  191. columns: [
  192. {
  193. width: 45,
  194. type: 'index',
  195. columnKey: 'index',
  196. align: 'center',
  197. fixed: 'left'
  198. },
  199. {
  200. minWidth: 200,
  201. prop: 'productName',
  202. label: '名称',
  203. slot: 'productName',
  204. align: 'center'
  205. },
  206. {
  207. minWidth: 120,
  208. prop: 'productCode',
  209. label: '编码',
  210. slot: 'productCode',
  211. align: 'center'
  212. },
  213. {
  214. minWidth: 150,
  215. prop: 'productCategoryName',
  216. label: '类型',
  217. slot: 'productCategoryName',
  218. align: 'center'
  219. },
  220. {
  221. prop: 'batchNo',
  222. label: '批次号',
  223. align: 'center',
  224. showOverflowTooltip: true,
  225. minWidth: 100
  226. },
  227. {
  228. minWidth: 100,
  229. prop: 'productBrand',
  230. label: '牌号',
  231. slot: 'productBrand',
  232. align: 'center'
  233. },
  234. {
  235. minWidth: 120,
  236. prop: 'modelType',
  237. label: '型号',
  238. slot: 'modelType',
  239. align: 'center'
  240. },
  241. {
  242. minWidth: 120,
  243. prop: 'specification',
  244. label: '规格',
  245. slot: 'specification',
  246. align: 'center'
  247. },
  248. {
  249. minWidth: 150,
  250. prop: 'customerMark',
  251. label: '客户代号',
  252. slot: 'customerMark',
  253. align: 'center'
  254. },
  255. {
  256. minWidth: 200,
  257. prop: 'warehouseId',
  258. label: '仓库名称',
  259. slot: 'warehouseId',
  260. headerSlot: 'headerWarehouseId',
  261. align: 'center'
  262. },
  263. {
  264. minWidth: 80,
  265. prop: 'warehouseNum',
  266. label: '库存',
  267. slot: 'warehouseNum',
  268. align: 'center'
  269. },
  270. {
  271. width: 110,
  272. prop: 'orderTotalCount',
  273. label: '订单总数量',
  274. slot: 'orderTotalCount',
  275. align: 'center'
  276. },
  277. {
  278. width: 120,
  279. prop: 'totalCount',
  280. label: '本次收货数量',
  281. slot: 'totalCount',
  282. headerSlot: 'headerTotalCount',
  283. align: 'center'
  284. },
  285. {
  286. width: 100,
  287. prop: 'receiveTotalCount',
  288. label: '已收货总数',
  289. slot: 'receiveTotalCount',
  290. align: 'center'
  291. },
  292. {
  293. minWidth: 100,
  294. prop: 'measuringUnit',
  295. label: '计量单位',
  296. slot: 'measuringUnit',
  297. align: 'center'
  298. },
  299. {
  300. width: 120,
  301. prop: 'singleWeight',
  302. label: '单重',
  303. slot: 'singleWeight',
  304. align: 'center'
  305. },
  306. {
  307. width: 100,
  308. prop: 'receiveTotalWeight',
  309. label: '收货总重',
  310. slot: 'receiveTotalWeight',
  311. align: 'center'
  312. },
  313. {
  314. width: 100,
  315. prop: 'weightUnit',
  316. label: '重量单位',
  317. slot: 'weightUnit',
  318. align: 'center'
  319. },
  320. {
  321. width: 160,
  322. prop: 'pricingWay',
  323. label: '计价方式',
  324. slot: 'pricingWay',
  325. align: 'center',
  326. formatter: (row, column) => {
  327. return row.pricingWay == 1
  328. ? '按数量计费'
  329. : row.pricingWay == 2
  330. ? '按重量计费'
  331. : '';
  332. }
  333. },
  334. {
  335. width: 80,
  336. prop: 'singlePrice',
  337. label: '单价',
  338. slot: 'singlePrice',
  339. align: 'center'
  340. },
  341. {
  342. width: 100,
  343. prop: 'taxRate',
  344. label: '税率',
  345. formatter: (_row, _column, cellValue) => {
  346. return _row.taxRate ? _row.taxRate + '%' : '';
  347. },
  348. align: 'center'
  349. },
  350. {
  351. width: 100,
  352. prop: 'discountSinglePrice',
  353. label: '折让单价',
  354. slot: 'discountSinglePrice',
  355. align: 'center',
  356. formatter: (_row, _column, cellValue) => {
  357. return _row.discountSinglePrice
  358. ? Number(_row.discountSinglePrice).toFixed(2)
  359. : '';
  360. }
  361. },
  362. {
  363. width: 80,
  364. prop: 'totalPrice',
  365. label: '合计',
  366. slot: 'totalPrice',
  367. align: 'center'
  368. },
  369. {
  370. width: 100,
  371. prop: 'discountTotalPrice',
  372. label: '折让合计',
  373. slot: 'discountTotalPrice',
  374. align: 'center',
  375. formatter: (_row, _column, cellValue) => {
  376. return _row.discountTotalPrice
  377. ? Number(_row.discountTotalPrice).toFixed(2)
  378. : '';
  379. }
  380. },
  381. // {
  382. // width: 80,
  383. // prop: 'deliveryDays',
  384. // label: '交期(天)',
  385. // slot: 'deliveryDays'
  386. // },
  387. // {
  388. // width: 160,
  389. // prop: 'deliveryDeadline',
  390. // label: '交期截止日期',
  391. // slot: 'deliveryDeadline',
  392. // show: this.isDeliveryDeadline
  393. // },
  394. {
  395. minWidth: 140,
  396. prop: 'customerExpectDeliveryDeadline',
  397. label: '客户期望交期',
  398. slot: 'customerExpectDeliveryDeadline',
  399. headerSlot: 'headerCustomerExpectDeliveryDeadline',
  400. align: 'center'
  401. },
  402. {
  403. minWidth: 140,
  404. prop: 'produceDeliveryDeadline',
  405. label: '生产交付交期',
  406. slot: 'produceDeliveryDeadline',
  407. align: 'center'
  408. },
  409. {
  410. width: 100,
  411. prop: 'guaranteePeriod',
  412. label: '有效期',
  413. slot: 'guaranteePeriod',
  414. formatter: (_row, _column, cellValue) => {
  415. return (
  416. (_row.guaranteePeriod || '') + _row.guaranteePeriodUnitName
  417. );
  418. },
  419. align: 'center'
  420. },
  421. {
  422. minWidth: 120,
  423. prop: 'guaranteePeriodDeadline',
  424. label: '有效期截止日期',
  425. align: 'center'
  426. },
  427. {
  428. minWidth: 200,
  429. prop: 'customerReqFiles',
  430. label: '客户需求',
  431. slot: 'customerReqFiles',
  432. align: 'center'
  433. },
  434. {
  435. width: 130,
  436. prop: 'technicalAnswerName',
  437. label: '技术答疑人',
  438. slot: 'technicalAnswerName',
  439. align: 'center'
  440. },
  441. {
  442. width: 220,
  443. prop: 'technicalParams',
  444. label: '技术参数',
  445. slot: 'technicalParams',
  446. align: 'center'
  447. },
  448. {
  449. width: 240,
  450. prop: 'technicalDrawings',
  451. label: '技术图纸',
  452. slot: 'technicalDrawings',
  453. align: 'center'
  454. },
  455. {
  456. width: 240,
  457. prop: 'industryArtFiles',
  458. label: '工艺附件',
  459. slot: 'industryArtFiles',
  460. align: 'center'
  461. },
  462. {
  463. width: 240,
  464. prop: 'otherFiles',
  465. label: '其他附件',
  466. slot: 'otherFiles',
  467. align: 'center'
  468. },
  469. {
  470. width: 220,
  471. prop: 'remark',
  472. label: '备注',
  473. slot: 'remark',
  474. align: 'center'
  475. },
  476. {
  477. columnKey: 'action',
  478. label: '操作',
  479. width: 120,
  480. align: 'center',
  481. resizable: false,
  482. slot: 'action',
  483. fixed: 'right',
  484. showOverflowTooltip: true
  485. }
  486. ]
  487. };
  488. },
  489. created() {
  490. getWarehouseList().then((res) => {
  491. this.warehouseList = res;
  492. });
  493. },
  494. computed: {
  495. canHandl() {
  496. return this.form.datasource.length;
  497. },
  498. contractId() {
  499. return this.$store.state.order.contractId;
  500. }
  501. },
  502. methods: {
  503. downloadFile(file) {
  504. getFile({ objectName: file.storePath }, file.name);
  505. },
  506. async warehouseChange(index, row) {
  507. let warehouseIds =
  508. this.form.datasource
  509. .filter(
  510. (item, i) => row.productCode == item.productCode && index != i
  511. )
  512. .map((item) => item.warehouseId) || [];
  513. const data = this.warehouseList.find(
  514. (item) => item.id == row.warehouseId
  515. );
  516. if (warehouseIds.length > 0 && warehouseIds.includes(row.warehouseId)) {
  517. row.warehouseId = '';
  518. return this.$message.error('同一个产品不能选择相同的仓库');
  519. }
  520. this.$set(this.form.datasource[index], 'warehouseName', data.name);
  521. this.$set(this.form.datasource[index], 'warehouseCode', data.code);
  522. const warehouseOutStock = await getWarehouseOutStock({
  523. warehouseId: data.id,
  524. code: row.productCode
  525. });
  526. this.$set(
  527. this.form.datasource[index],
  528. 'warehouseNum',
  529. warehouseOutStock
  530. );
  531. },
  532. //修改数量更新合计
  533. changeNum(val, row, index) {
  534. console.log(val, row, index);
  535. this.$set(
  536. this.form.datasource[index],
  537. 'receiveTotalWeight',
  538. val ? val * row.singleWeight : ''
  539. );
  540. this.$set(
  541. this.form.datasource[index],
  542. 'totalPrice',
  543. this.getTotalPrice(row).toFixed(2)
  544. );
  545. this.$set(
  546. this.form.datasource[index],
  547. 'discountTotalPrice',
  548. this.getDiscountTotalPrice(row).toFixed(2)
  549. );
  550. this.$refs.table.reload();
  551. },
  552. getTotalPrice(row) {
  553. let num = 0;
  554. if (row.pricingWay == 1) {
  555. num = Number(row.singlePrice) * Number(row.totalCount);
  556. }
  557. if (row.pricingWay == 2) {
  558. num =
  559. Number(row.singlePrice) *
  560. Number(row.totalCount) *
  561. Number(row.singleWeight);
  562. }
  563. return num;
  564. },
  565. getDiscountTotalPrice(row) {
  566. let num = 0;
  567. if (row.pricingWay == 1) {
  568. num = Number(row.discountSinglePrice) * Number(row.totalCount);
  569. }
  570. if (row.pricingWay == 2) {
  571. num =
  572. Number(row.discountSinglePrice) *
  573. Number(row.totalCount) *
  574. Number(row.singleWeight);
  575. }
  576. return num;
  577. },
  578. //选择产品回调
  579. changeParent(obj, idx) {
  580. obj.orderTotalCount = obj.totalCount;
  581. obj.id = '';
  582. obj['pricingWay'] = obj.pricingWay || this.pricingWay;
  583. this.$set(
  584. this.form.datasource,
  585. this.form.datasource.length,
  586. copyObj(obj)
  587. );
  588. },
  589. validateTotalCount(row) {
  590. return (rule, value, callback) => {
  591. if (isNaN(value) || Number(value) <= 0) {
  592. callback(new Error('请输入大于0的数字'));
  593. } else if (
  594. Number(value) + Number(row.receiveTotalCount) >
  595. row.orderTotalCount
  596. ) {
  597. callback(new Error('输入的数字不能大于最大发货值'));
  598. } else {
  599. callback();
  600. }
  601. };
  602. },
  603. // 返回列表数据
  604. getTableValue() {
  605. let comitDatasource = this.form.datasource;
  606. if (comitDatasource.length === 0) return [];
  607. comitDatasource.forEach(async (v) => {
  608. v.totalCount = Number(v.totalCount);
  609. item['pricingWay'] = item.pricingWay || this.pricingWay;
  610. v.technicalDrawings = Array.isArray(v.technicalDrawings)
  611. ? v.technicalDrawings
  612. : [];
  613. v.customerReqFiles = Array.isArray(v.customerReqFiles)
  614. ? v.customerReqFiles
  615. : [];
  616. v.industryArtFiles = v.industryArtFiles || [];
  617. v.otherFiles = v.otherFiles || [];
  618. // v.ownerId = this.warehouseList.find(item => v.warehouseId == item.id)?.ownerId 获取仓库人员id
  619. });
  620. return comitDatasource;
  621. },
  622. getPrice() {
  623. return [this.allPrice];
  624. },
  625. //修改回显
  626. putTableValue(data) {
  627. if (data) {
  628. this.form.datasource = data;
  629. data.forEach(async (item, index) => {
  630. if (item.warehouseId) {
  631. this.$set(
  632. this.form.datasource[index],
  633. 'warehouseNum',
  634. await getWarehouseOutStock({
  635. warehouseId: item.warehouseId,
  636. code: item.productCode
  637. })
  638. );
  639. }
  640. //发货数量因已发货数量被修改需要重新计算金额,总重
  641. this.changeNum(item.totalCount, item, index);
  642. });
  643. }
  644. },
  645. //选择产品
  646. handParent(row, index) {
  647. let item = {
  648. id: row.productCode
  649. };
  650. this.$refs.productListRef.open(item, index);
  651. },
  652. //选择技术人回调
  653. changeAnswer(obj, idx) {
  654. this.$set(this.form.datasource[idx], 'technicalAnswerId', obj.id);
  655. this.$set(this.form.datasource[idx], 'technicalAnswerName', obj.name);
  656. },
  657. handHead(row, index) {
  658. let item = {
  659. id: row.technicalAnswerId
  660. };
  661. this.$refs.headRef.open(item, index);
  662. },
  663. remove(i) {
  664. this.form.datasource.splice(i, 1);
  665. this.setSort();
  666. },
  667. // 清空表格
  668. restTable() {
  669. this.form.datasource = [];
  670. },
  671. // 重新排序
  672. setSort() {
  673. this.form.datasource.forEach((n, index) => {
  674. n.key = index + 1;
  675. });
  676. },
  677. // 添加
  678. handlAdd() {
  679. if (!this.orderId) return this.$message.error('请先选择订单');
  680. this.$refs.productListRef.open(this.form.datasource);
  681. },
  682. validateForm(callback) {
  683. //开始表单校验
  684. this.$refs.form.validate((valid, obj) => {
  685. if (obj) {
  686. let messages = Object.keys(obj).map((key) => obj[key][0]);
  687. if (messages.length > 0) {
  688. this.$message.warning(messages[0].message);
  689. }
  690. }
  691. callback(valid);
  692. });
  693. }
  694. }
  695. };
  696. </script>
  697. <style lang="scss" scoped>
  698. .headbox {
  699. display: flex;
  700. justify-content: space-between;
  701. align-items: center;
  702. .amount {
  703. font-size: 14px;
  704. font-weight: bold;
  705. padding-right: 30px;
  706. }
  707. }
  708. .time-form .el-form-item {
  709. margin-bottom: 0 !important;
  710. }
  711. ::v-deep .period {
  712. display: flex;
  713. .borderleftnone {
  714. .el-input--medium .el-input__inner {
  715. border-top-right-radius: 0;
  716. border-bottom-right-radius: 0;
  717. }
  718. }
  719. .borderrightnone {
  720. .el-input--medium .el-input__inner {
  721. border-top-left-radius: 0;
  722. border-bottom-left-radius: 0;
  723. }
  724. }
  725. }
  726. ::v-deep .time-form tbody > tr:hover > td {
  727. background-color: transparent !important;
  728. }
  729. ::v-deep .time-form .el-table tr {
  730. background-color: #ffffff;
  731. }
  732. .pricebox {
  733. display: flex;
  734. justify-content: flex-start;
  735. align-items: center;
  736. font-weight: bold;
  737. }
  738. </style>