index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. <template>
  2. <div class="ele-body">
  3. <el-card shadow="never" v-loading="loading">
  4. <produceOrder-search
  5. @search="reload"
  6. ref="searchRef"
  7. :statusOpt="statusOpt"
  8. :planType="planType"
  9. :activeName="activeName"
  10. >
  11. </produceOrder-search>
  12. <el-tabs v-model="activeName" type="card">
  13. <el-tab-pane label="未完成工单" name="first"></el-tab-pane>
  14. <el-tab-pane label="已完成工单" name="second"></el-tab-pane>
  15. </el-tabs>
  16. <!-- 数据表格 -->
  17. <ele-pro-table
  18. ref="table"
  19. :key="activeName"
  20. :initLoad="false"
  21. :columns="newColumns"
  22. :datasource="datasource"
  23. row-key="code"
  24. :cache-key="`${activeName}produceOrderTable`"
  25. :selection.sync="selection"
  26. @sort-change="onSortChange"
  27. autoAmendPage
  28. :parse-data="parseData"
  29. >
  30. <template v-slot:toolbar>
  31. <el-button type="success">新建</el-button>
  32. <el-button type="success">暂停</el-button>
  33. <el-button type="success">终止</el-button>
  34. <!-- <el-button type="primary">工单刷新</el-button> -->
  35. <el-button type="success" @click="handlePicking">领料</el-button>
  36. <el-button type="success" @click="toEnd()">批量完结</el-button>
  37. <!-- <el-button type="success" @click="handleCreate">创建工单</el-button> -->
  38. <!-- <el-button type="success">工单操作控制</el-button> -->
  39. <el-button type="success" @click="allPrinting()"
  40. >批量打印二维码</el-button
  41. >
  42. </template>
  43. <template v-slot:code="{ row }">
  44. <el-link type="primary" :underline="false" @click="goDetail(row)">
  45. {{ row.code }}
  46. </el-link>
  47. </template>
  48. <template v-slot:QRcode="{ row }">
  49. <el-link type="primary" :underline="false" @click="handleQRcode(row)"
  50. >生成二维码
  51. </el-link>
  52. </template>
  53. <template v-slot:priority="{ row }">
  54. <div style="display: flex">
  55. <el-input
  56. v-model="row.priority"
  57. type="number"
  58. size="mini"
  59. :min="0"
  60. :max="10"
  61. @change="priorityChange(row)"
  62. style="width: 80px"
  63. ></el-input>
  64. <el-popover
  65. placement="right"
  66. width="200"
  67. trigger="hover"
  68. content="数值越大优先级越高(0-3普通, 4-6优先, 7-10紧急)"
  69. >
  70. <div class="sort-wrap" slot="reference">
  71. <i class="el-icon-caret-top" @click="sortTop(row)"></i>
  72. <i class="el-icon-caret-bottom" @click="sortBottom(row)"></i>
  73. </div>
  74. </el-popover>
  75. </div>
  76. </template>
  77. <template v-slot:formingNum="{ row }">
  78. <span> {{ row.formingNum }} {{ row.unit }} </span>
  79. </template>
  80. <template v-slot:formingWeight="{ row }">
  81. <span> {{ row.formingNum }} {{ row.weightUnit }} </span>
  82. </template>
  83. <template v-slot:status="{ row }">
  84. <span :class="{ 'ele-text-danger': row.status == 3 }">
  85. {{ statusFormatter(row.status) }}
  86. </span>
  87. </template>
  88. <!-- 操作列 -->
  89. <template v-slot:action="{ row }">
  90. <template v-if="activeName == 'second'">
  91. <el-link
  92. type="primary"
  93. :underline="false"
  94. icon="el-icon-truck"
  95. v-if="row.status == 6"
  96. @click="toCancel(row)"
  97. >
  98. 取消完结
  99. </el-link></template
  100. >
  101. <template v-if="activeName != 'second'">
  102. <el-link
  103. v-if="row.status == 4"
  104. type="primary"
  105. :underline="false"
  106. icon="el-icon-truck"
  107. @click="handleOrderPublish(1, row)"
  108. >
  109. 报工
  110. </el-link>
  111. <el-link
  112. v-if="row.status == 4 && row.isSplit == 0"
  113. type="primary"
  114. :underline="false"
  115. @click="toUnpack(row)"
  116. >
  117. 拆分
  118. </el-link>
  119. <el-link
  120. v-if="row.status == 4"
  121. type="primary"
  122. :underline="false"
  123. @click="toEnd(row)"
  124. >
  125. 完结
  126. </el-link>
  127. </template>
  128. </template>
  129. </ele-pro-table>
  130. </el-card>
  131. <print ref="printRef"></print>
  132. <printSr ref="printSrRef"></printSr>
  133. <printTg ref="printTgRef"></printTg>
  134. <createDialog ref="createRef" @success="createSuccess" />
  135. <unpackDialog ref="unpackRef" @success="createSuccess" />
  136. <pickingDialog ref="PickingRef" />
  137. <detailsPop ref="detailsRef"> </detailsPop>
  138. </div>
  139. </template>
  140. <script>
  141. import {
  142. getPage,
  143. batchCompletion,
  144. cancelCompletion,
  145. updatePriority
  146. } from '@/api/produceOrder/index.js';
  147. import { fieldModel } from '@/api/produceWord/index.js';
  148. import produceOrderSearch from './components/produceOrder-search.vue';
  149. import createDialog from './components/createDialog.vue';
  150. import unpackDialog from './components/unpackDialog.vue';
  151. import pickingDialog from './components/pickingDialog.vue';
  152. import print from './components/print.vue';
  153. import printSr from './components/printSr';
  154. import printTg from './components/printTg';
  155. import detailsPop from './components/details/index.vue'
  156. import { debounce } from 'lodash';
  157. export default {
  158. components: {
  159. produceOrderSearch,
  160. pickingDialog,
  161. createDialog,
  162. unpackDialog,
  163. print,
  164. printSr,
  165. printTg,
  166. detailsPop
  167. },
  168. data() {
  169. return {
  170. activeName: 'first',
  171. // 加载状态
  172. loading: false,
  173. pageType: 'add',
  174. dialogTitle: '',
  175. isBindPlan: false,
  176. statusOpt: {
  177. first: [
  178. { label: '所有状态', value: '5,4' },
  179. { label: '待生产', value: '4' },
  180. { label: '生产中', value: '5' }
  181. // { label: '已延期', value: '7' }
  182. ],
  183. second: [{ label: '已完成', value: '6' }]
  184. },
  185. planType: [
  186. { label: '所有计划类型', value: null },
  187. { label: '内销计划', value: '1' },
  188. { label: '外销计划', value: '2' },
  189. { label: '预制计划', value: '3' }
  190. ],
  191. selection: [],
  192. newColumns: []
  193. };
  194. },
  195. computed: {
  196. // 表格列配置
  197. columns() {
  198. const opt = {
  199. first: [
  200. // {
  201. // prop: 'deliveryTime',
  202. // label: '预测交货日期',
  203. // align: 'center',
  204. // showOverflowTooltip: true,
  205. // minWidth: 110
  206. // }
  207. ],
  208. second: [
  209. {
  210. prop: 'completeTime',
  211. label: '完成时间',
  212. align: 'center'
  213. },
  214. {
  215. prop: 'cycle',
  216. label: '生产周期',
  217. align: 'center'
  218. }
  219. ]
  220. };
  221. return [
  222. {
  223. width: 45,
  224. type: 'selection',
  225. columnKey: 'selection',
  226. align: 'center',
  227. fixed: 'left'
  228. },
  229. {
  230. prop: 'batchNo',
  231. label: '批次号',
  232. align: 'center',
  233. minWidth: 100
  234. },
  235. {
  236. slot: 'code',
  237. label: '生产工单号',
  238. align: 'center',
  239. minWidth: 110
  240. },
  241. // {
  242. // prop: 'originalCode',
  243. // label: '原始工单号',
  244. // align: 'center',
  245. // minWidth: 110
  246. // },
  247. {
  248. slot: 'QRcode',
  249. label: '二维码',
  250. align: 'center',
  251. minWidth: 110
  252. },
  253. {
  254. prop: 'productionPlanCode',
  255. label: '计划编号',
  256. align: 'center',
  257. minWidth: 110
  258. },
  259. {
  260. prop: 'planType',
  261. label: '计划类型',
  262. align: 'center',
  263. formatter: (row) => {
  264. const obj = this.planType.find((i) => i.value == row.planType);
  265. return obj && obj.label;
  266. }
  267. },
  268. {
  269. prop: 'produceRoutingName',
  270. label: '工艺路线',
  271. align: 'center'
  272. },
  273. {
  274. prop: 'productCode',
  275. label: '物料编码',
  276. align: 'center'
  277. },
  278. {
  279. prop: 'productName',
  280. label: '产品名称',
  281. align: 'center'
  282. },
  283. {
  284. prop: 'brandNo',
  285. label: '牌号',
  286. align: 'center'
  287. },
  288. {
  289. prop: 'model',
  290. label: '型号',
  291. align: 'center'
  292. },
  293. {
  294. prop: 'priority',
  295. label: '优先级',
  296. align: 'center',
  297. minWidth: 120,
  298. slot: 'priority',
  299. sortable: 'custom'
  300. },
  301. {
  302. prop: 'formingNum',
  303. label: '要求生产数量',
  304. align: 'center',
  305. slot: 'formingNum',
  306. showOverflowTooltip: true,
  307. minWidth: 110
  308. },
  309. {
  310. prop: 'formingWeight',
  311. label: '要求生产重量',
  312. slot: 'formingWeight',
  313. align: 'center',
  314. showOverflowTooltip: true,
  315. minWidth: 110
  316. },
  317. {
  318. prop: 'formedNum',
  319. label: '已生产数量',
  320. align: 'center',
  321. showOverflowTooltip: true,
  322. minWidth: 110
  323. },
  324. {
  325. prop: 'formedWeight',
  326. label: '已生产重量',
  327. align: 'center',
  328. showOverflowTooltip: true,
  329. minWidth: 110
  330. },
  331. {
  332. prop: 'planStartTime',
  333. label: '计划开始时间',
  334. align: 'center',
  335. showOverflowTooltip: true,
  336. minWidth: 110
  337. },
  338. {
  339. prop: 'planCompleteTime',
  340. label: '计划结束时间',
  341. align: 'center',
  342. showOverflowTooltip: true,
  343. minWidth: 110
  344. },
  345. {
  346. prop: 'startTime',
  347. label: '实际开始时间',
  348. align: 'center',
  349. showOverflowTooltip: true,
  350. minWidth: 110
  351. },
  352. ...opt[this.activeName],
  353. {
  354. prop: 'createTime',
  355. label: '创建时间',
  356. align: 'center',
  357. showOverflowTooltip: true,
  358. minWidth: 110
  359. },
  360. {
  361. slot: 'status',
  362. label: '状态',
  363. align: 'center',
  364. formatter: (row) => {
  365. const obj = this.statusOpt[this.activeName].find(
  366. (i) => i.value == row.status
  367. );
  368. return obj && obj.label;
  369. }
  370. },
  371. {
  372. prop: 'teamName',
  373. label: '班组',
  374. align: 'center',
  375. showOverflowTooltip: true
  376. }
  377. ];
  378. },
  379. clientEnvironmentId() {
  380. return this.$store.state.user.info.clientEnvironmentId;
  381. }
  382. },
  383. created() {
  384. this.getFieldModel();
  385. },
  386. methods: {
  387. handlePicking() {
  388. this.$router.push({
  389. path: '/produceOrder/picking'
  390. });
  391. },
  392. statusFormatter(status) {
  393. const obj = this.statusOpt[this.activeName].find(
  394. (i) => i.value == status
  395. );
  396. return obj && obj.label;
  397. },
  398. /* 表格数据源 */
  399. async datasource({ page, limit, where, order }) {
  400. let res = await getPage({
  401. ...where,
  402. ...order,
  403. pageNum: page,
  404. size: limit,
  405. ...this.sort
  406. });
  407. // res['list'] = this.flattenArray(res.list)
  408. return res;
  409. },
  410. onSortChange(e) {
  411. let sort = {
  412. orderBy: e.order,
  413. sortName: e.prop
  414. };
  415. this.sort = sort;
  416. this.reload();
  417. },
  418. flattenArray(arr) {
  419. var result = []; // 存放结果的数组
  420. for (let i = 0; i < arr.length; i++) {
  421. if (Array.isArray(arr[i].subWorkOrder)) {
  422. let _arr = [];
  423. _arr = _arr.concat(arr[i].subWorkOrder);
  424. delete arr[i].subWorkOrder;
  425. result.push(arr[i]);
  426. result.push(..._arr);
  427. } else {
  428. result.push(arr[i]);
  429. }
  430. }
  431. return result;
  432. },
  433. /* 数据转为树形结构 */
  434. parseData(data) {
  435. return {
  436. ...data,
  437. list: this.$util.toTreeData({
  438. data: data.list,
  439. count: data.total,
  440. idField: 'code',
  441. parentIdField: 'originalCode'
  442. })
  443. };
  444. },
  445. /* 数据转为树形结构 */
  446. createSuccess() {
  447. this.reload();
  448. },
  449. handleCreate() {
  450. this.$refs.createRef.open(0);
  451. },
  452. // 发布工单
  453. handleOrderPublish(type, row) {
  454. this.$router.push({
  455. path: '/produce',
  456. query: {
  457. workOrderId: row.id
  458. }
  459. });
  460. },
  461. getFieldModel() {
  462. fieldModel({ fieldModel: 't_main_category' }).then((res) => {
  463. const privateColumn = [
  464. {
  465. columnKey: 'action',
  466. label: '操作',
  467. width: 250,
  468. align: 'center',
  469. resizable: false,
  470. fixed: 'right',
  471. slot: 'action',
  472. showOverflowTooltip: true
  473. }
  474. ];
  475. let newRes = res.map((m) => {
  476. return {
  477. prop: 'extField.' + m.prop,
  478. label: m.label,
  479. align: 'center',
  480. showOverflowTooltip: true
  481. };
  482. });
  483. this.newColumns = [...this.columns, ...newRes, ...privateColumn];
  484. this.$forceUpdate();
  485. });
  486. },
  487. // 完结与批量完结
  488. toEnd(row) {
  489. if (row) {
  490. this.$confirm(`是否要完结工单【${row.code}】?`, '提醒', {
  491. confirmButtonText: '确认',
  492. cancelButtonText: '取消',
  493. type: 'warning'
  494. })
  495. .then(() => {
  496. batchCompletion([row.id]).then((res) => {
  497. this.$message.success('成功');
  498. this.reload();
  499. });
  500. })
  501. .catch(() => {});
  502. } else {
  503. if (!this.selection.length) {
  504. return this.$message.warning('请至少选择一条工单!');
  505. }
  506. const ids = [];
  507. this.selection.map((item) => {
  508. ids.push(item.id);
  509. });
  510. const h = this.$createElement;
  511. this.$msgbox({
  512. title: '提醒',
  513. message: h('p', null, [
  514. h('span', null, '是否要完结 '),
  515. h(
  516. 'span',
  517. { style: 'color: #70B603' },
  518. `${this.selection.length}`
  519. ),
  520. h('span', null, ' 条工单?')
  521. ]),
  522. showCancelButton: true,
  523. confirmButtonText: '确认',
  524. cancelButtonText: '取消',
  525. type: 'warning'
  526. })
  527. .then(() => {
  528. batchCompletion(ids).then((res) => {
  529. this.$message.success('成功');
  530. this.reload();
  531. });
  532. })
  533. .catch(() => {});
  534. }
  535. },
  536. // 取消完结
  537. toCancel(row) {
  538. cancelCompletion([row.id]).then((res) => {
  539. this.$message.success('成功');
  540. this.reload();
  541. });
  542. },
  543. // 拆分
  544. toUnpack(row) {
  545. this.$refs.unpackRef.open(row);
  546. },
  547. handleTabChange() {
  548. this.$refs.searchRef.reset();
  549. },
  550. /* 刷新表格 */
  551. reload(where = {}) {
  552. this.$nextTick(() => {
  553. where.statusList = (
  554. where.status || this.statusOpt[this.activeName][0].value
  555. ).split(',');
  556. this.$refs.table.reload({ page: 1, where });
  557. });
  558. },
  559. goDetail(row) {
  560. this.$refs.detailsRef.open(row)
  561. },
  562. handleDelete({ id }) {
  563. this.$confirm('确定删除当前数据?', '提示').then(async () => {
  564. await del(id);
  565. this.$message.success('删除成功!');
  566. this.reload();
  567. });
  568. },
  569. handleQRcode(row) {
  570. if (this.clientEnvironmentId == 2) {
  571. this.$refs.printSrRef.open([row.id]);
  572. } else if (this.clientEnvironmentId == 3) {
  573. this.$refs.printTgRef.open([row.id]);
  574. } else {
  575. this.$refs.printRef.open([row.id]);
  576. }
  577. },
  578. allPrinting() {
  579. let ids = this.findAllIds(this.selection);
  580. if (this.clientEnvironmentId == 2) {
  581. this.$refs.printSrRef.open(ids);
  582. } else if (this.clientEnvironmentId == 3) {
  583. this.$refs.printTgRef.open(ids);
  584. } else {
  585. this.$refs.printRef.open(ids);
  586. }
  587. },
  588. findAllIds(nodes) {
  589. let ids = [];
  590. nodes.forEach((node) => {
  591. ids.push(node.id); // 添加当前节点的id
  592. if (node.children && node.children.length > 0) {
  593. // 递归调用自身来处理子节点
  594. ids = ids.concat(this.findAllIds(node.children));
  595. }
  596. });
  597. return ids;
  598. },
  599. sortTop(row) {
  600. row.priority = Number(row.priority) + 1;
  601. this.priorityChange(row);
  602. },
  603. sortBottom(row) {
  604. if (row.priority <= 1) {
  605. return;
  606. }
  607. row.priority = Number(row.priority) - 1;
  608. this.priorityChange(row);
  609. },
  610. priorityChange(row) {
  611. if (row.priority > 10) {
  612. row.priority = 10; // 如果大于 10,则设置为 10
  613. } else if (row.priority < 0) {
  614. row.priority = 0; // 如果小于 0,则设置为 0
  615. }
  616. this.priorityFn(row);
  617. },
  618. priorityFn: debounce(function (row) {
  619. let params = {
  620. id: row.id,
  621. priority: row.priority
  622. };
  623. updatePriority(params).then((res) => {});
  624. }, 800)
  625. }
  626. };
  627. </script>