pda-cli/src/pages/storefabric/storeFabricBusinessOutMixin.js
xuan 567da03aea feat(storefabric): 新增坯布出库相关页面及功能
- 在 manifest.json 中添加 Android 包名配置
- 在 package.json 中添加 pnpm 依赖覆盖配置
- 在 pages.json 中注册坯布出库相关页面路由
- 重命名 storefabricBusinessOutAdd.vue 为 storeFabricBusinessOutAdd.vue
- 重构坯布出库新增页面,优化界面布局和交互逻辑
- 添加坯布出库列表、编辑、查看等功能页面
- 集成订单状态栏、面料信息弹窗、细码弹窗等组件
- 实现坯布出库详情列表和扫码功能模块
- 优化表单验证和数据提交流程
2026-06-17 16:59:31 +08:00

405 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 坯布出仓单(其他出货单)表单 mixin新增/编辑共用)
*
* 业务说明:
* 1. 后端当前仅提供「坯布其他出货单」一种类型的接口,故出仓类型固定为 other
* 表头保留接收单位(business_unit) / 出货日期 / 备注 等字段。
* 2. 用户先填写表头并「提交保存」(addGfmOtherDeliveryOrder),拿到出仓单 id 后即可扫码;
* 扫码新增/删除细码走 updateGfmOtherDeliveryOrder勾选锁定维度时附带 *_lock 字段,操作后用返回的最新详情刷新。
* 3. 后端没有「修改表头」接口,保存成功或编辑进入后表头变为只读,仅可扫码维护细码。
*/
import util from '@/common/util';
import scanMixin from '@/common/scanMixin.js';
import {
AUDIT_STATUS_MAP,
BILL_TYPES,
normalizeOrder,
mapToSelectOptions,
getFineCodeScanCode,
canScanAtStatus,
isLockedOrderStatus,
getFormStatusActionButtons,
orderToFormFields,
confirmOrderStatusAction,
DRAFT_AUDIT_STATUS,
DRAFT_AUDIT_STATUS_NAME,
getEmptyScanDetail,
findScanContext,
buildScanDetailFromContext,
buildScanDetailFromOrderResponse,
buildScanUpdateParams,
getOrderDisplayWeight,
getFineCodeDisplayWeight,
} from '@/common/storeFabricBusinessOut';
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: '',
currentScanItem: null,
fabricInfoShow: false,
fineCodeShow: false,
currentFabricInfo: {},
currentFineCodes: [],
currentGroupKey: '',
scanDetail: getEmptyScanDetail(),
scanGroupKeys: [],
lastScanCode: '',
billTypeName: (BILL_TYPES[0] || {}).label || '坯布其他出货单',
auditStatus: 0,
businessUnitOptions: [],
dropdownLoading: false,
};
},
computed: {
isReadonly() {
return this.pageMode === 'view';
},
isEditable() {
return this.pageMode === 'add' || this.pageMode === 'edit';
},
// 表头是否可编辑:仅新增且未保存时可编辑(后端无修改表头接口)
headerEditable() {
return this.pageMode === 'add' && !this.headerSaved;
},
dateLabel() {
return '出货日期';
},
/** 表头保存后,待审核/已驳回状态下显示审核、作废 */
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] || '';
},
},
onShow() {
this.isPageActive = true;
// #ifdef APP-PLUS
if (this.canScan && this.isEditable) {
this.registerScanBroadcast((scanResult) => {
this.QRBarCode = scanResult.trim().replace(/[\r\n]/g, '');
this.$nextTick(() => this.handleScan());
});
}
// #endif
},
onHide() {
this.isPageActive = false;
// #ifdef APP-PLUS
this.unregisterScanBroadcast();
// #endif
},
onUnload() {
this.isPageActive = false;
// #ifdef APP-PLUS
this.unregisterScanBroadcast();
// #endif
},
methods: {
registerOrderScanBroadcast() {
// #ifdef APP-PLUS
this.registerScanBroadcast((scanResult) => {
this.QRBarCode = scanResult.trim().replace(/[\r\n]/g, '');
this.$nextTick(() => this.handleScan());
});
// #endif
},
syncCanScanFromStatus(status) {
const nextCanScan = this.isEditable && canScanAtStatus(status);
if (this.canScan && !nextCanScan) {
// #ifdef APP-PLUS
this.unregisterScanBroadcast();
// #endif
}
this.canScan = nextCanScan;
},
loadBaseDropdowns() {
if (this.dropdownLoading) return Promise.resolve();
if (this.businessUnitOptions.length) {
return Promise.resolve();
}
this.dropdownLoading = true;
return this.ensureBusinessUnitOptions().finally(() => {
this.dropdownLoading = false;
});
},
ensureBusinessUnitOptions() {
if (this.businessUnitOptions.length) return Promise.resolve(this.businessUnitOptions);
return this.$u.api.businessUnit.list({})
.then((res) => {
this.businessUnitOptions = mapToSelectOptions(res);
return this.businessUnitOptions;
});
},
getEmptyForm() {
return {
order_no: '',
business_unit_id: '',
business_unit_name: '',
delivery_time: this.$u.timeFormat(new Date(), 'yyyy-mm-dd'),
remark: '',
item_data: [],
};
},
loadOrder(id, options = {}) {
const { rejectIfLocked = false } = options;
return this.$u.api.gfmOtherDeliveryOrder.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.refreshScanDetailTotals(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.ensureBusinessUnitOptions()
.then(openPicker)
.catch((e) => this.showError(e.message || '加载单位列表失败'));
}
},
selectConfirmFun(e) {
const item = e[0];
if (this.selectType === '接收单位') {
this.form.business_unit_id = item.value;
this.form.business_unit_name = item.label;
}
},
bindDateChange(e) {
this.form.delivery_time = e.detail.value;
},
validateForm() {
if (!this.form.business_unit_id) return '请选择接收单位';
if (!this.form.delivery_time) return '请选择出货日期';
return '';
},
buildAddParam() {
return {
business_unit_id: Number(this.form.business_unit_id),
delivery_time: this.form.delivery_time,
remark: this.form.remark || '',
};
},
submitSave() {
if (this.headerSaved) {
this.showError('表头已保存,请直接扫码维护细码');
return;
}
const err = this.validateForm();
if (err) {
this.showError(err);
return;
}
uni.showLoading({ title: '保存中...' });
this.$u.api.gfmOtherDeliveryOrder.add(this.buildAddParam())
.then((res) => {
this.orderId = (res && res.id) || 0;
if (!this.orderId) {
throw new Error('保存失败未返回单据ID');
}
return this.$u.api.gfmOtherDeliveryOrder.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));
this.refreshScanDetailTotals(order);
},
refreshScanDetailTotals(order) {
this.scanDetail = {
...this.scanDetail,
total_roll: order.total_roll ?? 0,
total_weight: getOrderDisplayWeight(order),
};
},
applyScanDetail(scanCode, order, scanType) {
const fromResponse = buildScanDetailFromOrderResponse(order);
if (fromResponse) {
const context = findScanContext(order.item_data, scanCode);
if (context) {
fromResponse.weight = getFineCodeDisplayWeight(context.fineCode);
}
this.scanDetail = fromResponse;
if (scanType !== 2) this.lastScanCode = scanCode;
else if (this.lastScanCode === scanCode) this.lastScanCode = '';
return;
}
const context = findScanContext(order.item_data, scanCode);
if (context) {
this.scanDetail = buildScanDetailFromContext(context, order);
this.lastScanCode = scanCode;
return;
}
if (scanType === 2 && this.lastScanCode === scanCode) {
this.scanDetail = {
...getEmptyScanDetail(),
total_roll: order.total_roll ?? 0,
total_weight: getOrderDisplayWeight(order),
};
this.lastScanCode = '';
return;
}
this.refreshScanDetailTotals(order);
},
handleScan() {
const code = (this.QRBarCode || '').trim();
if (!this.canScan || !this.orderId) {
this.showError('请先提交保存后再扫码');
this.QRBarCode = '';
return;
}
if (!code) {
this.QRBarCode = '';
return;
}
const scanType = this.BarCodeDelStatus ? 2 : 1;
this.doScanUpdate(code, scanType);
},
BarCodeDelChange(e) {
this.BarCodeDelStatus = (e.detail.value || []).length > 0;
},
/** 扫码新增/删除细码,统一走 scanUpdate 接口 */
doScanUpdate(scanCode, scanType, options = {}) {
const { refreshFineCodes = false } = options;
uni.showLoading({ title: scanType === 2 ? '删除中...' : '处理中...' });
return this.$u.api.gfmOtherDeliveryOrder.scanUpdate(buildScanUpdateParams({
id: this.orderId,
scanCode,
scanType,
groupKeys: this.scanGroupKeys,
detail: this.scanDetail,
}))
.then((detail) => {
uni.hideLoading();
const order = normalizeOrder(detail);
this.form.item_data = order.item_data;
this.auditStatus = order.audit_status;
this.applyScanDetail(scanCode, order, scanType);
if (refreshFineCodes) {
const row = order.item_data.find((i) => (i.group_key || String(i.id)) === this.currentGroupKey);
this.currentFineCodes = row ? [...row.fine_codes] : [];
}
this.BillDataMessage = scanType === 2 ? '删除成功' : '扫描成功';
this.QRBarCode = '';
if (scanType === 2) this.showSuccess('删除成功');
else util.playSuccessAudio();
})
.catch((e) => {
uni.hideLoading();
this.showError(e.message || (scanType === 2 ? '删除失败' : '扫码失败'));
this.QRBarCode = '';
});
},
openFabricInfo(row) {
this.currentFabricInfo = { ...row };
this.fabricInfoShow = true;
},
openFineCode(row) {
this.currentGroupKey = row.group_key || String(row.id);
this.currentFineCodes = [...(row.fine_codes || [])];
this.fineCodeShow = true;
},
onDeleteFineCode(fineCode) {
if (this.isReadonly || !this.canScan) return;
const scanCode = getFineCodeScanCode(fineCode);
if (!scanCode) {
this.showError('该细码缺少条码,无法删除');
return;
}
uni.showModal({
title: '提示',
content: `确认删除细码 ${scanCode}`,
success: (res) => {
if (!res.confirm) return;
this.doScanUpdate(scanCode, 2, { refreshFineCodes: true });
},
});
},
handleStatusAction(action) {
confirmOrderStatusAction(this, {
orderId: this.orderId,
action,
onSuccess: () => this.$u.api.gfmOtherDeliveryOrder.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' });
},
getBillTypeName() {
return this.billTypeName;
},
},
};