edit.vue 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  1. <template>
  2. <ele-modal
  3. v-if="visible"
  4. custom-class="ele-dialog-form long-dialog-form"
  5. :centered="true"
  6. :append-to-body="true"
  7. :visible.sync="dialogVisible"
  8. :close-on-click-modal="false"
  9. width="70%"
  10. :maxable="true"
  11. :resizable="true"
  12. :title="title"
  13. @close="onModalClose"
  14. >
  15. <div class="work-order-handover" v-loading="loading">
  16. <div class="handover_content">
  17. <!-- 工单信息 -->
  18. <div class="card_box">
  19. <div class="title_box rx-bc">
  20. <div class="name">工单信息</div>
  21. </div>
  22. <div class="material">
  23. <div class="content_table">
  24. <div class="item rx-sc">
  25. <div class="rx ww16">
  26. <div class="lable rx-cc">工单编码</div>
  27. <div class="content">{{ workOrderInfo.workOrderCode }}</div>
  28. </div>
  29. <div class="rx ww16">
  30. <div class="lable rx-cc">产品编码</div>
  31. <div class="content">{{ workOrderInfo.categoryCode }}</div>
  32. </div>
  33. <div class="rx ww16">
  34. <div class="lable rx-cc">批次号</div>
  35. <div class="content">{{ workOrderInfo.batchNo }}</div>
  36. </div>
  37. </div>
  38. <div class="item rx-sc">
  39. <div class="rx ww16">
  40. <div class="lable rx-cc">产品名称</div>
  41. <div class="content">{{ workOrderInfo.categoryName }}</div>
  42. </div>
  43. <div class="rx ww16">
  44. <div class="lable rx-cc">要求生产数量</div>
  45. <div class="content"
  46. >{{ workOrderInfo.formingNum }}{{ workOrderInfo.unit }}</div
  47. >
  48. </div>
  49. <div class="rx ww16">
  50. <div class="lable rx-cc">要求生产重量</div>
  51. <div class="content"
  52. >{{ workOrderInfo.formingWeight
  53. }}{{ workOrderInfo.weightUnit }}</div
  54. >
  55. </div>
  56. </div>
  57. <div class="item rx-sc">
  58. <div class="rx ww16">
  59. <div class="lable rx-cc">生产编号</div>
  60. <div class="content">{{ workOrderInfo.productionCodes }}</div>
  61. </div>
  62. <div class="rx ww16">
  63. <div class="lable rx-cc">已生产数量</div>
  64. <div class="content"
  65. >{{ workOrderInfo.formedNum || 0
  66. }}{{ workOrderInfo.unit }}</div
  67. >
  68. </div>
  69. <div class="rx ww16">
  70. <div class="lable rx-cc">已生产重量</div>
  71. <div class="content"
  72. >{{ workOrderInfo.formedWeight || 0
  73. }}{{ workOrderInfo.weightUnit }}</div
  74. >
  75. </div>
  76. </div>
  77. <div class="item rx-sc">
  78. <div class="rx ww16">
  79. <div class="lable rx-cc">当前工序</div>
  80. <div class="content">{{
  81. (workOrderInfo.currentTaskDiagram || {}).taskTypeName ||
  82. workOrderInfo.initiateTaskName ||
  83. ''
  84. }}</div>
  85. </div>
  86. <div class="rx ww16">
  87. <div class="lable rx-cc"></div>
  88. <div class="content"></div>
  89. </div>
  90. <div class="rx ww16">
  91. <div class="lable rx-cc"></div>
  92. <div class="content"></div>
  93. </div>
  94. </div>
  95. </div>
  96. </div>
  97. </div>
  98. <!-- 交接信息 -->
  99. <div class="card_box">
  100. <div class="title_box rx-bc">
  101. <div class="name">交接信息</div>
  102. </div>
  103. <el-form
  104. ref="handoverForm"
  105. :model="workOrderInfo"
  106. :rules="handoverRules"
  107. label-width="100px"
  108. class="handover_form"
  109. size="small"
  110. >
  111. <div class="form_row">
  112. <el-form-item
  113. label="交接类型"
  114. prop="type"
  115. class="form_item"
  116. required
  117. >
  118. <el-select
  119. v-model="workOrderInfo.type"
  120. placeholder="请选择"
  121. style="width: 100%"
  122. :disabled="formLocked"
  123. >
  124. <el-option label="物品移交" :value="1" />
  125. <el-option label="班次交接" :value="2" />
  126. </el-select>
  127. </el-form-item>
  128. <el-form-item
  129. label="交接时间"
  130. prop="initiateTime"
  131. class="form_item"
  132. required
  133. >
  134. <el-date-picker
  135. v-model="workOrderInfo.initiateTime"
  136. type="datetime"
  137. format="yyyy-MM-dd HH:mm:ss"
  138. value-format="yyyy-MM-dd HH:mm:ss"
  139. placeholder="选择交接时间"
  140. style="width: 100%"
  141. :disabled="formLocked"
  142. />
  143. </el-form-item>
  144. <el-form-item label="交接单编码" class="form_item">
  145. <el-input
  146. v-model="handoverCodeDisplay"
  147. disabled
  148. placeholder="系统自动生成"
  149. />
  150. </el-form-item>
  151. </div>
  152. <div class="form_row">
  153. <el-form-item
  154. label="交接班组"
  155. prop="acceptTeamId"
  156. class="form_item"
  157. required
  158. v-if="workOrderInfo.type == 1"
  159. >
  160. <el-select
  161. v-model="workOrderInfo.acceptTeamId"
  162. filterable
  163. :disabled="formLocked"
  164. @change="onHandoverTeamChange"
  165. style="width: 100%"
  166. >
  167. <el-option
  168. v-for="item in teamList"
  169. :key="item.id"
  170. :label="item.name"
  171. :value="item.id"
  172. />
  173. </el-select>
  174. </el-form-item>
  175. <el-form-item
  176. label="交接人"
  177. prop="acceptId"
  178. class="form_item"
  179. required
  180. v-if="workOrderInfo.type == 1"
  181. >
  182. <el-select
  183. v-model="workOrderInfo.acceptId"
  184. filterable
  185. :disabled="formLocked"
  186. @change="onAcceptPersonChange"
  187. style="width: 100%"
  188. >
  189. <el-option
  190. v-for="item in personList"
  191. :key="item.id"
  192. :label="item.name"
  193. :value="item.id"
  194. />
  195. </el-select>
  196. </el-form-item>
  197. <div class="form_item form_item_empty"></div>
  198. </div>
  199. <div class="form_row">
  200. <el-form-item
  201. label="交接数量"
  202. prop="handoverQuantity"
  203. class="form_item"
  204. >
  205. <el-input
  206. v-model="workOrderInfo.handoverQuantity"
  207. placeholder="请输入"
  208. :disabled="formLocked"
  209. @input="
  210. batchInput(
  211. workOrderInfo.handoverQuantity,
  212. 'handoverQuantity'
  213. )
  214. "
  215. >
  216. <template slot="append">PCS</template>
  217. </el-input>
  218. </el-form-item>
  219. <el-form-item
  220. label="交接重量"
  221. prop="handoverWeight"
  222. class="form_item"
  223. >
  224. <el-input
  225. v-model="workOrderInfo.handoverWeight"
  226. placeholder="请输入"
  227. :disabled="formLocked"
  228. @input="
  229. batchInput(workOrderInfo.handoverWeight, 'handoverWeight')
  230. "
  231. >
  232. <template slot="append">KG</template>
  233. </el-input>
  234. </el-form-item>
  235. <div class="form_item form_item_empty"></div>
  236. </div>
  237. <div class="form_row_full">
  238. <el-form-item
  239. label="交接注意事项"
  240. prop="remark"
  241. class="form_item_full"
  242. >
  243. <el-input
  244. v-model="workOrderInfo.remark"
  245. type="textarea"
  246. :rows="3"
  247. placeholder="请输入"
  248. :disabled="formLocked"
  249. />
  250. </el-form-item>
  251. </div>
  252. </el-form>
  253. </div>
  254. <!-- 物料清单 -->
  255. <div class="card_box">
  256. <div class="title_box rx-bc">
  257. <div class="name">物料清单</div>
  258. <div class="rx-bc action_btns" v-if="!formLocked">
  259. <el-button type="primary" size="small" @click="addMaterial"
  260. >添加物料</el-button
  261. >
  262. <el-button type="danger" size="small" @click="batchDeleteMaterial"
  263. >批量删除</el-button
  264. >
  265. </div>
  266. </div>
  267. <ele-pro-table
  268. ref="table"
  269. :key="'handover-material-' + (formLocked ? 'ro' : 'rw')"
  270. :needPage="false"
  271. :columns="columns"
  272. :datasource="workOrderInfo.detailList"
  273. class="time-form"
  274. max-height="600"
  275. @columns-change="handleColumnChange"
  276. :cache-key="cacheKeyUrl"
  277. :selection.sync="selection"
  278. row-key="id"
  279. >
  280. <template v-slot:action="{ row }">
  281. <el-popconfirm
  282. class="ele-action"
  283. title="确定要删除此物料吗?"
  284. @confirm="remove(row)"
  285. >
  286. <template v-slot:reference>
  287. <el-link type="danger" :underline="false"> 删除 </el-link>
  288. </template>
  289. </el-popconfirm>
  290. </template>
  291. </ele-pro-table>
  292. </div>
  293. <!-- 接收信息:status 为 1(待接收)且非详情时展示 -->
  294. <div class="card_box receive_card" v-if="showReceiveBlock">
  295. <div class="title_box rx-bc">
  296. <div class="name">接收信息</div>
  297. </div>
  298. <el-form label-width="100px" size="small" class="receive_form">
  299. <el-form-item label="接收建议" required>
  300. <el-input
  301. v-model="workOrderInfo.acceptRemark"
  302. type="textarea"
  303. :rows="4"
  304. placeholder="请输入"
  305. />
  306. </el-form-item>
  307. <div class="receive_actions">
  308. <el-button
  309. type="success"
  310. size="small"
  311. :loading="receiveSubmitting"
  312. @click="handleReceiveDecision(2)"
  313. >同意</el-button
  314. >
  315. <el-button
  316. type="danger"
  317. size="small"
  318. :loading="receiveSubmitting"
  319. @click="handleReceiveDecision(3)"
  320. >驳回</el-button
  321. >
  322. </div>
  323. </el-form>
  324. </div>
  325. </div>
  326. <pickingList
  327. v-if="!formLocked"
  328. isType="pick"
  329. ref="pickingListRef"
  330. @allSelection="allSelection"
  331. ></pickingList>
  332. </div>
  333. <div slot="footer" class="dialog-footer" v-if="showMainFooter">
  334. <el-button
  335. type="primary"
  336. size="small"
  337. :loading="submitLoading"
  338. @click="submitHandover(1)"
  339. >提交</el-button
  340. >
  341. <el-button
  342. type="primary"
  343. size="small"
  344. :loading="submitLoading"
  345. @click="submitHandover(0)"
  346. >保存</el-button
  347. >
  348. <el-button size="small" :disabled="submitLoading" @click="onCancel"
  349. >取 消</el-button
  350. >
  351. </div>
  352. </ele-modal>
  353. </template>
  354. <script>
  355. import { getteampage } from '@/api/workforceManagement/team';
  356. import {
  357. getHandoverById,
  358. updateHandover,
  359. acceptOrRejectHandover
  360. } from '@/api/workOrderHandover';
  361. import pickingList from '@/views/produce/components/picking/newPickingList.vue';
  362. export default {
  363. name: 'WorkOrderHandoverEdit',
  364. components: {
  365. pickingList
  366. },
  367. props: {
  368. visible: {
  369. type: Boolean,
  370. default: true
  371. },
  372. title: {
  373. type: String,
  374. default: '工单交接'
  375. },
  376. /** 交接单主键 id,编辑/查看时传入;新增不传或传空 */
  377. workOrderId: {
  378. type: [String, Number],
  379. default: ''
  380. },
  381. /** 列表行状态:0 待提交 1 待接收 2 已接收 3 已驳回 */
  382. status: {
  383. type: [Number, String],
  384. default: 0
  385. },
  386. /** 仅查看详情:全部只读且不展示任何操作按钮 */
  387. detail: {
  388. type: Boolean,
  389. default: false
  390. }
  391. },
  392. data() {
  393. return {
  394. loading: false,
  395. dialogVisible: false,
  396. workOrderInfo: {
  397. acceptRemark: '',
  398. detailList: []
  399. },
  400. handoverRules: {
  401. type: [
  402. { required: true, message: '请选择交接类型', trigger: 'change' }
  403. ],
  404. initiateTime: [
  405. { required: true, message: '请选择交接时间', trigger: 'change' }
  406. ],
  407. acceptTeamId: [
  408. { required: true, message: '请选择交接班组', trigger: 'change' }
  409. ],
  410. acceptId: [
  411. { required: true, message: '请选择交接人', trigger: 'change' }
  412. ],
  413. handoverQuantity: [
  414. { required: true, message: '请输入交接数量', trigger: 'change' }
  415. ],
  416. handoverWeight: [
  417. { required: true, message: '请输入交接重量', trigger: 'change' }
  418. ]
  419. },
  420. teamList: [],
  421. personList: [],
  422. selection: [],
  423. cacheKeyUrl: 'mes_workOrderHandover_materialTable',
  424. receiveSubmitting: false,
  425. submitLoading: false
  426. };
  427. },
  428. watch: {
  429. visible: {
  430. immediate: true,
  431. handler(v) {
  432. this.dialogVisible = v;
  433. }
  434. },
  435. /** 与 ele-modal 同步:弹窗真正显示为 true 时再拉取详情(不监听 workOrderId) */
  436. dialogVisible(v) {
  437. if (v) {
  438. this.$nextTick(() => {
  439. this.bootstrapDialog();
  440. });
  441. }
  442. }
  443. },
  444. computed: {
  445. numericStatus() {
  446. const s = this.status;
  447. if (s === '' || s === null || s === undefined) return 0;
  448. const n = Number(s);
  449. return Number.isNaN(n) ? 0 : n;
  450. },
  451. /** 详情 或 待接收:交接信息与物料不可编辑 */
  452. formLocked() {
  453. if (this.detail) return true;
  454. return this.numericStatus === 1;
  455. },
  456. /** 展示底部 提交 / 保存 / 取消 */
  457. showMainFooter() {
  458. if (this.detail) return false;
  459. return this.numericStatus !== 1;
  460. },
  461. /** 展示「接收信息」区块(同意 / 驳回) */
  462. showReceiveBlock() {
  463. if (this.detail) return false;
  464. return this.numericStatus === 1;
  465. },
  466. handoverCodeDisplay() {
  467. const w = this.workOrderInfo || {};
  468. return w.handoverCode || w.code || '';
  469. },
  470. columns() {
  471. const cols = [];
  472. if (!this.formLocked) {
  473. cols.push({
  474. width: 45,
  475. type: 'selection',
  476. columnKey: 'selection',
  477. align: 'center',
  478. fixed: 'left',
  479. reserveSelection: true
  480. });
  481. }
  482. cols.push(
  483. {
  484. width: 55,
  485. type: 'index',
  486. columnKey: 'index',
  487. align: 'center',
  488. label: '序号',
  489. fixed: 'left'
  490. },
  491. {
  492. prop: 'categoryCode',
  493. label: '编码',
  494. align: 'center',
  495. showOverflowTooltip: true,
  496. sortable: true,
  497. minWidth: 110
  498. },
  499. {
  500. prop: 'categoryName',
  501. label: '名称',
  502. align: 'center',
  503. showOverflowTooltip: true,
  504. minWidth: 110
  505. },
  506. {
  507. prop: 'categoryLevelName',
  508. label: '分类',
  509. align: 'center',
  510. showOverflowTooltip: true
  511. },
  512. {
  513. prop: 'brandNum',
  514. align: 'center',
  515. label: '牌号',
  516. showOverflowTooltip: true
  517. },
  518. {
  519. prop: 'modelType',
  520. label: '型号',
  521. align: 'center',
  522. showOverflowTooltip: true
  523. },
  524. {
  525. prop: 'specification',
  526. label: '规格',
  527. align: 'center',
  528. showOverflowTooltip: true
  529. },
  530. {
  531. prop: 'measuringUnit',
  532. label: '计量单位',
  533. showOverflowTooltip: true,
  534. minWidth: 90
  535. },
  536. {
  537. prop: 'weightUnit',
  538. label: '重量单位',
  539. showOverflowTooltip: true,
  540. minWidth: 90
  541. }
  542. );
  543. if (!this.formLocked) {
  544. cols.push({
  545. columnKey: 'action',
  546. label: '操作',
  547. width: 100,
  548. align: 'center',
  549. resizable: false,
  550. slot: 'action',
  551. fixed: 'right'
  552. });
  553. }
  554. return cols;
  555. }
  556. },
  557. methods: {
  558. hasValidId(val) {
  559. return (
  560. val !== '' &&
  561. val !== null &&
  562. val !== undefined &&
  563. String(val).length > 0
  564. );
  565. },
  566. getSafeDetailList() {
  567. return Array.isArray(this.workOrderInfo.detailList)
  568. ? this.workOrderInfo.detailList
  569. : [];
  570. },
  571. closeDialogWithSuccess(res) {
  572. this.dialogVisible = false;
  573. this.$emit('update:visible', false);
  574. this.$emit('success', res);
  575. },
  576. closeDialogByCancel() {
  577. this.dialogVisible = false;
  578. this.$emit('update:visible', false);
  579. this.$emit('cancel');
  580. },
  581. clearAndRestoreTableSelection() {
  582. this.$nextTick(() => {
  583. this.$refs.table && this.$refs.table.clearSelection();
  584. this.selection.forEach((item) => {
  585. this.$refs.table && this.$refs.table.toggleRowSelection(item, true);
  586. });
  587. });
  588. },
  589. formatDateTimeNow() {
  590. const d = new Date();
  591. const p = (n) => String(n).padStart(2, '0');
  592. return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(
  593. d.getHours()
  594. )}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
  595. },
  596. /** 防止 acceptRemark 未定义导致模板报错 */
  597. ensureAcceptRemarkShape() {
  598. const w = this.workOrderInfo;
  599. if (!w || typeof w !== 'object') return;
  600. if (w.acceptRemark === undefined) {
  601. this.$set(w, 'acceptRemark', '');
  602. }
  603. },
  604. pickAcceptRemarkFromRaw(raw) {
  605. if (!raw || typeof raw !== 'object') return '';
  606. if (raw.acceptRemark !== undefined && raw.acceptRemark !== null) {
  607. return raw.acceptRemark;
  608. }
  609. if (
  610. raw.receiveAdvice &&
  611. typeof raw.receiveAdvice === 'object' &&
  612. raw.receiveAdvice !== null
  613. ) {
  614. return (
  615. raw.receiveAdvice.acceptRemark || raw.receiveAdvice.remark || ''
  616. );
  617. }
  618. if (typeof raw.receiveAdvice === 'string') {
  619. return raw.receiveAdvice;
  620. }
  621. return raw.receiveRemark || '';
  622. },
  623. getEmptyWorkOrderState() {
  624. return {
  625. type: 1,
  626. initiateTime: this.formatDateTimeNow(),
  627. handoverCode: '',
  628. workOrderCode: '',
  629. categoryCode: '',
  630. categoryName: '',
  631. productCode: '',
  632. productName: '',
  633. batchNo: '',
  634. unit: '',
  635. weightUnit: '',
  636. formingNum: '',
  637. formingWeight: '',
  638. productionCodes: '',
  639. formedNum: 0,
  640. formedWeight: 0,
  641. currentTaskDiagram: {},
  642. initiateTaskId: '',
  643. initiateTaskName: '',
  644. acceptTeamId: '',
  645. acceptTeamName: '',
  646. acceptId: '',
  647. acceptName: '',
  648. handoverQuantity: '',
  649. handoverWeight: '',
  650. remark: '',
  651. acceptRemark: '',
  652. detailList: [],
  653. workOrderId: ''
  654. };
  655. },
  656. normalizeHandoverDetail(raw) {
  657. if (!raw || typeof raw !== 'object') {
  658. return this.getEmptyWorkOrderState();
  659. }
  660. const detailList = Array.isArray(raw.detailList) ? raw.detailList : [];
  661. const task = raw.currentTaskDiagram || {};
  662. return {
  663. ...raw,
  664. detailList,
  665. categoryCode: raw.categoryCode || raw.productCode || '',
  666. categoryName: raw.categoryName || raw.productName || '',
  667. workOrderCode:
  668. raw.workOrderCode ||
  669. raw.workOrderNum ||
  670. raw.workOrderEntityCode ||
  671. '',
  672. handoverCode:
  673. raw.handoverCode || raw.passonCode || raw.passonOrderCode || '',
  674. acceptRemark: this.pickAcceptRemarkFromRaw(raw),
  675. currentTaskDiagram:
  676. raw.currentTaskDiagram ||
  677. (raw.initiateTaskId || raw.initiateTaskName
  678. ? {
  679. taskId: raw.initiateTaskId,
  680. taskTypeName: raw.initiateTaskName
  681. }
  682. : task)
  683. };
  684. },
  685. async loadTeamListOnly() {
  686. const factoryId = this.$store.state.user.info.factoryId;
  687. const { list } = await getteampage({
  688. pageNum: 1,
  689. size: -1,
  690. factoryId
  691. });
  692. this.teamList = Array.isArray(list) ? list : [];
  693. if (!this.teamList.length) {
  694. this.personList = [];
  695. }
  696. },
  697. /** 弹窗打开(visible 为 true)时统一拉取/初始化,不依赖对 workOrderId 的 watch */
  698. async bootstrapDialog() {
  699. if (!this.visible) return;
  700. this.loading = true;
  701. try {
  702. const id = this.workOrderId;
  703. const hasId = this.hasValidId(id);
  704. await this.loadTeamListOnly();
  705. if (hasId) {
  706. const res = await getHandoverById(id);
  707. this.workOrderInfo = this.normalizeHandoverDetail(res);
  708. const preferredAcceptId = this.workOrderInfo.acceptId;
  709. if (
  710. this.workOrderInfo.acceptTeamId != null &&
  711. this.workOrderInfo.acceptTeamId !== ''
  712. ) {
  713. this.syncTeamUsersAndLeader(
  714. this.workOrderInfo.acceptTeamId,
  715. preferredAcceptId
  716. );
  717. } else if (this.teamList.length) {
  718. const first = this.teamList[0].id;
  719. this.workOrderInfo.acceptTeamId = first;
  720. this.syncTeamUsersAndLeader(first);
  721. }
  722. } else {
  723. this.workOrderInfo = this.getEmptyWorkOrderState();
  724. if (this.teamList.length) {
  725. const first = this.teamList[0].id;
  726. this.workOrderInfo.acceptTeamId = first;
  727. this.syncTeamUsersAndLeader(first);
  728. }
  729. }
  730. } catch (e) {
  731. this.$message.error((e && e.message) || '加载交接单失败');
  732. this.workOrderInfo = this.getEmptyWorkOrderState();
  733. } finally {
  734. this.loading = false;
  735. this.ensureAcceptRemarkShape();
  736. this.$nextTick(() => {
  737. this.$refs.handoverForm && this.$refs.handoverForm.clearValidate();
  738. });
  739. }
  740. },
  741. onHandoverTeamChange(teamId) {
  742. this.syncTeamUsersAndLeader(teamId);
  743. },
  744. onAcceptPersonChange(personId) {
  745. const person =
  746. this.personList.find(
  747. (item) => String(item.id) === String(personId)
  748. ) || {};
  749. this.workOrderInfo.acceptName = person.name || '';
  750. },
  751. /**
  752. * @param preferredAcceptId 详情接口已返回的交接人 id,若在班组人员中则优先保留
  753. */
  754. syncTeamUsersAndLeader(teamId, preferredAcceptId) {
  755. const currentTeam =
  756. this.teamList.find((item) => String(item.id) === String(teamId)) ||
  757. {};
  758. this.workOrderInfo.acceptTeamName = currentTeam.name || '';
  759. const users = Array.isArray(currentTeam.userVOList)
  760. ? currentTeam.userVOList
  761. : [];
  762. this.personList = users.map((user) => ({
  763. id: user.id,
  764. name: user.name
  765. }));
  766. if (!this.personList.length) {
  767. this.workOrderInfo.acceptId = '';
  768. this.workOrderInfo.acceptName = '';
  769. return;
  770. }
  771. if (
  772. preferredAcceptId != null &&
  773. preferredAcceptId !== '' &&
  774. this.personList.some(
  775. (u) => String(u.id) === String(preferredAcceptId)
  776. )
  777. ) {
  778. const pick =
  779. this.personList.find(
  780. (u) => String(u.id) === String(preferredAcceptId)
  781. ) || {};
  782. this.workOrderInfo.acceptId = pick.id;
  783. this.workOrderInfo.acceptName = pick.name || '';
  784. return;
  785. }
  786. const leader =
  787. this.personList.find(
  788. (user) => String(user.id) === String(currentTeam.leaderUserId)
  789. ) || this.personList[0];
  790. this.workOrderInfo.acceptId = leader.id;
  791. this.workOrderInfo.acceptName = leader.name || '';
  792. },
  793. batchInput(val, type) {
  794. let newVal = String(val == null ? '' : val).replace(/[^\d.]/g, '');
  795. if (newVal.startsWith('.')) {
  796. newVal = '';
  797. }
  798. const firstDotIndex = newVal.indexOf('.');
  799. if (firstDotIndex !== -1) {
  800. newVal =
  801. newVal.slice(0, firstDotIndex + 1) +
  802. newVal.slice(firstDotIndex + 1).replace(/\./g, '');
  803. }
  804. const match = newVal.match(/^(\d+)(\.\d{0,4})?/);
  805. if (type === 'handoverQuantity') {
  806. this.workOrderInfo.handoverQuantity = match ? match[0] : '';
  807. } else if (type === 'handoverWeight') {
  808. this.workOrderInfo.handoverWeight = match ? match[0] : '';
  809. }
  810. },
  811. submitHandover(isRelease) {
  812. if (this.formLocked || this.detail || this.submitLoading) return;
  813. this.$refs.handoverForm.validate((valid) => {
  814. if (!valid) return;
  815. const detailList = this.getSafeDetailList().map((row) => {
  816. const { id, ...rest } = row;
  817. return rest;
  818. });
  819. const param = {
  820. ...this.workOrderInfo,
  821. detailList,
  822. isRelease
  823. };
  824. this.submitLoading = true;
  825. updateHandover(param)
  826. .then((res) => {
  827. this.$message.success(isRelease === 1 ? '提交成功' : '保存成功');
  828. this.closeDialogWithSuccess(res);
  829. })
  830. .catch((e) => {
  831. this.$message.error((e && e.message) || '提交失败');
  832. })
  833. .finally(() => {
  834. this.submitLoading = false;
  835. });
  836. });
  837. },
  838. /** 同意 status=2,驳回 status=3 */
  839. async handleReceiveDecision(status) {
  840. const advice = (this.workOrderInfo.acceptRemark || '').trim();
  841. if (!advice) {
  842. this.$message.warning('请填写接收建议');
  843. return;
  844. }
  845. if (!this.hasValidId(this.workOrderInfo.id)) {
  846. this.$message.warning('缺少交接单信息');
  847. return;
  848. }
  849. this.receiveSubmitting = true;
  850. try {
  851. const res = await acceptOrRejectHandover({
  852. id: this.workOrderInfo.id,
  853. status,
  854. acceptRemark: advice
  855. });
  856. this.$message.success(status === 2 ? '接收成功' : '已驳回');
  857. this.closeDialogWithSuccess(res);
  858. } catch (e) {
  859. this.$message.error((e && e.message) || '操作失败');
  860. } finally {
  861. this.receiveSubmitting = false;
  862. }
  863. },
  864. onModalClose() {
  865. this.closeDialogByCancel();
  866. },
  867. onCancel() {
  868. this.closeDialogByCancel();
  869. },
  870. handleColumnChange() {},
  871. addMaterial() {
  872. const pickList = this.getSafeDetailList();
  873. this.$refs.pickingListRef.open(
  874. '',
  875. {
  876. pickList
  877. },
  878. '选择列表'
  879. );
  880. },
  881. batchDeleteMaterial() {
  882. const selected = Array.isArray(this.selection) ? this.selection : [];
  883. if (!selected.length) {
  884. this.$message.warning('请先选择要删除的物料');
  885. return;
  886. }
  887. this.$confirm(
  888. `确定删除已选中的 ${selected.length} 条物料吗?`,
  889. '提示',
  890. {
  891. type: 'warning'
  892. }
  893. )
  894. .then(() => {
  895. const detailList = this.getSafeDetailList();
  896. const selectedIds = new Set(
  897. selected
  898. .map((item) => (item && item.id != null ? String(item.id) : ''))
  899. .filter(Boolean)
  900. );
  901. let next;
  902. if (selectedIds.size) {
  903. next = detailList.filter(
  904. (item) => !selectedIds.has(String(item.id))
  905. );
  906. } else {
  907. next = detailList.filter((item) => !selected.includes(item));
  908. }
  909. this.$set(this.workOrderInfo, 'detailList', next);
  910. this.selection = [];
  911. this.$nextTick(() => {
  912. this.$refs.table && this.$refs.table.clearSelection();
  913. });
  914. this.$message.success('删除成功');
  915. })
  916. .catch(() => {});
  917. },
  918. allSelection(id, list) {
  919. this.$set(this.workOrderInfo, 'detailList', list || []);
  920. },
  921. /* 删除 */
  922. remove(row) {
  923. if (!row) return;
  924. const detailList = this.getSafeDetailList();
  925. const next = detailList.filter((item) => {
  926. if (row.id != null && item.id != null) {
  927. return String(item.id) !== String(row.id);
  928. }
  929. return item !== row;
  930. });
  931. this.$set(this.workOrderInfo, 'detailList', next);
  932. this.selection = this.selection.filter((item) => {
  933. if (row.id != null && item.id != null) {
  934. return String(item.id) !== String(row.id);
  935. }
  936. return item !== row;
  937. });
  938. this.clearAndRestoreTableSelection();
  939. this.$message.success('删除成功');
  940. }
  941. }
  942. };
  943. </script>
  944. <style scoped lang="scss">
  945. .work-order-handover {
  946. background: #fff;
  947. padding: 0;
  948. max-height: calc(100vh - 220px);
  949. overflow-y: auto;
  950. }
  951. .handover_content {
  952. display: flex;
  953. flex-direction: column;
  954. gap: 16px;
  955. }
  956. .card_box {
  957. background: #fff;
  958. padding: 12px;
  959. border-radius: 2px;
  960. border: 1px solid #e3e5e5;
  961. }
  962. .title_box {
  963. padding: 10px 0;
  964. margin-bottom: 8px;
  965. .name {
  966. font-size: 14px;
  967. font-style: normal;
  968. font-weight: 400;
  969. color: #157a2c;
  970. padding-left: 5px;
  971. position: relative;
  972. &::before {
  973. position: absolute;
  974. content: '';
  975. left: 0;
  976. top: 0;
  977. bottom: 0;
  978. width: 2px;
  979. height: 14px;
  980. background: #157a2c;
  981. margin: auto;
  982. }
  983. }
  984. }
  985. /* 工单信息表格 */
  986. .material {
  987. margin-top: 6px;
  988. .content_table {
  989. width: 100%;
  990. max-width: 100%;
  991. box-sizing: border-box;
  992. border: 1px solid #e3e5e5;
  993. overflow: hidden;
  994. .item {
  995. display: flex;
  996. min-width: 0;
  997. border-bottom: 1px solid #e3e5e5;
  998. &:last-child {
  999. border-bottom: none;
  1000. }
  1001. .rx {
  1002. display: flex;
  1003. flex: 1;
  1004. min-width: 0;
  1005. }
  1006. /* 三等分:避免 33.33%×3 舍入与 flex-shrink:0 撑破卡片 */
  1007. .ww16 {
  1008. flex: 1 1 0;
  1009. min-width: 0;
  1010. }
  1011. .lable {
  1012. width: 100px;
  1013. min-width: 100px;
  1014. flex-shrink: 0;
  1015. text-align: center;
  1016. background-color: #f7f9fa;
  1017. font-size: 12px;
  1018. border-right: 1px solid #e3e5e5;
  1019. padding: 8px 4px;
  1020. box-sizing: border-box;
  1021. }
  1022. .content {
  1023. flex: 1;
  1024. min-width: 0;
  1025. min-height: 32px;
  1026. font-size: 13px;
  1027. padding: 8px 10px;
  1028. box-sizing: border-box;
  1029. background: #fff;
  1030. border-right: 1px solid #e3e5e5;
  1031. white-space: nowrap;
  1032. overflow: hidden;
  1033. text-overflow: ellipsis;
  1034. &:last-child {
  1035. border-right: none;
  1036. }
  1037. }
  1038. }
  1039. }
  1040. }
  1041. /* 交接信息表单:三列等宽网格,与第一行对齐;避免 space-between 拉散两列行 */
  1042. .handover_form {
  1043. .form_row_full {
  1044. margin-bottom: 12px;
  1045. }
  1046. .form_row {
  1047. display: grid;
  1048. grid-template-columns: repeat(3, minmax(0, 1fr));
  1049. column-gap: 16px;
  1050. margin-bottom: 12px;
  1051. align-items: start;
  1052. }
  1053. .form_item {
  1054. width: 100%;
  1055. min-width: 0;
  1056. margin-bottom: 0;
  1057. display: flex;
  1058. align-items: center;
  1059. ::v-deep .el-form-item__label {
  1060. padding-right: 8px;
  1061. }
  1062. ::v-deep .el-form-item__content {
  1063. flex: 1;
  1064. min-width: 0;
  1065. margin-left: 0 !important;
  1066. }
  1067. ::v-deep .el-select,
  1068. ::v-deep .el-date-editor.el-input {
  1069. width: 100% !important;
  1070. }
  1071. ::v-deep .el-select .el-input__inner,
  1072. ::v-deep .el-date-editor .el-input__inner {
  1073. overflow: hidden;
  1074. text-overflow: ellipsis;
  1075. white-space: nowrap;
  1076. }
  1077. }
  1078. .form_item_empty {
  1079. min-width: 0;
  1080. }
  1081. .form_item_full {
  1082. width: 100%;
  1083. margin-bottom: 0;
  1084. }
  1085. }
  1086. .material_table {
  1087. margin-top: 8px;
  1088. }
  1089. .receive_card {
  1090. .receive_form {
  1091. margin-top: 4px;
  1092. }
  1093. .receive_actions {
  1094. display: flex;
  1095. gap: 12px;
  1096. padding-left: 100px;
  1097. margin-top: 4px;
  1098. }
  1099. }
  1100. </style>