index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. <template>
  2. <div class="ele-body">
  3. <el-card shadow="never" v-loading="loading">
  4. <productionPlan-search @search="reload" ref="searchRef" :statusOpt="statusOpt" :planType="planType"
  5. :activeName="activeName">
  6. </productionPlan-search>
  7. <div class="btn_box">
  8. <el-button type="success" size="mini">齐套性检查</el-button>
  9. <el-button type="primary" size="mini">计划分解</el-button>
  10. <el-button type="primary" size="mini">补单计划</el-button>
  11. <el-button type="info" size="mini">计划行事历</el-button>
  12. <el-button type="warning" size="mini">预警设置</el-button>
  13. <el-button type="primary" size="mini" @click="handleMerge">合批</el-button>
  14. <el-button type="danger" size="mini">延期申请</el-button>
  15. <el-button type="danger" size="mini">变更申请</el-button>
  16. <el-button type="success" size="mini" v-if="timeDimensionPlanType == 3">新增</el-button>
  17. </div>
  18. <el-tabs v-model="activeName" type="card" size="mini">
  19. <el-tab-pane label="未发布" name="first"></el-tab-pane>
  20. <el-tab-pane label="已发布" name="second"></el-tab-pane>
  21. <el-tab-pane label="已变更" name="change"></el-tab-pane>
  22. </el-tabs>
  23. <!-- 数据表格 -->
  24. <ele-pro-table ref="table" :key="activeName" :initLoad="false" :columns="newColumns" :datasource="datasource"
  25. row-key="code" :selection.sync="selection" :cache-key="`${activeName}ProductionPlanTable`"
  26. @sort-change="onSortChange" autoAmendPage :parse-data="parseData" @update:selection="handleSelectionChange">
  27. <template v-slot:batchNo="{ row }">
  28. <el-link type="primary" :underline="false">
  29. <el-tag type="success" size="mini" v-if="row.joinPlanCode" @click.stop="splitDetails(1, row)">
  30. 拆</el-tag>
  31. <el-tag type="danger" size="mini" v-if="row.splitBatch == 2" @click.stop="splitDetails(2, row)">
  32. 合</el-tag>
  33. {{ row.batchNo }}
  34. </el-link>
  35. </template>
  36. <template v-slot:selection="{ row }">
  37. <div class="check_box" @click="handOneSelection(row)">
  38. <div class="check act_check" v-if="selectionFilter(row)">
  39. <i class="el-icon-check"></i>
  40. </div>
  41. <div class="check" v-else></div>
  42. </div>
  43. </template>
  44. <template v-slot:code="{ row }">
  45. <el-link type="primary" :underline="false" @click="goDetail(row)">
  46. {{ row.code }}
  47. </el-link>
  48. </template>
  49. <template v-slot:salesCode="{ row }">
  50. {{ row.salesCode }}
  51. </template>
  52. <template v-slot:priority="{ row }">
  53. <div style="display: flex">
  54. <el-input v-model="row.priority" type="number" size="mini" :min="0" :max="10" @change="priorityChange(row)"
  55. style="width: 80px">
  56. </el-input>
  57. <el-popover placement="right" width="200" trigger="hover" content="数值越大优先级越高(0-3普通, 4-6优先, 7-10紧急)">
  58. <div class="sort-wrap" slot="reference">
  59. <i class="el-icon-caret-top" @click="sortTop(row)"></i>
  60. <i class="el-icon-caret-bottom" @click="sortBottom(row)"></i>
  61. </div>
  62. </el-popover>
  63. </div>
  64. </template>
  65. <template v-slot:productNum="{ row }">
  66. {{ row.productNum }} {{ row.unit }}
  67. </template>
  68. <template v-slot:productWeight="{ row }">
  69. {{ row.productWeight }} {{ row.weightUnit }}
  70. </template>
  71. <template v-slot:requiredFormingNum="{ row }">
  72. {{ row.requiredFormingNum }} {{ row.unit }}
  73. </template>
  74. <template v-slot:newSumOrderWeight="{ row }">
  75. {{ row.newSumOrderWeight }} {{ row.newWeightUnit }}
  76. </template>
  77. <template v-slot:status="{ row }">
  78. <span :class="{ 'ele-text-danger': row.status == 3 }">
  79. {{ statusFormatter(row.status) }}
  80. </span>
  81. </template>
  82. <!-- 操作列 -->
  83. <template v-slot:action="{ row }">
  84. <el-link type="primary" :underline="false" v-if="row.status == 2" @click="handleOrderPublish(1, row)">
  85. 发布
  86. </el-link>
  87. <el-link type="primary" v-if="row.status == 3" :underline="false" @click="handleOrderPublish(2, row)">
  88. 重新发布
  89. </el-link>
  90. <el-link v-if="
  91. row.splitBatch != 2 && !row.joinPlanCode && activeName == 'first'
  92. " type="primary" :underline="false" @click="planEdit(row)">
  93. 修改
  94. </el-link>
  95. <el-link v-if="
  96. clientEnvironmentId != 4 &&
  97. activeName == 'first' &&
  98. row.splitBatch != 2 &&
  99. !row.joinPlanCode
  100. " type="primary" :underline="false" @click="toUnpack(row)">
  101. 拆批
  102. </el-link>
  103. </template>
  104. </ele-pro-table>
  105. </el-card>
  106. <unpackDialog ref="unpackRef" @success="reload" />
  107. <mergeDialog ref="mergeRef" @success="reload"></mergeDialog>
  108. <unpackDetails ref="DetailsRef"></unpackDetails>
  109. </div>
  110. </template>
  111. <script>
  112. import { getList, del, updatePriority } from '@/api/productionPlan/index.js';
  113. import productionPlanSearch from './components/productionPlan-search.vue';
  114. import unpackDialog from './components/unpackDialog.vue';
  115. import mergeDialog from './components/mergeDialog.vue';
  116. import unpackDetails from './components/unpackDetails.vue';
  117. import { release } from '@/api/productionPlan/order.js';
  118. import { getCode } from '@/api/codeManagement';
  119. import { fieldModel } from '@/api/saleOrder';
  120. import { debounce } from 'lodash';
  121. export default {
  122. components: {
  123. productionPlanSearch,
  124. unpackDialog,
  125. mergeDialog,
  126. unpackDetails
  127. },
  128. props: {
  129. timeDimensionPlanType: { type: Number, default: 1 }
  130. },
  131. data() {
  132. return {
  133. activeName: 'first',
  134. // 加载状态
  135. loading: false,
  136. pageType: 'add',
  137. dialogTitle: '',
  138. isBindPlan: false,
  139. statusOpt: {
  140. first: [
  141. { label: '所有状态', value: '3,2' },
  142. { label: '待发布', value: '2' },
  143. { label: '发布失败', value: '3' }
  144. ],
  145. second: [
  146. { label: '所有状态', value: '7,4,5,6' },
  147. { label: '待生产', value: '4' },
  148. { label: '生产中', value: '5' },
  149. { label: '已完成', value: '6' },
  150. { label: '已延期', value: '7' }
  151. ],
  152. change: [{ label: '已变更', value: '9' }]
  153. },
  154. planType: [
  155. { label: '所有计划类型', value: null },
  156. { label: '内销计划', value: '1' },
  157. { label: '外销计划', value: '2' },
  158. { label: '预制计划', value: '3' }
  159. ],
  160. newColumns: [],
  161. selection: [],
  162. };
  163. },
  164. computed: {
  165. clientEnvironmentId() {
  166. return this.$store.state.user.info.clientEnvironmentId;
  167. },
  168. // 表格列配置
  169. columns() {
  170. const opt = {
  171. first: [
  172. ],
  173. second: [
  174. {
  175. prop: 'releaseTime',
  176. label: '工单发布日期',
  177. align: 'center',
  178. minWidth: 110
  179. },
  180. {
  181. prop: 'planFormingTime',
  182. label: '预测生产日期',
  183. align: 'center',
  184. minWidth: 110
  185. },
  186. {
  187. prop: 'deliveryTime',
  188. label: '预测交货日期',
  189. align: 'center',
  190. minWidth: 110
  191. },
  192. {
  193. prop: 'formingTime',
  194. label: '实际交货日期',
  195. align: 'center',
  196. minWidth: 110
  197. }
  198. ],
  199. change: []
  200. };
  201. return [
  202. {
  203. width: 45,
  204. type: 'selection',
  205. columnKey: 'selection',
  206. align: 'center',
  207. slot: 'selection'
  208. },
  209. {
  210. slot: 'batchNo',
  211. prop: 'batchNo',
  212. label: '批次号',
  213. align: 'center',
  214. minWidth: 140
  215. },
  216. {
  217. slot: 'code',
  218. prop: 'code',
  219. action: 'code',
  220. label: '计划编号',
  221. align: 'center',
  222. minWidth: 160,
  223. sortable: true
  224. },
  225. {
  226. prop: 'salesCode',
  227. action: 'salesCode',
  228. label: '销售订单号',
  229. align: 'center',
  230. minWidth: 160
  231. },
  232. {
  233. prop: 'productCode',
  234. label: '产品编码',
  235. align: 'center',
  236. minWidth: 140
  237. },
  238. {
  239. prop: 'productName',
  240. label: '产品名称',
  241. align: 'center',
  242. minWidth: 140
  243. },
  244. {
  245. prop: 'brandNo',
  246. label: '牌号',
  247. align: 'center',
  248. showOverflowTooltip: true
  249. },
  250. {
  251. prop: 'specification',
  252. label: '规格',
  253. align: 'center',
  254. minWidth: 150,
  255. showOverflowTooltip: true
  256. },
  257. {
  258. prop: 'model',
  259. label: '型号',
  260. align: 'center',
  261. showOverflowTooltip: true
  262. },
  263. {
  264. prop: 'priority',
  265. label: '优先级',
  266. align: 'center',
  267. minWidth: 120,
  268. slot: 'priority',
  269. sortable: 'custom'
  270. },
  271. {
  272. prop: 'produceRoutingName',
  273. label: '工艺路线',
  274. align: 'center',
  275. minWidth: 120
  276. },
  277. {
  278. prop: 'productNum',
  279. label: '计划数量',
  280. align: 'center',
  281. slot: 'productNum'
  282. },
  283. {
  284. prop: 'productWeight',
  285. label: '计划重量',
  286. align: 'center',
  287. slot: 'productWeight'
  288. },
  289. {
  290. prop: 'requiredFormingNum',
  291. label: '要求生产数量',
  292. align: 'center',
  293. slot: 'requiredFormingNum'
  294. },
  295. {
  296. prop: 'newSumOrderWeight',
  297. label: '要求生产重量',
  298. align: 'center',
  299. slot: 'newSumOrderWeight'
  300. },
  301. {
  302. prop: 'scheduleStatusName',
  303. label: '进度状态',
  304. align: 'center',
  305. minWidth: 100
  306. },
  307. {
  308. prop: '',
  309. label: '已排产数量',
  310. align: 'center',
  311. showOverflowTooltip: true
  312. },
  313. {
  314. prop: '',
  315. label: '未排产数量',
  316. align: 'center',
  317. showOverflowTooltip: true
  318. },
  319. {
  320. prop: '',
  321. label: '已生产数量',
  322. align: 'center',
  323. showOverflowTooltip: true
  324. },
  325. {
  326. prop: '',
  327. label: '未生产数量',
  328. align: 'center',
  329. showOverflowTooltip: true
  330. },
  331. {
  332. prop: 'moCount',
  333. label: '模数',
  334. align: 'center',
  335. show: this.clientEnvironmentId == '4'
  336. },
  337. {
  338. prop: 'blockCount',
  339. label: '块数',
  340. align: 'center',
  341. show: this.clientEnvironmentId == '4'
  342. },
  343. {
  344. prop: 'noWordCount',
  345. label: '未排程块数',
  346. align: 'center',
  347. show: this.clientEnvironmentId == '4',
  348. minWidth: 110
  349. },
  350. {
  351. prop: 'reqMoldTime',
  352. label: '计划完成日期',
  353. align: 'center',
  354. minWidth: 110
  355. },
  356. ...opt[this.activeName],
  357. {
  358. prop: 'orderType',
  359. label: '计划类型',
  360. align: 'center',
  361. formatter: (row) => {
  362. const obj = this.planType.find((i) => i.value == row.planType);
  363. return obj && obj.label;
  364. }
  365. },
  366. {
  367. prop: 'version',
  368. label: '版本',
  369. align: 'center',
  370. minWidth: 80
  371. },
  372. {
  373. prop: 'createTime',
  374. label: '创建时间',
  375. align: 'center',
  376. minWidth: 110
  377. },
  378. {
  379. columnKey: 'status',
  380. slot: 'status',
  381. label: '状态',
  382. align: 'center',
  383. formatter: (row) => {
  384. const obj = this.statusOpt[this.activeName].find(
  385. (i) => i.value == row.status
  386. );
  387. return obj && obj.label;
  388. }
  389. }
  390. ];
  391. }
  392. },
  393. created() {
  394. this.getFieldModel();
  395. },
  396. methods: {
  397. statusFormatter(status) {
  398. const obj = this.statusOpt[this.activeName].find(
  399. (i) => i.value == status
  400. );
  401. return obj && obj.label;
  402. },
  403. /* 表格数据源 */
  404. datasource({ page, limit, where }) {
  405. return getList({
  406. pageNum: page,
  407. timeDimensionPlanType: this.timeDimensionPlanType,
  408. size: limit,
  409. ...where,
  410. ...this.sort
  411. });
  412. },
  413. // 发布工单
  414. handleOrderPublish(type, row) {
  415. if (!row.produceRoutingName) {
  416. return this.$message.error('请先选择工艺路线!');
  417. }
  418. this.$confirm('发布工单后不可撤回,确定发布吗?', '发布确认')
  419. .then(async () => {
  420. const loading = this.$loading({
  421. lock: true,
  422. fullscreen: true,
  423. text: '工单发布中...'
  424. });
  425. try {
  426. let code = row.workOrderCode;
  427. if (!code) {
  428. code = await getCode('product_order_code');
  429. }
  430. // 反显对象会报错 status
  431. const data = await release([row.id]);
  432. if (data || data === 0) {
  433. this.$message.success('发布成功!');
  434. } else {
  435. this.$message.error('发布失败,请重新发布!');
  436. }
  437. this.reload();
  438. } catch (error) {
  439. console.error(error);
  440. }
  441. loading.close();
  442. })
  443. .catch((err) => {
  444. console.error(err);
  445. });
  446. // this.$router.push({
  447. // path: '/productionPlan/workOrderPublish',
  448. // query: {
  449. // type,
  450. // id: row.id
  451. // }
  452. // });
  453. },
  454. // 修改计划
  455. planEdit({ id }) {
  456. this.$router.push({
  457. path: '/saleOrder/salesToProduction',
  458. query: {
  459. type: 'edit',
  460. id
  461. }
  462. });
  463. },
  464. getFieldModel() {
  465. fieldModel({ fieldModel: 't_main_category' }).then((res) => {
  466. const privateColumn = [];
  467. if (this.activeName == 'first') {
  468. privateColumn.push({
  469. columnKey: 'action',
  470. label: '操作',
  471. width: 148,
  472. align: 'center',
  473. resizable: false,
  474. fixed: 'right',
  475. slot: 'action'
  476. });
  477. } else {
  478. privateColumn = [];
  479. }
  480. let newRes = res.map((m) => {
  481. return {
  482. prop: 'extField.' + m.prop,
  483. label: m.label,
  484. align: 'center',
  485. showOverflowTooltip: true
  486. };
  487. });
  488. this.newColumns = [...this.columns, ...newRes, ...privateColumn];
  489. this.$forceUpdate();
  490. });
  491. },
  492. handleTabChange() {
  493. this.$refs.searchRef.reset();
  494. },
  495. /* 刷新表格 */
  496. reload(where) {
  497. this.$nextTick(() => {
  498. this.$refs.table.reload({ page: 1, where });
  499. });
  500. },
  501. /* 数据转为树形结构 */
  502. parseData(data) {
  503. return {
  504. ...data,
  505. list: this.$util.toTreeData({
  506. data: data.list,
  507. count: data.total,
  508. idField: 'code',
  509. parentIdField: 'joinPlanCode'
  510. })
  511. };
  512. },
  513. handleSelectionChange(list) {
  514. console.log(list);
  515. if (list.length > 1) {
  516. this.selection = list;
  517. } else {
  518. this.selection = [];
  519. }
  520. },
  521. handOneSelection(row) {
  522. const index = this.selection.findIndex((item) => item.id == row.id);
  523. if (index >= 0) {
  524. this.selection.splice(index, 1);
  525. } else {
  526. this.selection.push(row)
  527. }
  528. },
  529. selectionFilter(row) {
  530. return this.selection.findIndex((item) => item.id == row.id) >= 0;
  531. },
  532. goDetail({ id }) {
  533. this.$router.push({
  534. path: '/productionPlan/detail/' + id
  535. });
  536. },
  537. // 拆批
  538. toUnpack(row) {
  539. if (!row.batchNo) {
  540. return this.$message.error('请先填写批次号!');
  541. }
  542. this.$refs.unpackRef.open(row);
  543. },
  544. // 合并
  545. handleMerge() {
  546. if (this.selection.length <= 1) {
  547. return this.$message.warning('请先勾选二个或多个计划!');
  548. }
  549. const productCode = this.selection[0].productCode;
  550. const produceRoutingId = this.selection[0].produceRoutingId;
  551. for (var i = 0; i < this.selection.length; i++) {
  552. if (productCode != this.selection[i].productCode) {
  553. return this.$message.warning('产品编码不一致!');
  554. }
  555. if (produceRoutingId != this.selection[i].produceRoutingId) {
  556. return this.$message.warning('工艺路线不一致!');
  557. }
  558. }
  559. this.$refs.mergeRef.open(this.selection);
  560. console.log(this.selection);
  561. },
  562. splitDetails(type, row) {
  563. this.$refs.DetailsRef.open(type, row);
  564. },
  565. onSortChange(e) {
  566. let sort = {
  567. orderBy: e.order,
  568. sortName: e.prop
  569. };
  570. this.sort = sort;
  571. this.reload();
  572. },
  573. sortTop(row) {
  574. row.priority = Number(row.priority) + 1;
  575. this.priorityChange(row);
  576. },
  577. sortBottom(row) {
  578. if (row.priority <= 1) {
  579. return;
  580. }
  581. row.priority = Number(row.priority) - 1;
  582. this.priorityChange(row);
  583. },
  584. priorityChange(row) {
  585. if (row.priority > 10) {
  586. row.priority = 10; // 如果大于 10,则设置为 10
  587. } else if (row.priority < 0) {
  588. row.priority = 0; // 如果小于 0,则设置为 0
  589. }
  590. this.priorityFn(row);
  591. },
  592. priorityFn: debounce(function (row) {
  593. let params = {
  594. id: row.id,
  595. priority: row.priority
  596. };
  597. updatePriority(params).then((res) => { });
  598. }, 800)
  599. }
  600. };
  601. </script>
  602. <style lang="scss" scoped>
  603. .btn_box {
  604. margin-bottom: 6px;
  605. }
  606. .check_box {
  607. width: 100%;
  608. display: flex;
  609. align-items: center;
  610. justify-content: center;
  611. cursor: pointer;
  612. }
  613. .check {
  614. width: 14px;
  615. height: 14px;
  616. border: 1px solid #dddddd;
  617. display: flex;
  618. align-items: center;
  619. justify-content: center;
  620. }
  621. .act_check {
  622. border: 1px solid #409eff;
  623. background: #409eff;
  624. .el-icon-check {
  625. color: #fff;
  626. font-size: 10px;
  627. }
  628. }
  629. </style>