inventoryTable.vue 20 KB

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