/** * 加工出仓单表单 mixin(新增/编辑共用) * * 1. 先填表头(加工单位 / 仓库 / 备注)提交 add,拿到 id 后可扫码 * 2. 扫码走 updateFpmProcessOutOrder(arrange_type: 1 录入 / 3 删除),不扫仓位 * 3. 手机扫码成功展示详情后自动再次调起扫码,形成连续扫 */ import util from '@/common/util'; import scanMixin from '@/common/scanMixin.js'; import { initTts, speak } from '@/common/tts.js'; import { AUDIT_STATUS_MAP, ARRANGE_TYPE, BUSINESS_UNIT_TYPE_PROCESS, WAREHOUSE_TYPE_FINISHED, OUT_ORDER_TYPE_PROCESS, OUT_ORDER_TYPE_PROCESS_NAME, normalizeOrder, mapToSelectOptions, canScanAtStatus, isLockedOrderStatus, getFormStatusActionButtons, orderToFormFields, confirmOrderStatusAction, DRAFT_AUDIT_STATUS, DRAFT_AUDIT_STATUS_NAME, getEmptyScanDetail, buildScanDetailFromScanResponse, buildScanUpdateParams, pickDefaultWarehouse, formatDisplayTime, } from '@/common/storeGoodsProcessOut'; export default { mixins: [scanMixin], data() { return { pageMode: 'add', orderId: 0, form: this.getEmptyForm(), headerSaved: false, selectShow: false, selectList: [], selectType: '', canScan: false, QRBarCode: '', BarCodeDelStatus: false, BillDataMessage: '', scanDetail: getEmptyScanDetail(), auditStatus: 0, processUnitOptions: [], warehouseOptions: [], outOrderTypeOptions: [], dropdownLoading: false, /** 手机连续扫码开关:成功后自动再调起;取消/失败则关闭 */ phoneContinuousScan: false, }; }, computed: { isReadonly() { return this.pageMode === 'view'; }, isEditable() { return this.pageMode === 'add' || this.pageMode === 'edit'; }, headerEditable() { return this.pageMode === 'add' && !this.headerSaved; }, statusActionButtons() { return getFormStatusActionButtons(this.headerSaved, this.orderId, this.auditStatus); }, displayAuditStatus() { if (this.pageMode === 'add' && !this.headerSaved) return DRAFT_AUDIT_STATUS; return this.auditStatus; }, displayAuditStatusName() { if (this.pageMode === 'add' && !this.headerSaved) return DRAFT_AUDIT_STATUS_NAME; return AUDIT_STATUS_MAP[this.auditStatus] || ''; }, canScanQr() { return this.canScan; }, showQrScanBar() { return this.canScan; }, qrDisabledTip() { if (!this.canScan) return '请先提交保存后再扫码'; return '当前状态不可扫码'; }, }, onShow() { this.isPageActive = true; initTts(); if (this.canScan && this.isEditable) { this.registerOrderScanBroadcast(); } }, onHide() { this.isPageActive = false; this.phoneContinuousScan = false; this.unregisterScanBroadcast(); }, onUnload() { this.isPageActive = false; this.phoneContinuousScan = false; this.unregisterScanBroadcast(); }, methods: { handlePhoneScan() { if (!this.canScan || !this.orderId) { this.showError('请先提交保存后再扫码'); return; } this.phoneContinuousScan = true; this.openContinuousPhoneScan(); }, openContinuousPhoneScan() { if (!this.phoneContinuousScan || !this.canScanQr || !this.isPageActive) { this.phoneContinuousScan = false; return; } this.openPhoneScan({ callback: (scanResult) => { this.QRBarCode = String(scanResult || '').trim().replace(/[\r\n]/g, ''); this.$nextTick(() => this.handleScan({ fromPhone: true })); }, fail: () => { this.phoneContinuousScan = false; }, }); }, scheduleNextPhoneScan() { if (!this.phoneContinuousScan || !this.canScanQr || !this.isPageActive) { this.phoneContinuousScan = false; return; } setTimeout(() => { this.openContinuousPhoneScan(); }, 400); }, registerOrderScanBroadcast() { this.registerScanBroadcast((scanResult) => { const code = String(scanResult || '').trim().replace(/[\r\n]/g, ''); if (!code) return; this.QRBarCode = code; this.$nextTick(() => this.handleScan()); }); }, syncCanScanFromStatus(status) { const nextCanScan = this.isEditable && canScanAtStatus(status); if (this.canScan && !nextCanScan) { this.phoneContinuousScan = false; this.unregisterScanBroadcast(); } this.canScan = nextCanScan; }, loadBaseDropdowns() { if (this.dropdownLoading) return Promise.resolve(); this.dropdownLoading = true; return Promise.all([ this.ensureOutOrderTypeOptions().catch((e) => { console.error('加载出仓类型失败', e); return []; }), this.ensureProcessUnitOptions().catch((e) => { console.error('加载加工单位失败', e); return []; }), this.ensureWarehouseOptions().catch((e) => { console.error('加载仓库失败', e); return []; }), ]).finally(() => { this.dropdownLoading = false; }); }, ensureOutOrderTypeOptions() { if (this.outOrderTypeOptions.length) return Promise.resolve(this.outOrderTypeOptions); return this.$u.api.fpmProcessOutOrder.getOutTypeEnum() .then((res) => { this.outOrderTypeOptions = mapToSelectOptions(res); if (this.headerEditable && !this.form.out_order_type) { this.applyDefaultOutOrderType(); } return this.outOrderTypeOptions; }); }, applyDefaultOutOrderType() { const list = this.outOrderTypeOptions || []; const preferred = list.find((item) => Number(item.value) === OUT_ORDER_TYPE_PROCESS) || list[0]; if (!preferred) return; this.form.out_order_type = preferred.value; this.form.out_order_type_name = preferred.label; }, ensureProcessUnitOptions() { if (this.processUnitOptions.length) return Promise.resolve(this.processUnitOptions); return this.$u.api.businessUnit.list({ unit_type_id: BUSINESS_UNIT_TYPE_PROCESS }) .then((res) => { this.processUnitOptions = mapToSelectOptions(res); return this.processUnitOptions; }); }, ensureWarehouseOptions() { if (this.warehouseOptions.length) return Promise.resolve(this.warehouseOptions); return this.$u.api.physicalWarehouse.getDropdownList({ warehouse_type_id: WAREHOUSE_TYPE_FINISHED }) .then((res) => { this.warehouseOptions = mapToSelectOptions(res); if (this.headerEditable && !this.form.warehouse_id) { this.applyDefaultWarehouse(); } return this.warehouseOptions; }); }, applyDefaultWarehouse() { const app = getApp(); const picked = pickDefaultWarehouse( this.warehouseOptions, app && app.globalData ? app.globalData.StoreNameID : '', app && app.globalData ? app.globalData.StoreName : '', ); if (!picked) return; this.form.warehouse_id = picked.value; this.form.warehouse_name = picked.label; }, getEmptyForm() { return { order_no: '', out_order_type: OUT_ORDER_TYPE_PROCESS, out_order_type_name: OUT_ORDER_TYPE_PROCESS_NAME, process_unit_id: '', process_name: '', warehouse_id: '', warehouse_name: '', remark: '', item_data: [], }; }, loadOrder(id, options = {}) { const { rejectIfLocked = false } = options; return this.$u.api.fpmProcessOutOrder.detail({ id }) .then((res) => { const order = normalizeOrder(res); if (!order) { this.showError('单据不存在'); return null; } if (rejectIfLocked && isLockedOrderStatus(order.audit_status)) { this.showError('当前状态不可编辑'); setTimeout(() => uni.navigateBack(), 1500); return null; } this.orderId = order.id; this.auditStatus = order.audit_status; this.headerSaved = true; this.form = orderToFormFields(order); this.syncCanScanFromStatus(order.audit_status); if (this.canScan) this.registerOrderScanBroadcast(); return order; }) .catch((e) => { this.showError(e.message || '加载单据失败'); return null; }); }, pickerSelectFun(type) { if (!this.headerEditable) return; this.selectType = type; const openPicker = (list) => { if (!list.length) { this.showError('暂无可选数据'); return; } this.selectList = list; this.selectShow = true; }; if (type === '出仓类型') { this.ensureOutOrderTypeOptions() .then(openPicker) .catch((e) => this.showError(e.message || '加载出仓类型失败')); } else if (type === '加工单位') { this.ensureProcessUnitOptions() .then(openPicker) .catch((e) => this.showError(e.message || '加载加工单位失败')); } else if (type === '仓库名称') { this.ensureWarehouseOptions() .then(openPicker) .catch((e) => this.showError(e.message || '加载仓库失败')); } }, selectConfirmFun(e) { const item = e[0]; if (this.selectType === '出仓类型') { this.form.out_order_type = item.value; this.form.out_order_type_name = item.label; } else if (this.selectType === '加工单位') { this.form.process_unit_id = item.value; this.form.process_name = item.label; } else if (this.selectType === '仓库名称') { this.form.warehouse_id = item.value; this.form.warehouse_name = item.label; } }, validateForm() { if (!this.form.out_order_type) return '请选择出仓类型'; if (!this.form.process_unit_id) return '请选择加工单位'; if (!this.form.warehouse_id) return '请选择仓库名称'; const app = getApp(); const saleSystemId = app && app.globalData ? app.globalData.PlanDepartmentID : ''; if (!saleSystemId) return '缺少营销体系,请重新登录'; return ''; }, buildAddParam() { const app = getApp(); return { // OpenAPI: product.AddFpmProcessOutOrderParam out_order_type: Number(this.form.out_order_type), process_unit_id: Number(this.form.process_unit_id), warehouse_id: Number(this.form.warehouse_id), sale_system_id: Number(app.globalData.PlanDepartmentID), warehouse_out_time: formatDisplayTime(new Date(), 'YYYY-MM-DD'), remark: this.form.remark || '', item_data: [], }; }, submitSave() { if (this.headerSaved) { this.showError('表头已保存,请直接扫码'); return; } const err = this.validateForm(); if (err) { this.showError(err); return; } uni.showLoading({ title: '保存中...' }); this.$u.api.fpmProcessOutOrder.add(this.buildAddParam()) .then((res) => { this.orderId = (res && res.id) || 0; if (!this.orderId) { throw new Error('保存失败,未返回单据ID'); } return this.$u.api.fpmProcessOutOrder.detail({ id: this.orderId }); }) .then((detail) => { uni.hideLoading(); const order = normalizeOrder(detail); this.applyOrder(order); this.headerSaved = true; this.syncCanScanFromStatus(order.audit_status); this.showSuccess('保存成功,请开始扫码'); this.registerOrderScanBroadcast(); }) .catch((e) => { uni.hideLoading(); this.showError(e.message || '保存失败'); }); }, applyOrder(order) { if (!order) return; this.auditStatus = order.audit_status; Object.assign(this.form, orderToFormFields(order)); }, handleScan(options = {}) { const fromPhone = !!options.fromPhone; const code = (this.QRBarCode || '').trim(); if (!this.canScan || !this.orderId) { this.showError('请先提交保存后再扫码'); this.QRBarCode = ''; this.phoneContinuousScan = false; return; } if (!code) { this.QRBarCode = ''; if (fromPhone) this.scheduleNextPhoneScan(); return; } const arrangeType = this.BarCodeDelStatus ? ARRANGE_TYPE.DEL : ARRANGE_TYPE.IN; this.doScanUpdate(code, arrangeType, { fromPhone }); }, BarCodeDelChange(e) { this.BarCodeDelStatus = (e.detail.value || []).length > 0; this.BillDataMessage = this.BarCodeDelStatus ? '删除模式:扫描细码即可删除' : ''; }, getFineCodeScanCode(fineCode) { if (!fineCode) return ''; return fineCode.qr_code || fineCode.scan_code || fineCode.digital_code || fineCode.bar_code || ''; }, onDeleteFineCode(fineCode) { if (!this.canScan || !this.orderId) { this.showError('当前状态不可删除'); return; } const scanCode = this.getFineCodeScanCode(fineCode); if (!scanCode) { this.showError('该细码缺少条码,无法删除'); return; } uni.showModal({ title: '提示', content: `确认删除细码 ${scanCode}?`, success: (res) => { if (!res.confirm) return; this.doScanUpdate(scanCode, ARRANGE_TYPE.DEL); }, }); }, doScanUpdate(scanCode, arrangeType, options = {}) { const { fromPhone = false } = options; const isDelete = arrangeType === ARRANGE_TYPE.DEL; uni.showLoading({ title: isDelete ? '删除中...' : '处理中...' }); return this.$u.api.fpmProcessOutOrder.scanUpdate(buildScanUpdateParams({ id: this.orderId, scanCode, arrangeType, })) .then((scanRes) => { this.scanDetail = buildScanDetailFromScanResponse(scanRes); this.BillDataMessage = isDelete ? '删除成功' : '扫描成功'; this.QRBarCode = ''; if (isDelete) { speak('已删除'); uni.showToast({ title: '删除成功', icon: 'success' }); } else { speak('成功'); } return this.$u.api.fpmProcessOutOrder.detail({ id: this.orderId }) .then((detail) => { const order = normalizeOrder(detail); if (order) { this.form.item_data = order.item_data; this.auditStatus = order.audit_status; } }) .catch(() => null); }) .then(() => { uni.hideLoading(); if (fromPhone) this.scheduleNextPhoneScan(); }) .catch((e) => { uni.hideLoading(); this.phoneContinuousScan = false; const message = e.message || (isDelete ? '删除失败' : '扫码失败'); this.showError(message); this.QRBarCode = ''; }); }, handleStatusAction(action) { confirmOrderStatusAction(this, { orderId: this.orderId, action, onSuccess: () => this.$u.api.fpmProcessOutOrder.detail({ id: this.orderId }) .then((detail) => { uni.hideLoading(); const order = normalizeOrder(detail); if (!order) return; this.applyOrder(order); this.syncCanScanFromStatus(order.audit_status); this.showSuccess('操作成功'); }), }); }, showError(message) { util.playErrorAudio(); uni.showModal({ title: '提示', content: message, showCancel: false }); }, showSuccess(message) { util.playSuccessAudio(); uni.showToast({ title: message, icon: 'success' }); }, }, };