salesToProduction.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. <template>
  2. <div class="ele-body">
  3. <el-card shadow="never">
  4. <div class="body-title">
  5. <div class="title-left">销售订单转生产计划</div>
  6. <div class="title-right">
  7. <el-button @click="cancel">取消</el-button>
  8. <el-button type="primary" @click="toSubmit" :loading="loading">
  9. 提交计划
  10. </el-button>
  11. </div>
  12. </div>
  13. <el-form
  14. ref="form"
  15. :model="form"
  16. :rules="rules"
  17. label-width="90px"
  18. class="formbox"
  19. >
  20. <el-row :gutter="24">
  21. <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
  22. <el-form-item label="计划类型:">
  23. <DictSelection
  24. dictName="订单计划类型"
  25. clearable
  26. v-model="form.planType"
  27. disabled
  28. >
  29. </DictSelection>
  30. </el-form-item>
  31. </el-col>
  32. <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
  33. <el-form-item label="生产版本:" prop="produceVersionName">
  34. <el-input
  35. @click.native="openVersion"
  36. placeholder="请选择生产版本"
  37. readonly
  38. v-model="form.produceVersionName"
  39. ></el-input>
  40. </el-form-item>
  41. </el-col>
  42. <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
  43. <el-form-item label="产线:">
  44. <!-- <el-input
  45. v-model="form.productLine"
  46. placeholder="选择生产版本带入"
  47. readonly
  48. ></el-input> -->
  49. <el-select
  50. placeholder="选择生产版本带入"
  51. v-model="form.productLine"
  52. >
  53. <el-option
  54. v-for="(item, index) in lineNamesList"
  55. :key="index"
  56. :label="item"
  57. :value="item"
  58. ></el-option>
  59. </el-select>
  60. </el-form-item>
  61. </el-col>
  62. <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
  63. <el-form-item label="使用改型:" prop="modification">
  64. <el-checkbox
  65. v-model="form.modification"
  66. :true-label="1"
  67. :false-label="0"
  68. ></el-checkbox>
  69. </el-form-item>
  70. </el-col>
  71. </el-row>
  72. <el-row :gutter="24" class="row-intro">
  73. <el-col v-bind="styleResponsive ? { lg: 3, md: 12 } : { span: 3 }">
  74. <el-form-item label="销售单数量:">{{ form.codeNum }}</el-form-item>
  75. </el-col>
  76. <el-col v-bind="styleResponsive ? { lg: 3, md: 12 } : { span: 3 }">
  77. <el-form-item label="订单总数量:">{{
  78. form.contractNum
  79. }}</el-form-item>
  80. </el-col>
  81. <el-col v-bind="styleResponsive ? { lg: 4, md: 12 } : { span: 4 }">
  82. <el-form-item label="订单总重量:">{{
  83. form.sumOrderWeight
  84. }}</el-form-item>
  85. </el-col>
  86. <el-col v-bind="styleResponsive ? { lg: 5, md: 12 } : { span: 5 }">
  87. <el-form-item label="物料编码:">{{
  88. form.productCode
  89. }}</el-form-item>
  90. </el-col>
  91. <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
  92. <el-form-item label="牌号|型号:"
  93. >{{ form.brandNo }}|{{ form.model }}</el-form-item
  94. >
  95. </el-col>
  96. <el-col v-bind="styleResponsive ? { lg: 3, md: 12 } : { span: 3 }">
  97. <el-form-item label="库存数量:">{{
  98. form.stockCountBase
  99. }}</el-form-item>
  100. </el-col>
  101. </el-row>
  102. <el-table :data="form.salesOrders" border>
  103. <el-table-column label="序号" align="center" width="60">
  104. <template slot-scope="scope">
  105. <span>{{ scope.$index + 1 }}</span>
  106. </template>
  107. </el-table-column>
  108. <el-table-column
  109. label="销售订单号"
  110. align="center"
  111. prop="code"
  112. width="120"
  113. >
  114. </el-table-column>
  115. <el-table-column label="行号" align="center" prop="lineNumber">
  116. </el-table-column>
  117. <el-table-column label="合同数量" align="center" prop="contractNum">
  118. </el-table-column>
  119. <el-table-column
  120. label="合同重量"
  121. align="center"
  122. prop="productSumWeight"
  123. >
  124. </el-table-column>
  125. <el-table-column label="欠交数量" align="center" prop="lackNum">
  126. </el-table-column>
  127. <el-table-column
  128. label="计划生产数量"
  129. align="center"
  130. prop="planProductNum"
  131. width="120"
  132. >
  133. <template slot-scope="scope">
  134. <el-form-item
  135. label-width="0px"
  136. :prop="'salesOrders.' + scope.$index + '.planProductNum'"
  137. :rules="{
  138. required: true,
  139. message: '请输入计划生产数量',
  140. trigger: 'blur'
  141. }"
  142. class="table-item"
  143. >
  144. <el-input
  145. v-model.number="scope.row.planProductNum"
  146. size="small"
  147. oninput="value=value.replace(/[^\d]/g,'')"
  148. style="width: 100%"
  149. placeholder="输入数量"
  150. ></el-input>
  151. </el-form-item>
  152. </template>
  153. </el-table-column>
  154. <el-table-column
  155. label="要求成型数量"
  156. align="center"
  157. prop="requiredFormingNum"
  158. width="120"
  159. >
  160. <template slot-scope="scope">
  161. <el-form-item
  162. label-width="0px"
  163. :prop="'salesOrders.' + scope.$index + '.requiredFormingNum'"
  164. :rules="{
  165. required: true,
  166. message: '请输入要求成型数量',
  167. trigger: 'blur'
  168. }"
  169. class="table-item"
  170. >
  171. <el-input
  172. v-model.number="scope.row.requiredFormingNum"
  173. size="small"
  174. oninput="value=value.replace(/[^\d]/g,'')"
  175. style="width: 100%"
  176. placeholder="输入数量"
  177. ></el-input>
  178. </el-form-item>
  179. </template>
  180. </el-table-column>
  181. <el-table-column
  182. label="按单按库"
  183. align="center"
  184. prop="orderLibraryType"
  185. >
  186. <template slot-scope="{ row }">
  187. {{ getDictValue('按单按库', row.orderLibraryType) }}
  188. </template>
  189. </el-table-column>
  190. <el-table-column label="订单类型" align="center" prop="orderType">
  191. <template slot-scope="{ row }">
  192. {{ getDictValue('订单类型', row.orderType) }}
  193. </template>
  194. </el-table-column>
  195. <el-table-column
  196. label="交付日期"
  197. align="center"
  198. prop="deliveryTime"
  199. width="160"
  200. >
  201. </el-table-column>
  202. <el-table-column
  203. label="要求成型日期"
  204. align="center"
  205. prop="reqMoldTime"
  206. width="180"
  207. >
  208. <template slot-scope="scope">
  209. <el-form-item
  210. label-width="0px"
  211. :prop="'salesOrders.' + scope.$index + '.reqMoldTime'"
  212. :rules="{
  213. required: true,
  214. message: '请选择要求成型日期',
  215. trigger: 'blur'
  216. }"
  217. class="table-item"
  218. >
  219. <el-date-picker
  220. style="width: 100%"
  221. v-model="scope.row.reqMoldTime"
  222. :pickerOptions="{
  223. disabledDate: (time) =>
  224. time.getTime() <
  225. new Date(new Date().setHours(0, 0, 0, 0)).getTime()
  226. }"
  227. type="date"
  228. placeholder="选择日期"
  229. value-format="yyyy-MM-dd"
  230. >
  231. </el-date-picker>
  232. <!-- <el-date-picker
  233. style="width: 100%;"
  234. v-model="scope.row.reqMoldTime"
  235. type="datetime"
  236. placeholder="选择日期"
  237. value-format="yyyy-MM-dd HH:mm:ss">
  238. </el-date-picker> -->
  239. </el-form-item>
  240. </template>
  241. </el-table-column>
  242. <el-table-column label="客户名称" align="center" prop="customerName">
  243. </el-table-column>
  244. <el-table-column label="业务员" align="center" prop="salesman">
  245. </el-table-column>
  246. <el-table-column
  247. label="交付要求"
  248. align="center"
  249. prop="deliveryRequirements"
  250. >
  251. <template slot-scope="{ row }">
  252. {{ getDictValue('交付要求', row.deliveryRequirements) }}
  253. </template>
  254. </el-table-column>
  255. <el-table-column
  256. prop="priority"
  257. label="优先级"
  258. width="100"
  259. align="center"
  260. >
  261. <template slot-scope="{ row }"
  262. >{{ row.priority }}
  263. <div class="sort-wrap">
  264. <i class="el-icon-caret-top" @click="sortTop(row)"></i>
  265. <i class="el-icon-caret-bottom" @click="sortBottom(row)"></i>
  266. </div>
  267. </template>
  268. </el-table-column>
  269. <el-table-column label="操作" align="center" width="70" fixed="right">
  270. <template slot-scope="scope">
  271. <el-button type="text" @click="handleDeleteItem(scope.$index)"
  272. >删除</el-button
  273. >
  274. </template>
  275. </el-table-column>
  276. </el-table>
  277. <div class="add-product" @click="addEquipment">
  278. <i class="el-icon-circle-plus-outline"></i>
  279. </div>
  280. <el-row :gutter="24">
  281. <el-col v-bind="styleResponsive ? { lg: 24, md: 24 } : { span: 24 }">
  282. <el-form-item label="计划备注:">
  283. <el-input
  284. type="textarea"
  285. :rows="4"
  286. v-model="form.notes"
  287. resize="none"
  288. ></el-input>
  289. </el-form-item>
  290. </el-col>
  291. </el-row>
  292. </el-form>
  293. <AdditionalOrder
  294. ref="additionalRefs"
  295. :productCode="form.productCode"
  296. :selectList="form.salesOrders"
  297. @choose="confirmChoose"
  298. ></AdditionalOrder>
  299. <ProductionVersion
  300. ref="versionRefs"
  301. @confirm="confirmVersion"
  302. :productCode="form.productCode"
  303. :produceVersionId="form.produceVersionId"
  304. ></ProductionVersion>
  305. <PlanSubmit
  306. ref="submitRefs"
  307. :type="$route.query.type"
  308. :info="form"
  309. @publish="publishData"
  310. ></PlanSubmit>
  311. </el-card>
  312. </div>
  313. </template>
  314. <script>
  315. import AdditionalOrder from './components/AdditionalOrder.vue';
  316. import PlanSubmit from './components/plan-submit.vue';
  317. import ProductionVersion from '@/components/CreatePlan/ProductionVersion.vue';
  318. import {
  319. productionToPlan,
  320. saveSaleToPlan,
  321. updateSaleToPlan,
  322. releaseSave,
  323. getUpdateInfoById,
  324. getProductVersion
  325. } from '@/api/saleOrder';
  326. import dictMixins from '@/mixins/dictMixins';
  327. import { deepClone } from '@/utils/index';
  328. import { getRouteTabKey, removePageTab } from '@/utils/page-tab-util';
  329. import { getCode } from '@/api/codeManagement';
  330. import { release } from '@/api/productionPlan/order.js';
  331. import dayjs from 'dayjs';
  332. export default {
  333. mixins: [dictMixins],
  334. components: {
  335. AdditionalOrder,
  336. ProductionVersion,
  337. PlanSubmit
  338. },
  339. data() {
  340. return {
  341. type: this.$route.query.type,
  342. form: {
  343. planType: 1,
  344. produceVersionId: '',
  345. productLine: '',
  346. salesOrders: [],
  347. produceVersionName: ''
  348. },
  349. lineNamesList: [],
  350. // 表单验证规则
  351. rules: {
  352. produceVersionName: [
  353. { required: true, message: '请选择生产版本', trigger: 'change' }
  354. ]
  355. },
  356. // selection: [],
  357. loading: false
  358. };
  359. },
  360. computed: {
  361. // 是否开启响应式布局
  362. styleResponsive() {
  363. return this.$store.state.theme.styleResponsive;
  364. }
  365. },
  366. created() {
  367. this.requestDict('按单按库');
  368. this.requestDict('订单类型');
  369. this.requestDict('交付要求');
  370. if (this.type == 'edit') {
  371. this.getPlanInfo(this.$route.query.id);
  372. } else {
  373. const list = JSON.parse(this.$route.query.selection);
  374. this.getSaleInfo(list);
  375. }
  376. },
  377. methods: {
  378. async getPlanInfo(id) {
  379. const data = await getUpdateInfoById(id);
  380. this.form = data;
  381. if (data.productCode) {
  382. this.getVersionData();
  383. }
  384. },
  385. getSaleInfo(params) {
  386. productionToPlan(params).then((res) => {
  387. this.form = deepClone(res);
  388. this.form.salesOrders.map((item, index) => {
  389. item.priority = index + 1;
  390. item.planProductNum = item.lackNum;
  391. item.requiredFormingNum = item.lackNum;
  392. item.reqMoldTime = dayjs(
  393. new Date(item.deliveryTime).getTime() + 3600 * 1000 * 24 * 10
  394. ).format('YYYY-MM-DD');
  395. });
  396. if (this.form.salesOrders.every((itm) => itm.orderType == 2)) {
  397. this.form.planType = 2;
  398. } else if (this.form.salesOrders.every((itm) => itm.orderType == 1)) {
  399. this.form.planType = 1;
  400. } else {
  401. this.form.planType = 3;
  402. }
  403. });
  404. },
  405. getVersionData() {
  406. getProductVersion(this.form.productCode).then((res) => {
  407. res.map((item) => {
  408. if (item.produceVersionId == this.form.produceVersionId) {
  409. this.lineNamesList = Array.from(new Set(item.lineNames || []));
  410. }
  411. });
  412. });
  413. },
  414. cancel() {
  415. const key = getRouteTabKey();
  416. this.$router.go(-1);
  417. removePageTab({ key });
  418. },
  419. toSubmit() {
  420. this.$refs.form.validate((valid) => {
  421. if (valid) {
  422. this.mapList();
  423. this.$refs.submitRefs.open();
  424. }
  425. });
  426. },
  427. // 对比日期,计算要求成型重量
  428. mapList() {
  429. var requiredFormingWeight = 0;
  430. var requiredFormingNum = 0;
  431. var productNum = 0;
  432. this.form.salesOrders.map((item, index) => {
  433. requiredFormingWeight =
  434. requiredFormingWeight +
  435. Number(item.productUnitWeight) * Number(item.requiredFormingNum);
  436. requiredFormingNum = requiredFormingNum + item.requiredFormingNum;
  437. productNum += Number(item.planProductNum);
  438. });
  439. this.form.productNum = productNum;
  440. this.form.productUnitWeight =
  441. this.form.salesOrders[0]?.productUnitWeight;
  442. this.form.requiredFormingWeight = requiredFormingWeight.toFixed(2);
  443. this.form.requiredFormingNum = requiredFormingNum;
  444. const collection = deepClone(this.form.salesOrders);
  445. const sortedCollection = collection.sort(
  446. (a, b) => new Date(b.reqMoldTime) - new Date(a.reqMoldTime)
  447. );
  448. let latestData = {};
  449. for (let i = 0; i < sortedCollection.length; i++) {
  450. const data = sortedCollection[i];
  451. if (
  452. !latestData.reqMoldTime ||
  453. new Date(data.reqMoldTime) >= new Date(latestData.reqMoldTime)
  454. ) {
  455. latestData = data;
  456. }
  457. }
  458. this.form.reqMoldTime = latestData.reqMoldTime;
  459. },
  460. sortTop(row) {
  461. if (row.priority <= 1) {
  462. return;
  463. }
  464. let rowN = this.form.salesOrders.find(
  465. (n) => n.priority == row.priority - 1
  466. );
  467. rowN.priority += 1;
  468. row.priority -= 1;
  469. this.form.salesOrders.sort((a, b) => {
  470. return a.priority - b.priority;
  471. });
  472. },
  473. sortBottom(row) {
  474. if (row.priority >= this.form.salesOrders.length) {
  475. return;
  476. }
  477. let rowN = this.form.salesOrders.find(
  478. (n) => n.priority == row.priority + 1
  479. );
  480. rowN.priority -= 1;
  481. row.priority += 1;
  482. this.form.salesOrders.sort((a, b) => {
  483. return a.priority - b.priority;
  484. });
  485. },
  486. againSort() {
  487. this.form.salesOrders.forEach((n, index) => {
  488. n.priority = index + 1;
  489. });
  490. },
  491. // 删除产品
  492. handleDeleteItem(index) {
  493. this.form.salesOrders.splice(index, 1);
  494. },
  495. addEquipment() {
  496. this.$refs.additionalRefs.open(this.form.planType);
  497. },
  498. openVersion() {
  499. this.$refs.versionRefs.open();
  500. },
  501. confirmVersion(data) {
  502. this.$set(this.form, 'produceVersionName', data.produceVersionName);
  503. this.$set(this.form, 'produceVersionId', data.produceVersionId);
  504. this.$set(
  505. this.form,
  506. 'productLine',
  507. data.lineNames.length ? data.lineNames[0] : ''
  508. );
  509. this.lineNamesList = Array.from(new Set(data.lineNames || []));
  510. },
  511. confirmChoose(list) {
  512. // 取出在弹窗中选中并且不在表格中的数据
  513. const result = list.filter(
  514. (i) => this.form.salesOrders.findIndex((p) => p.id === i.id) === -1
  515. );
  516. // 取出在表格中并且不在弹窗中选中的数据 即取消选中的数据
  517. const del = this.form.salesOrders.filter(
  518. (i) => list.findIndex((p) => p.id === i.id) === -1
  519. );
  520. for (let i = this.form.salesOrders.length - 1; i >= 0; i--) {
  521. for (let j in del) {
  522. if (this.form.salesOrders[i].id === del[j].id) {
  523. this.form.salesOrders.splice(i, 1);
  524. }
  525. }
  526. }
  527. let priority =
  528. this.form.salesOrders[this.form.salesOrders.length - 1]?.priority ||
  529. 0;
  530. this.form.salesOrders = this.form.salesOrders.concat(
  531. result.map((item, index) => {
  532. item.priority = ++priority;
  533. item.planProductNum = item.lackNum;
  534. item.requiredFormingNum = item.lackNum;
  535. item.reqMoldTime = dayjs(
  536. new Date(item.deliveryTime).getTime() + 3600 * 1000 * 24 * 10
  537. ).format('YYYY-MM-DD');
  538. return item;
  539. })
  540. );
  541. this.changeData();
  542. },
  543. changeData() {
  544. var planProductNum = 0;
  545. var productWeight = 0;
  546. this.form.salesOrders.map((item, index) => {
  547. item.priority = index + 1;
  548. planProductNum = planProductNum + item.contractNum;
  549. productWeight = productWeight + Number(item.productSumWeight);
  550. });
  551. this.$set(this.form, 'codeNum', this.form.salesOrders.length);
  552. this.$set(this.form, 'contractNum', planProductNum);
  553. this.$set(this.form, 'sumOrderWeight', productWeight.toFixed(2));
  554. },
  555. async publishData(type) {
  556. let params = deepClone(this.form);
  557. params.categoryId = params.salesOrders[0]?.categoryId;
  558. if (this.$route.query.type != 'edit') {
  559. delete params.id;
  560. }
  561. if (type === 2) {
  562. this.$confirm('发布工单后不可撤回,确定发布吗?', '发布确认').then(
  563. async () => {
  564. const loading = this.$loading({
  565. lock: true,
  566. fullscreen: true,
  567. text: '工单发布中...'
  568. });
  569. try {
  570. const code = await getCode('product_order_code');
  571. const data = {
  572. productionPlan: params,
  573. workOrder: {
  574. productionPlanCode: params.code,
  575. code: code,
  576. formingNum: params.contractNum,
  577. formingWeight: params.sumOrderWeight,
  578. produceVersionId: params.produceVersionId,
  579. status: 4,
  580. model: params.model,
  581. brandNo: params.brandNo,
  582. categoryId: params.categoryId,
  583. productCode: params.productCode,
  584. productName: params.productName
  585. }
  586. };
  587. if (this.$route.query.type == 'edit') {
  588. data.workOrder.productionPlanId = params.id;
  589. }
  590. const key = getRouteTabKey();
  591. releaseSave(data)
  592. .then(() => {
  593. this.$message.success('发布成功!');
  594. this.$router.push({
  595. path: '/productionPlan'
  596. });
  597. removePageTab({ key });
  598. })
  599. .catch(() => {
  600. this.$message.error('发布失败,请重新发布!');
  601. });
  602. } catch (error) {}
  603. loading.close();
  604. }
  605. );
  606. } else {
  607. let request =
  608. this.$route.query.type == 'edit'
  609. ? updateSaleToPlan
  610. : saveSaleToPlan;
  611. request(params)
  612. .then(async (res) => {
  613. reloadPageTab({ fullPath: '/saleOrder' });
  614. // 提交
  615. this.$router.push({
  616. path: '/productionPlan'
  617. });
  618. removePageTab({ key });
  619. })
  620. .catch(() => {
  621. this.$message.error('提交失败,请重新提交!');
  622. });
  623. }
  624. }
  625. }
  626. };
  627. </script>
  628. <style lang="scss" scoped>
  629. .ele-body {
  630. background: #fff;
  631. }
  632. .body-title {
  633. width: 100%;
  634. display: flex;
  635. align-items: center;
  636. justify-content: space-between;
  637. }
  638. .title-left {
  639. font-size: 20px;
  640. color: #333;
  641. }
  642. .formbox {
  643. margin: 20px auto;
  644. }
  645. .row-intro {
  646. border-bottom: 1px dashed #ccc;
  647. margin-bottom: 20px;
  648. }
  649. .sort-wrap {
  650. i {
  651. font-size: 30px;
  652. cursor: pointer;
  653. }
  654. .el-icon-caret-top {
  655. color: red;
  656. }
  657. .el-icon-caret-bottom {
  658. color: #157a2c;
  659. }
  660. }
  661. .add-product {
  662. width: 100%;
  663. display: flex;
  664. align-items: center;
  665. justify-content: flex-end;
  666. font-size: 30px;
  667. color: #1890ff;
  668. margin: 10px 0;
  669. cursor: pointer;
  670. }
  671. .table-item {
  672. margin-bottom: 0;
  673. }
  674. </style>