index.vue 18 KB

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