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. icon="el-icon-truck"
  116. @click="toUnpack(row)"
  117. >
  118. 拆分
  119. </el-link>
  120. <el-link
  121. v-if="row.status == 4"
  122. type="primary"
  123. :underline="false"
  124. icon="el-icon-edit"
  125. @click="toEnd(row)"
  126. >
  127. 完结
  128. </el-link>
  129. </template>
  130. </template>
  131. </ele-pro-table>
  132. </el-card>
  133. <print ref="printRef"></print>
  134. <printSr ref="printSrRef"></printSr>
  135. <printTg ref="printTgRef"></printTg>
  136. <createDialog ref="createRef" @success="createSuccess" />
  137. <unpackDialog ref="unpackRef" @success="createSuccess" />
  138. <pickingDialog ref="PickingRef" />
  139. </div>
  140. </template>
  141. <script>
  142. import {
  143. getPage,
  144. batchCompletion,
  145. cancelCompletion,
  146. updatePriority
  147. } from '@/api/produceOrder/index.js';
  148. import { fieldModel } from '@/api/produceWord/index.js';
  149. import produceOrderSearch from './components/produceOrder-search.vue';
  150. import createDialog from './components/createDialog.vue';
  151. import unpackDialog from './components/unpackDialog.vue';
  152. import pickingDialog from './components/pickingDialog.vue';
  153. import print from './components/print.vue';
  154. import printSr from './components/printSr';
  155. import printTg from './components/printTg';
  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. },
  167. data() {
  168. return {
  169. activeName: 'first',
  170. // 加载状态
  171. loading: false,
  172. pageType: 'add',
  173. dialogTitle: '',
  174. isBindPlan: false,
  175. statusOpt: {
  176. first: [
  177. { label: '所有状态', value: '5,4' },
  178. { label: '待生产', value: '4' },
  179. { label: '生产中', value: '5' }
  180. // { label: '已延期', value: '7' }
  181. ],
  182. second: [{ label: '已完成', value: '6' }]
  183. },
  184. planType: [
  185. { label: '所有计划类型', value: null },
  186. { label: '内销计划', value: '1' },
  187. { label: '外销计划', value: '2' },
  188. { label: '预制计划', value: '3' }
  189. ],
  190. selection: [],
  191. newColumns: []
  192. };
  193. },
  194. computed: {
  195. // 表格列配置
  196. columns() {
  197. const opt = {
  198. first: [
  199. // {
  200. // prop: 'deliveryTime',
  201. // label: '预测交货日期',
  202. // align: 'center',
  203. // showOverflowTooltip: true,
  204. // minWidth: 110
  205. // }
  206. ],
  207. second: [
  208. {
  209. prop: 'completeTime',
  210. label: '完成时间',
  211. align: 'center'
  212. },
  213. {
  214. prop: 'cycle',
  215. label: '生产周期',
  216. align: 'center'
  217. }
  218. ]
  219. };
  220. return [
  221. {
  222. width: 45,
  223. type: 'selection',
  224. columnKey: 'selection',
  225. align: 'center',
  226. fixed: 'left'
  227. },
  228. {
  229. prop: 'batchNo',
  230. label: '批次号',
  231. align: 'center',
  232. minWidth: 100
  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: 'priority',
  294. label: '优先级',
  295. align: 'center',
  296. minWidth: 120,
  297. slot: 'priority',
  298. sortable: 'custom'
  299. },
  300. {
  301. prop: 'formingNum',
  302. label: '要求生产数量',
  303. align: 'center',
  304. slot: 'formingNum',
  305. showOverflowTooltip: true,
  306. minWidth: 110
  307. },
  308. {
  309. prop: 'formingWeight',
  310. label: '要求生产重量',
  311. slot: 'formingWeight',
  312. align: 'center',
  313. showOverflowTooltip: true,
  314. minWidth: 110
  315. },
  316. {
  317. prop: 'formedNum',
  318. label: '已生产数量',
  319. align: 'center',
  320. showOverflowTooltip: true,
  321. minWidth: 110
  322. },
  323. {
  324. prop: 'formedWeight',
  325. label: '已生产重量',
  326. align: 'center',
  327. showOverflowTooltip: true,
  328. minWidth: 110
  329. },
  330. {
  331. prop: 'planStartTime',
  332. label: '计划开始时间',
  333. align: 'center',
  334. showOverflowTooltip: true,
  335. minWidth: 110
  336. },
  337. {
  338. prop: 'planCompleteTime',
  339. label: '计划结束时间',
  340. align: 'center',
  341. showOverflowTooltip: true,
  342. minWidth: 110
  343. },
  344. {
  345. prop: 'startTime',
  346. label: '实际开始时间',
  347. align: 'center',
  348. showOverflowTooltip: true,
  349. minWidth: 110
  350. },
  351. ...opt[this.activeName],
  352. {
  353. prop: 'createTime',
  354. label: '创建时间',
  355. align: 'center',
  356. showOverflowTooltip: true,
  357. minWidth: 110
  358. },
  359. {
  360. slot: 'status',
  361. label: '状态',
  362. align: 'center',
  363. formatter: (row) => {
  364. const obj = this.statusOpt[this.activeName].find(
  365. (i) => i.value == row.status
  366. );
  367. return obj && obj.label;
  368. }
  369. },
  370. {
  371. prop: 'teamName',
  372. label: '班组',
  373. align: 'center',
  374. showOverflowTooltip: true
  375. }
  376. ];
  377. },
  378. clientEnvironmentId() {
  379. return this.$store.state.user.info.clientEnvironmentId;
  380. }
  381. },
  382. created() {
  383. this.getFieldModel();
  384. },
  385. methods: {
  386. handlePicking() {
  387. this.$router.push({
  388. path: '/produceOrder/picking'
  389. });
  390. },
  391. statusFormatter(status) {
  392. const obj = this.statusOpt[this.activeName].find(
  393. (i) => i.value == status
  394. );
  395. return obj && obj.label;
  396. },
  397. /* 表格数据源 */
  398. async datasource({ page, limit, where, order }) {
  399. let res = await getPage({
  400. ...where,
  401. ...order,
  402. pageNum: page,
  403. size: limit,
  404. ...this.sort
  405. });
  406. // res['list'] = this.flattenArray(res.list)
  407. return res;
  408. },
  409. onSortChange(e) {
  410. let sort = {
  411. orderBy: e.order,
  412. sortName: e.prop
  413. };
  414. this.sort = sort;
  415. this.reload();
  416. },
  417. flattenArray(arr) {
  418. var result = []; // 存放结果的数组
  419. for (let i = 0; i < arr.length; i++) {
  420. if (Array.isArray(arr[i].subWorkOrder)) {
  421. let _arr = [];
  422. _arr = _arr.concat(arr[i].subWorkOrder);
  423. delete arr[i].subWorkOrder;
  424. result.push(arr[i]);
  425. result.push(..._arr);
  426. } else {
  427. result.push(arr[i]);
  428. }
  429. }
  430. return result;
  431. },
  432. /* 数据转为树形结构 */
  433. parseData(data) {
  434. return {
  435. ...data,
  436. list: this.$util.toTreeData({
  437. data: data.list,
  438. count: data.total,
  439. idField: 'code',
  440. parentIdField: 'originalCode'
  441. })
  442. };
  443. },
  444. /* 数据转为树形结构 */
  445. createSuccess() {
  446. this.reload();
  447. },
  448. handleCreate() {
  449. this.$refs.createRef.open(0);
  450. },
  451. // 发布工单
  452. handleOrderPublish(type, row) {
  453. this.$router.push({
  454. path: '/produceOrder/report',
  455. query: {
  456. type,
  457. id: 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.$router.push({
  561. path: '/produceOrder/detail/' + row.id,
  562. query: {
  563. produceVersionId: row.produceVersionId
  564. }
  565. });
  566. },
  567. handleDelete({ id }) {
  568. this.$confirm('确定删除当前数据?', '提示').then(async () => {
  569. await del(id);
  570. this.$message.success('删除成功!');
  571. this.reload();
  572. });
  573. },
  574. handleQRcode(row) {
  575. if (this.clientEnvironmentId == 2) {
  576. this.$refs.printSrRef.open([row.id]);
  577. } else if (this.clientEnvironmentId == 3) {
  578. this.$refs.printTgRef.open([row.id]);
  579. } else {
  580. this.$refs.printRef.open([row.id]);
  581. }
  582. },
  583. allPrinting() {
  584. let ids = this.findAllIds(this.selection);
  585. if (this.clientEnvironmentId == 2) {
  586. this.$refs.printSrRef.open(ids);
  587. } else if (this.clientEnvironmentId == 3) {
  588. this.$refs.printTgRef.open(ids);
  589. } else {
  590. this.$refs.printRef.open(ids);
  591. }
  592. },
  593. findAllIds(nodes) {
  594. let ids = [];
  595. nodes.forEach((node) => {
  596. ids.push(node.id); // 添加当前节点的id
  597. if (node.children && node.children.length > 0) {
  598. // 递归调用自身来处理子节点
  599. ids = ids.concat(this.findAllIds(node.children));
  600. }
  601. });
  602. return ids;
  603. },
  604. sortTop(row) {
  605. row.priority = Number(row.priority) + 1;
  606. this.priorityChange(row);
  607. },
  608. sortBottom(row) {
  609. if (row.priority <= 1) {
  610. return;
  611. }
  612. row.priority = Number(row.priority) - 1;
  613. this.priorityChange(row);
  614. },
  615. priorityChange(row) {
  616. if (row.priority > 10) {
  617. row.priority = 10; // 如果大于 10,则设置为 10
  618. } else if (row.priority < 0) {
  619. row.priority = 0; // 如果小于 0,则设置为 0
  620. }
  621. this.priorityFn(row);
  622. },
  623. priorityFn: debounce(function (row) {
  624. let params = {
  625. id: row.id,
  626. priority: row.priority
  627. };
  628. updatePriority(params).then((res) => {});
  629. }, 800)
  630. }
  631. };
  632. </script>