plan-edit-dialog.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. <template>
  2. <ele-modal
  3. :visible.sync="visible"
  4. :closed="cancel"
  5. :title="`${type == 'add' ? '创建' : '编辑'}配料计划`"
  6. custom-class="ele-dialog-form"
  7. :close-on-click-modal="true"
  8. :close-on-press-escape="false"
  9. width="80%"
  10. :maxable="true"
  11. >
  12. <el-form
  13. :model="formData"
  14. ref="formRef"
  15. label-width="120px"
  16. class="ele-body"
  17. :rules="rules"
  18. >
  19. <el-row :gutter="24">
  20. <el-col :span="8">
  21. <el-form-item label="需求类型" prop="demandType">
  22. <el-select
  23. v-model="formData.demandType"
  24. placeholder="请选择"
  25. style="width: 100%"
  26. >
  27. <el-option
  28. v-for="item in demandTypeList"
  29. :key="item.dictCode"
  30. :label="item.dictValue"
  31. :value="item.dictCode"
  32. >
  33. </el-option>
  34. </el-select>
  35. </el-form-item>
  36. </el-col>
  37. <el-col :span="8">
  38. <el-form-item label="计划名称" prop="name">
  39. <el-input placeholder="请选择" v-model="formData.name"></el-input>
  40. </el-form-item>
  41. </el-col>
  42. <el-col :span="8">
  43. <el-form-item label="备注" prop="remark">
  44. <el-input placeholder="备注" v-model="formData.remark"></el-input>
  45. </el-form-item>
  46. </el-col>
  47. </el-row>
  48. </el-form>
  49. <el-form :model="formData" ref="tableForm">
  50. <ele-pro-table
  51. ref="table"
  52. :needPage="false"
  53. :columns="columns"
  54. row-key="id"
  55. >
  56. <template v-slot:toolbar>
  57. <el-button
  58. size="small"
  59. type="primary"
  60. icon="el-icon-plus"
  61. class="ele-btn-icon"
  62. @click="saleAdd"
  63. >
  64. 添加销售订单
  65. </el-button>
  66. </template>
  67. <template v-slot:code="{ row }">
  68. <el-input
  69. placeholder="请输入"
  70. readonly
  71. :value="row.code || row.salesOrderCode"
  72. ></el-input>
  73. </template>
  74. <template v-slot:productionPlanId="{ row }">
  75. <el-link
  76. type="primary"
  77. v-if="!row.productionPlanId"
  78. :underline="false"
  79. @click.native="openVersion(row)"
  80. >选择</el-link
  81. >
  82. </template>
  83. <template v-slot:action="{ row, $index }">
  84. <template>
  85. <el-link
  86. type="primary"
  87. :underline="false"
  88. @click="categorySelect(row)"
  89. >
  90. 添加物料
  91. </el-link>
  92. <el-popconfirm
  93. class="ele-action"
  94. title="确定要删除此销售订单吗?"
  95. @confirm="remove(row, $index)"
  96. >
  97. <template v-slot:reference>
  98. <el-link type="danger" :underline="false" icon="el-icon-delete">
  99. 删除
  100. </el-link>
  101. </template>
  102. </el-popconfirm>
  103. </template>
  104. </template>
  105. <!-- 展开内容 -->
  106. <template v-slot:expand="{ row, $index }">
  107. <div
  108. style="
  109. width: calc(100% - 95px);
  110. min-height: 60px;
  111. margin-left: 95px;
  112. "
  113. v-if="row.materialList.length > 0"
  114. >
  115. <ele-pro-table
  116. :toolbar="false"
  117. toolsTheme="none"
  118. ref="table2"
  119. :need-page="false"
  120. :datasource="row.materialList"
  121. :columns="columns2"
  122. :key="row.categoryId + '-' + $index"
  123. >
  124. <template v-slot:sort="{ $index }">
  125. {{ $index }}
  126. </template>
  127. <template v-slot:unit="{ row }">
  128. {{ row.unit }}
  129. </template>
  130. <template v-slot:demandQuantity="{ row }">
  131. <el-input
  132. v-model="row.demandQuantity"
  133. placeholder="请输入"
  134. @input="
  135. (value) =>
  136. (row.demandQuantity = value.replace(
  137. /^(-)*(\d+)\.(\d\d\d\d\d\d).*$/,
  138. '$1$2.$3'
  139. ))
  140. "
  141. ></el-input>
  142. </template>
  143. <template v-slot:purchaseQuantity="{ row }">
  144. <el-input
  145. v-model="row.purchaseQuantity"
  146. placeholder="请输入"
  147. @input="
  148. (value) =>
  149. (row.purchaseQuantity = value.replace(
  150. /^(-)*(\d+)\.(\d\d\d\d\d\d).*$/,
  151. '$1$2.$3'
  152. ))
  153. "
  154. ></el-input>
  155. </template>
  156. <template v-slot:deliveryMethod="{ row }">
  157. <el-select
  158. clearable
  159. class="ele-block"
  160. v-model="row.deliveryMethod"
  161. placeholder="请选择"
  162. >
  163. <el-option
  164. label="一次性到货"
  165. :value="1"
  166. @click.native="row.timeList = null"
  167. />
  168. <el-option
  169. label="分批到货"
  170. :value="2"
  171. @click.native="
  172. handleMethod(row);
  173. row.requireDeliveryTime = null;
  174. "
  175. />
  176. </el-select>
  177. </template>
  178. <template v-slot:requireDeliveryTime="{ row }">
  179. <el-date-picker
  180. style="width: 100%"
  181. clearable
  182. v-model="row.requireDeliveryTime"
  183. type="date"
  184. v-if="row.deliveryMethod == 1"
  185. value-format="yyyy-MM-dd"
  186. placeholder="请选择日期"
  187. >
  188. </el-date-picker>
  189. <el-link
  190. type="primary"
  191. :underline="false"
  192. v-if="row.deliveryMethod == 2"
  193. @click.native="handleMethod(row)"
  194. >
  195. 设置分批时间
  196. </el-link>
  197. </template>
  198. <template v-slot:imgUrl="{ row }">
  199. <fileUpload
  200. v-model="row.imgUrl"
  201. module="main"
  202. :showLib="false"
  203. :limit="1"
  204. />
  205. </template>
  206. <template v-slot:files="{ row }">
  207. <fileUpload
  208. v-model="row.files"
  209. module="main"
  210. :showLib="false"
  211. :limit="1"
  212. />
  213. </template>
  214. <template v-slot:action="{ row }">
  215. <el-popconfirm
  216. class="ele-action"
  217. title="确定要删除当前物料吗?"
  218. @confirm="remove2(row)"
  219. >
  220. <template v-slot:reference>
  221. <el-link
  222. type="danger"
  223. :underline="false"
  224. icon="el-icon-delete"
  225. >
  226. 删除
  227. </el-link>
  228. </template>
  229. </el-popconfirm>
  230. </template>
  231. </ele-pro-table>
  232. </div>
  233. </template>
  234. </ele-pro-table>
  235. </el-form>
  236. <div slot="footer">
  237. <el-button @click="cancel">取消</el-button>
  238. <el-button type="primary" @click="confirm">保存</el-button>
  239. </div>
  240. <saleOrderPop ref="saleOrderRef" @chooseOrder="chooseOrder"></saleOrderPop>
  241. <ProductModal ref="productRefs" @chooseModal="chooseModal" />
  242. <ProductionVersion
  243. ref="versionRefs"
  244. @changeProduct="changeProduct"
  245. ></ProductionVersion>
  246. <timeDialog ref="timeDialogRef" @chooseTime="chooseTime"></timeDialog>
  247. </ele-modal>
  248. </template>
  249. <script>
  250. import saleOrderPop from './saleOrderPop.vue';
  251. import ProductModal from './ProductModal.vue';
  252. import {
  253. listBomBySalesOrderIdsOverride,
  254. listBomBySalesOrderId,
  255. save,
  256. getById
  257. } from '@/api/materialPlan/index';
  258. import ProductionVersion from '@/components/CreatePlan/ProductionVersion2.vue';
  259. import fileUpload from '@/components/upload/fileUpload';
  260. import timeDialog from './timeDialog';
  261. import dictMixins from '@/mixins/dictMixins';
  262. export default {
  263. components: {
  264. saleOrderPop,
  265. ProductModal,
  266. ProductionVersion,
  267. fileUpload,
  268. timeDialog
  269. },
  270. mixins: [dictMixins],
  271. data() {
  272. return {
  273. visible: false,
  274. type: 'add',
  275. tableData: [],
  276. xsId: null,
  277. // 表格列配置
  278. columns: [
  279. {
  280. width: 45,
  281. type: 'expand',
  282. columnKey: 'materialList',
  283. align: 'center',
  284. slot: 'expand'
  285. },
  286. {
  287. width: 50,
  288. label: '序号',
  289. type: 'index',
  290. align: 'center',
  291. slot: 'index'
  292. },
  293. {
  294. prop: 'code',
  295. label: '销售订单号',
  296. slot: 'code',
  297. showOverflowTooltip: true,
  298. align: 'center',
  299. minWidth: 170,
  300. sortable: true
  301. },
  302. {
  303. prop: 'customerName',
  304. label: '客户名称',
  305. align: 'center',
  306. showOverflowTooltip: true
  307. },
  308. {
  309. prop: 'deliveryNum',
  310. label: '客户代号',
  311. align: 'center',
  312. showOverflowTooltip: true
  313. },
  314. {
  315. prop: 'productCode',
  316. label: '产品编码',
  317. align: 'center',
  318. showOverflowTooltip: true,
  319. minWidth: 140
  320. },
  321. {
  322. prop: 'productName',
  323. label: '产品名称',
  324. align: 'center',
  325. minWidth: 120
  326. },
  327. {
  328. prop: 'model',
  329. label: '型号',
  330. align: 'center',
  331. minWidth: 120
  332. },
  333. {
  334. prop: 'brandNo',
  335. label: '牌号',
  336. align: 'center'
  337. },
  338. {
  339. prop: 'deliveryTime',
  340. label: '交付日期',
  341. align: 'center',
  342. showOverflowTooltip: true
  343. },
  344. {
  345. prop: 'contractNum',
  346. label: '订单数量',
  347. align: 'center'
  348. },
  349. {
  350. prop: 'lackNum',
  351. label: '欠交数量',
  352. align: 'center'
  353. },
  354. {
  355. prop: 'productionPlanId',
  356. label: '工艺路线',
  357. slot: 'productionPlanId',
  358. align: 'center',
  359. minWidth: 110
  360. },
  361. {
  362. columnKey: 'action',
  363. label: '操作',
  364. width: 150,
  365. align: 'center',
  366. resizable: false,
  367. slot: 'action',
  368. showOverflowTooltip: true
  369. }
  370. ],
  371. columns2: [
  372. {
  373. width: 50,
  374. label: '序号',
  375. prop: 'sort',
  376. slot: 'sort',
  377. align: 'center'
  378. },
  379. {
  380. label: '物料名称',
  381. prop: 'name',
  382. align: 'center'
  383. },
  384. {
  385. label: '物料编码',
  386. prop: 'code',
  387. align: 'center'
  388. },
  389. {
  390. label: '牌号',
  391. prop: 'brandNum',
  392. align: 'center'
  393. },
  394. {
  395. prop: 'specification',
  396. label: '规格',
  397. align: 'center',
  398. showOverflowTooltip: true,
  399. minWidth: 110
  400. },
  401. {
  402. label: '型号',
  403. prop: 'modelType',
  404. align: 'center'
  405. },
  406. {
  407. prop: 'inventoryQuantity',
  408. label: '库存',
  409. showOverflowTooltip: true
  410. },
  411. {
  412. prop: 'unit',
  413. label: '计量单位',
  414. showOverflowTooltip: true,
  415. action: 'unit',
  416. slot: 'unit'
  417. },
  418. {
  419. label: '需求数量',
  420. slot: 'demandQuantity',
  421. action: 'demandQuantity',
  422. align: 'center'
  423. },
  424. {
  425. label: '采购数量',
  426. slot: 'purchaseQuantity',
  427. action: 'purchaseQuantity',
  428. align: 'center'
  429. },
  430. {
  431. label: '到货方式',
  432. slot: 'deliveryMethod',
  433. action: 'deliveryMethod',
  434. align: 'center',
  435. minWidth: 140
  436. },
  437. {
  438. label: '要求到货时间',
  439. slot: 'requireDeliveryTime',
  440. action: 'requireDeliveryTime',
  441. align: 'center',
  442. minWidth: 140
  443. },
  444. {
  445. label: '图纸',
  446. slot: 'imgUrl',
  447. action: ' imgUrl',
  448. align: 'center',
  449. minWidth: 140
  450. },
  451. {
  452. label: '附件',
  453. slot: 'files',
  454. action: ' files',
  455. align: 'center',
  456. minWidth: 140
  457. },
  458. {
  459. columnKey: 'action',
  460. label: '操作',
  461. width: 70,
  462. align: 'center',
  463. resizable: false,
  464. slot: 'action',
  465. showOverflowTooltip: true
  466. }
  467. ],
  468. rules: {
  469. demandType: [
  470. {
  471. required: true,
  472. message: '请选择需求类型',
  473. trigger: ['blur', 'change']
  474. }
  475. ],
  476. name: [
  477. {
  478. required: true,
  479. message: '请输入配料计划名称',
  480. trigger: ['blur', 'change']
  481. }
  482. ]
  483. },
  484. formData: {
  485. name: '',
  486. remark: '',
  487. detailRemoveIds: [],
  488. materialRemoveIds: []
  489. },
  490. demandTypeList: []
  491. };
  492. },
  493. created() {
  494. },
  495. methods: {
  496. async open(type, row) {
  497. this.type = type;
  498. this.typeList();
  499. this.visible = true;
  500. if (row) {
  501. this.getDetail(row.id);
  502. } else {
  503. this.$nextTick(() => {
  504. this.$refs.table.setData([]);
  505. });
  506. }
  507. },
  508. typeList() {
  509. this.requestDict('需求类型').then((res) => {
  510. console.log(res, 999);
  511. this.demandTypeList = [];
  512. res.forEach((f) => {
  513. console.log(f.dictCode);
  514. if (f.dictCode == 1 || f.dictCode == 6 || f.dictCode == 7) {
  515. this.demandTypeList.push(f);
  516. }
  517. });
  518. this.$forceUpdate();
  519. });
  520. },
  521. getDetail(id) {
  522. getById(id).then((res) => {
  523. this.$set(this.formData, 'name', res.name);
  524. this.$set(this.formData, 'remark', res.remark);
  525. this.formData['id'] = res.id;
  526. this.$refs.table.setData([...res.salesOrderList]);
  527. this.$nextTick(() => {
  528. this.$refs.table.toggleRowExpansionAll();
  529. this.$forceUpdate();
  530. });
  531. });
  532. },
  533. confirm() {
  534. this.$refs.formRef.validate(async (value) => {
  535. if (value) {
  536. let _arr = this.$refs.table.getData() ?? [];
  537. if (_arr.length == 0) {
  538. this.$message.info('请添加销售订单');
  539. return false;
  540. }
  541. let _arr2 = [];
  542. _arr2 = _arr.map((m) => {
  543. if (
  544. Object.prototype.hasOwnProperty.call(m, 'salesOrderId') &&
  545. m.salesOrderId
  546. ) {
  547. } else {
  548. m.salesOrderId = m.id;
  549. m.salesOrderCode = m.code;
  550. delete m.id;
  551. delete m.code;
  552. }
  553. return {
  554. ...m
  555. };
  556. });
  557. this.formData['type'] = 1;
  558. this.formData['salesOrderList'] = _arr2;
  559. await save(this.formData);
  560. this.$message.success('保存成功!');
  561. this.$emit('success');
  562. this.cancel();
  563. }
  564. });
  565. },
  566. cancel() {
  567. this.visible = false;
  568. this.formData = {};
  569. this.formData.detailRemoveIds = [];
  570. this.formData.materialRemoveIds = [];
  571. this.$refs.table.setData([]);
  572. this.$refs.formRef.resetFields();
  573. },
  574. datasource({}) {
  575. return [];
  576. },
  577. reload() {
  578. this.$refs.table.reload();
  579. },
  580. saleAdd() {
  581. this.tableData = this.$refs.table.getData();
  582. this.$refs.saleOrderRef.open(this.tableData);
  583. },
  584. produceAdd() {},
  585. chooseOrder(list) {
  586. let salesOrderIds = [];
  587. list.map((m) => {
  588. salesOrderIds.push(m.id);
  589. return {
  590. ...m
  591. };
  592. });
  593. if (salesOrderIds.length > 0) {
  594. listBomBySalesOrderIdsOverride({ salesOrderIds: salesOrderIds }).then(
  595. (res) => {
  596. res.forEach((m) => {
  597. if (m.materialList.length > 0) {
  598. m.materialList.forEach((p) => {
  599. p.detailId = m.id;
  600. });
  601. }
  602. });
  603. this.$refs.table.setData([...this.tableData, ...res]);
  604. this.$nextTick(() => {
  605. this.$refs.table.toggleRowExpansionAll();
  606. this.$forceUpdate();
  607. });
  608. }
  609. );
  610. }
  611. },
  612. remove(row, index) {
  613. this.formData.detailRemoveIds.push(row.id);
  614. let _arr = this.$refs.table.getData() || [];
  615. _arr.splice(index, 1);
  616. this.$refs.table.setData([..._arr]);
  617. },
  618. categorySelect(row) {
  619. this.$refs.productRefs.open(row.materialList, row, this.type);
  620. },
  621. chooseModal(data, current) {
  622. data.map((m) => {
  623. m.detailId = current.id;
  624. return {
  625. ...m
  626. };
  627. });
  628. let tableList = [];
  629. tableList = this.$refs.table.getData();
  630. tableList.forEach((e) => {
  631. if (e.id == current.id) {
  632. if (e.materialList.length == 0) {
  633. e.materialList = data;
  634. } else {
  635. e.materialList = [...e.materialList, ...data];
  636. }
  637. }
  638. });
  639. console.log(tableList);
  640. this.$refs.table.setData([...tableList]);
  641. this.$forceUpdate();
  642. },
  643. remove2(row) {
  644. const data = this.$refs.table.getData() ?? [];
  645. this.formData.materialRemoveIds.push(row.categoryId);
  646. data.forEach((e) => {
  647. if (row.detailId == e.id) {
  648. e.materialList = e.materialList.filter(
  649. (d) => d.categoryId !== row.categoryId
  650. );
  651. }
  652. });
  653. this.$refs.table.setData([...data]);
  654. this.$forceUpdate();
  655. },
  656. openVersion(row) {
  657. this.xsId = row.id;
  658. this.$refs.versionRefs.open();
  659. },
  660. changeProduct(data) {
  661. let param = {
  662. salesOrderIds: [this.xsId],
  663. produceRoutingId: data.id
  664. };
  665. listBomBySalesOrderId(param).then((res) => {
  666. let tableList = [];
  667. tableList = this.$refs.table.getData();
  668. tableList.forEach((e) => {
  669. if (e.id == this.xsId) {
  670. res.map((m) => {
  671. m.detailId = this.xsId;
  672. return {
  673. ...m
  674. };
  675. });
  676. e.materialList = res;
  677. this.$nextTick(() => {
  678. this.$refs.table.setData([...tableList]);
  679. this.$refs.table.toggleRowExpansionAll();
  680. });
  681. }
  682. });
  683. });
  684. },
  685. handleMethod(row) {
  686. this.$refs.timeDialogRef.open(row);
  687. },
  688. chooseTime(current, timeList) {
  689. let tableList = [];
  690. tableList = this.$refs.table.getData();
  691. tableList.forEach((e) => {
  692. if (e.id == current.detailId) {
  693. e.materialList.forEach((m) => {
  694. if (m.categoryId == current.categoryId) {
  695. m.timeList = timeList || [];
  696. }
  697. });
  698. }
  699. });
  700. this.$refs.table.setData([...tableList]);
  701. this.$forceUpdate();
  702. }
  703. }
  704. };
  705. </script>
  706. <style lang="scss" scoped>
  707. :deep(.el-table__expanded-cell) {
  708. padding-bottom: 30px !important;
  709. border-bottom: 12px solid #ccffcc !important;
  710. }
  711. </style>