producePlan.vue 23 KB

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