456 lines
15 KiB
JavaScript
456 lines
15 KiB
JavaScript
/**
|
||
* 加工进仓单常量与数据工具
|
||
*
|
||
* 真实接口请求统一注册在 src/common/http.api.js 的 vm.$u.api.fpmProcessInOrder 下。
|
||
*/
|
||
import dayjs from 'dayjs';
|
||
|
||
export const AUDIT_STATUS = {
|
||
PENDING: 1,
|
||
APPROVED: 2,
|
||
REJECTED: 3,
|
||
VOIDED: 4,
|
||
};
|
||
|
||
export const DRAFT_AUDIT_STATUS = 0;
|
||
export const DRAFT_AUDIT_STATUS_NAME = '待保存';
|
||
|
||
export const AUDIT_STATUS_MAP = {
|
||
1: '待审核',
|
||
2: '已审核',
|
||
3: '已驳回',
|
||
4: '已作废',
|
||
};
|
||
|
||
/** 操作类型:1录入 3删除 */
|
||
export const ARRANGE_TYPE = {
|
||
IN: 1,
|
||
DEL: 3,
|
||
};
|
||
|
||
/** 入仓类型:加工进仓单 */
|
||
export const IN_ORDER_TYPE_PROCESS = 5;
|
||
|
||
/** 入仓类型:返修进仓 */
|
||
export const IN_ORDER_TYPE_REPAIR = 6;
|
||
|
||
export const IN_ORDER_TYPE_MAP = {
|
||
[IN_ORDER_TYPE_PROCESS]: '加工进仓单',
|
||
[IN_ORDER_TYPE_REPAIR]: '返修进仓',
|
||
};
|
||
|
||
export const IN_ORDER_TYPE_OPTIONS = [
|
||
{ value: IN_ORDER_TYPE_PROCESS, label: IN_ORDER_TYPE_MAP[IN_ORDER_TYPE_PROCESS] },
|
||
{ value: IN_ORDER_TYPE_REPAIR, label: IN_ORDER_TYPE_MAP[IN_ORDER_TYPE_REPAIR] },
|
||
];
|
||
|
||
/** 默认纸筒重量(公斤,表单展示值) */
|
||
export const DEFAULT_PAPER_TUBE_WEIGHT = 1.5;
|
||
|
||
/** 默认包装重量(公斤,表单展示值) */
|
||
export const DEFAULT_PACKAGE_WEIGHT = 0.3;
|
||
|
||
/** 往来单位类型:加工单位 */
|
||
export const BUSINESS_UNIT_TYPE_PROCESS = 12;
|
||
|
||
/** 成品物理仓类型 */
|
||
export const WAREHOUSE_TYPE_FINISHED = 103;
|
||
|
||
/** 默认仓库名称 */
|
||
export const DEFAULT_WAREHOUSE_NAME = '成品仓';
|
||
|
||
export const LIST_PAGE_SIZE = 100;
|
||
|
||
export const STATUS_ACTION_CONFIG = {
|
||
approve: { status: AUDIT_STATUS.APPROVED, msg: '确认审核该单据?', apiKey: 'statusPass' },
|
||
void: { status: AUDIT_STATUS.VOIDED, msg: '确认作废该单据?', apiKey: 'statusCancel' },
|
||
unapprove: { status: AUDIT_STATUS.PENDING, msg: '确认消审该单据?', apiKey: 'statusWait' },
|
||
};
|
||
|
||
export function getStatusOptions() {
|
||
return Object.keys(AUDIT_STATUS_MAP).map((value) => ({
|
||
value,
|
||
label: AUDIT_STATUS_MAP[value],
|
||
}));
|
||
}
|
||
|
||
export function mapToSelectOptions(data) {
|
||
const list = Array.isArray(data) ? data : (data && (data.list || data.items || data.records)) || [];
|
||
return list.map((item) => ({
|
||
value: item.id,
|
||
label: item.name || item.label || item.code || String(item.id),
|
||
}));
|
||
}
|
||
|
||
/** 匹数:后端 ×100 存储 */
|
||
export function formatRoll(value) {
|
||
const n = Number(value || 0) / 100;
|
||
return Number.isInteger(n) ? n : Number(n.toFixed(2));
|
||
}
|
||
|
||
/** 数量(公斤):后端 ×10000 存储 */
|
||
export function formatWeightKg(value) {
|
||
const n = Number(value || 0) / 10000;
|
||
return Number(n.toFixed(2));
|
||
}
|
||
|
||
/** 公斤展示值转后端存储(×10000) */
|
||
export function toWeightStorage(value) {
|
||
return Math.round(Number(value || 0) * 10000);
|
||
}
|
||
|
||
export function normalizeOrder(order) {
|
||
if (!order) return null;
|
||
return {
|
||
id: order.id,
|
||
order_no: order.order_no || '',
|
||
audit_status: order.audit_status,
|
||
audit_status_name: order.audit_status_name || AUDIT_STATUS_MAP[order.audit_status] || '',
|
||
process_unit_id: order.process_unit_id || '',
|
||
process_name: order.process_name || '',
|
||
warehouse_id: order.warehouse_id || '',
|
||
warehouse_name: order.warehouse_name || '',
|
||
sale_system_id: order.sale_system_id || '',
|
||
sale_system_name: order.sale_system_name || '',
|
||
in_order_type: order.in_order_type || IN_ORDER_TYPE_PROCESS,
|
||
in_order_type_name: order.in_order_type_name
|
||
|| IN_ORDER_TYPE_MAP[order.in_order_type]
|
||
|| IN_ORDER_TYPE_MAP[IN_ORDER_TYPE_PROCESS],
|
||
paper_tube_weight: formatWeightKg(order.paper_tube_weight),
|
||
package_weight: formatWeightKg(order.package_weight),
|
||
remark: order.remark || '',
|
||
warehouse_in_time: (order.warehouse_in_time || '').slice(0, 10),
|
||
creator_name: order.creator_name || '',
|
||
create_time: order.create_time || '',
|
||
auditor_name: order.auditor_name || '',
|
||
audit_time: order.audit_time || '',
|
||
total_roll: formatRoll(order.total_roll),
|
||
total_weight: formatWeightKg(order.total_weight),
|
||
total_length: formatRoll(order.total_length),
|
||
item_data: (order.item_data || []).map(normalizeItem),
|
||
_raw: order,
|
||
};
|
||
}
|
||
|
||
export function normalizeFineCode(fc) {
|
||
if (!fc) return null;
|
||
const weightRaw = fc.base_unit_weight != null
|
||
? fc.base_unit_weight
|
||
: (fc.settle_weight != null ? fc.settle_weight : fc.weight);
|
||
const qrCode = fc.qr_code || fc.long_code || '';
|
||
const barCode = fc.bar_code || fc.digital_code || fc.short_code || '';
|
||
return {
|
||
id: fc.id,
|
||
volume_number: fc.volume_number != null && fc.volume_number !== '' ? String(fc.volume_number) : '',
|
||
roll: formatRoll(fc.roll),
|
||
weight: formatWeightKg(weightRaw),
|
||
bar_code: barCode,
|
||
qr_code: qrCode,
|
||
digital_code: fc.digital_code || '',
|
||
long_code: fc.long_code || '',
|
||
short_code: fc.short_code || '',
|
||
dye_factory_dyelot_number: fc.dye_factory_dyelot_number || fc.dyelot_number || '',
|
||
warehouse_bin_name: fc.warehouse_bin_name || '',
|
||
shelf_no: fc.shelf_no || '',
|
||
scan_code: qrCode || barCode || '',
|
||
paper_tube_weight: fc.paper_tube_weight != null && fc.paper_tube_weight !== ''
|
||
? formatWeightKg(fc.paper_tube_weight)
|
||
: null,
|
||
package_weight: fc.package_weight != null && fc.package_weight !== ''
|
||
? formatWeightKg(fc.package_weight)
|
||
: null,
|
||
};
|
||
}
|
||
|
||
export function normalizeItem(item) {
|
||
return {
|
||
id: item.id,
|
||
quote_order_no: item.quote_order_no || '',
|
||
product_code: item.product_code || '',
|
||
product_name: item.product_name || '',
|
||
product_color_code: item.product_color_code || '',
|
||
product_color_name: item.product_color_name || '',
|
||
dye_factory_dyelot_number: item.dye_factory_dyelot_number || '',
|
||
dye_factory_color_code: item.dye_factory_color_code || '',
|
||
in_roll: formatRoll(item.in_roll),
|
||
in_weight: formatWeightKg(item.in_weight),
|
||
item_fc_data: (item.item_fc_data || []).map(normalizeFineCode).filter(Boolean),
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 明细分组:成品编号 → 成品色号 → 细码
|
||
*/
|
||
export function buildItemGroups(items) {
|
||
const productMap = new Map();
|
||
(items || []).forEach((item) => {
|
||
const productKey = item.product_code || '-';
|
||
if (!productMap.has(productKey)) {
|
||
productMap.set(productKey, {
|
||
group_key: `p:${productKey}`,
|
||
product_code: item.product_code || '',
|
||
product_name: item.product_name || '',
|
||
in_roll: 0,
|
||
in_weight: 0,
|
||
colors: new Map(),
|
||
});
|
||
}
|
||
const productGroup = productMap.get(productKey);
|
||
if (!productGroup.product_name && item.product_name) {
|
||
productGroup.product_name = item.product_name;
|
||
}
|
||
|
||
const colorKey = item.product_color_code || item.product_color_name || '-';
|
||
if (!productGroup.colors.has(colorKey)) {
|
||
productGroup.colors.set(colorKey, {
|
||
group_key: `p:${productKey}|c:${colorKey}`,
|
||
product_color_code: item.product_color_code || '',
|
||
product_color_name: item.product_color_name || '',
|
||
quote_order_no: item.quote_order_no || '',
|
||
dye_factory_dyelot_number: item.dye_factory_dyelot_number || '',
|
||
in_roll: 0,
|
||
in_weight: 0,
|
||
fine_codes: [],
|
||
});
|
||
}
|
||
const colorGroup = productGroup.colors.get(colorKey);
|
||
productGroup.in_roll = Number((productGroup.in_roll + Number(item.in_roll || 0)).toFixed(2));
|
||
productGroup.in_weight = Number((productGroup.in_weight + Number(item.in_weight || 0)).toFixed(2));
|
||
colorGroup.in_roll = Number((colorGroup.in_roll + Number(item.in_roll || 0)).toFixed(2));
|
||
colorGroup.in_weight = Number((colorGroup.in_weight + Number(item.in_weight || 0)).toFixed(2));
|
||
if (item.quote_order_no) colorGroup.quote_order_no = item.quote_order_no;
|
||
if (item.dye_factory_dyelot_number) {
|
||
colorGroup.dye_factory_dyelot_number = item.dye_factory_dyelot_number;
|
||
}
|
||
if (!colorGroup.product_color_name && item.product_color_name) {
|
||
colorGroup.product_color_name = item.product_color_name;
|
||
}
|
||
colorGroup.fine_codes.push(...(item.item_fc_data || []));
|
||
});
|
||
|
||
return Array.from(productMap.values()).map((product) => ({
|
||
group_key: product.group_key,
|
||
product_code: product.product_code,
|
||
product_name: product.product_name,
|
||
in_roll: product.in_roll,
|
||
in_weight: product.in_weight,
|
||
colors: Array.from(product.colors.values()),
|
||
}));
|
||
}
|
||
|
||
export function getEmptyScanDetail() {
|
||
return {
|
||
quote_order_no: '',
|
||
dyelot_number: '',
|
||
volume_number: '',
|
||
product_code: '',
|
||
product_color_code: '',
|
||
order_roll: 0,
|
||
item_roll: 0,
|
||
order_weight: 0,
|
||
item_weight: 0,
|
||
};
|
||
}
|
||
|
||
/** 刚扫细码的数量(公斤存储值):优先细码 weight */
|
||
function pickScannedFineWeight(res) {
|
||
if (!res) return 0;
|
||
if (res.weight != null && res.weight !== '') return res.weight;
|
||
if (res.base_unit_weight != null && res.base_unit_weight !== '') return res.base_unit_weight;
|
||
if (res.settle_weight != null && res.settle_weight !== '') return res.settle_weight;
|
||
if (res.actually_weight != null && res.actually_weight !== '') return res.actually_weight;
|
||
return res.item_weight;
|
||
}
|
||
|
||
/** 从扫码接口返回构建详情区(匹数÷100,数量÷10000) */
|
||
export function buildScanDetailFromScanResponse(res) {
|
||
if (!res) return getEmptyScanDetail();
|
||
return {
|
||
quote_order_no: res.quote_order_no || '',
|
||
dyelot_number: res.dyelot_number || '',
|
||
volume_number: res.volume_number != null && res.volume_number !== '' ? String(res.volume_number) : '',
|
||
product_code: res.product_code || '',
|
||
product_color_code: res.product_color_code || '',
|
||
order_roll: formatRoll(res.order_roll != null ? res.order_roll : res.total_roll),
|
||
item_roll: formatRoll(res.item_roll != null ? res.item_roll : res.roll),
|
||
order_weight: formatWeightKg(res.order_weight != null ? res.order_weight : res.total_weight),
|
||
// 本行数量:刚扫这条细码的重量(weight),不是明细行合计
|
||
item_weight: formatWeightKg(pickScannedFineWeight(res)),
|
||
};
|
||
}
|
||
|
||
/** 校验纸筒/包装重量(扫码时提交) */
|
||
export function validateScanWeights(paperTubeWeight, packageWeight) {
|
||
if (paperTubeWeight === '' || paperTubeWeight == null) {
|
||
return '请输入纸筒重量';
|
||
}
|
||
if (Number.isNaN(Number(paperTubeWeight))) return '纸筒重量格式不正确';
|
||
if (packageWeight === '' || packageWeight == null) {
|
||
return '请输入包装重量';
|
||
}
|
||
if (Number.isNaN(Number(packageWeight))) return '包装重量格式不正确';
|
||
return '';
|
||
}
|
||
|
||
/** 构建扫码请求参数(纸筒/包装重量在扫码时提交,公斤值需 ×10000) */
|
||
export function buildScanUpdateParams({
|
||
id,
|
||
scanCode,
|
||
arrangeType,
|
||
warehouseBinId,
|
||
paperTubeWeight,
|
||
packageWeight,
|
||
}) {
|
||
const code = (scanCode || '').trim();
|
||
const params = {
|
||
id: Number(id),
|
||
arrange_type: arrangeType,
|
||
paper_tube_weight: toWeightStorage(paperTubeWeight),
|
||
package_weight: toWeightStorage(packageWeight),
|
||
};
|
||
if (warehouseBinId) {
|
||
params.warehouse_bin_id = Number(warehouseBinId);
|
||
}
|
||
// 长度大于25走二维码,否则走条码
|
||
if (code.length > 25) {
|
||
params.qr_code = code;
|
||
} else {
|
||
params.bar_code = code;
|
||
}
|
||
return params;
|
||
}
|
||
|
||
export function buildStatusParam(id) {
|
||
return { id: String(id) };
|
||
}
|
||
|
||
export function formatDisplayTime(value, format = 'YYYY-MM-DD') {
|
||
if (!value) return '';
|
||
const parsed = dayjs(value);
|
||
return parsed.isValid() ? parsed.format(format) : String(value).slice(0, 10);
|
||
}
|
||
|
||
export function canScanAtStatus(status) {
|
||
return [AUDIT_STATUS.PENDING, AUDIT_STATUS.REJECTED].includes(status);
|
||
}
|
||
|
||
export function isLockedOrderStatus(status) {
|
||
return [AUDIT_STATUS.APPROVED, AUDIT_STATUS.VOIDED].includes(status);
|
||
}
|
||
|
||
export function fetchAllOrderList(listFn, params = {}, pageSize = LIST_PAGE_SIZE) {
|
||
const pickBatch = (res) => {
|
||
if (Array.isArray(res)) return res;
|
||
if (!res || typeof res !== 'object') return [];
|
||
return res.list || res.records || res.items || [];
|
||
};
|
||
const loadPage = (page, acc) => listFn({ ...params, page, size: pageSize })
|
||
.then((res) => {
|
||
const batch = pickBatch(res);
|
||
if (!batch.length) return acc;
|
||
// 一次返回超过 pageSize:后端忽略分页,视为全量,避免死循环翻页
|
||
if (batch.length > pageSize) return acc.concat(batch);
|
||
// 翻页后与首页首条相同:后端忽略 page,避免死循环
|
||
if (
|
||
page > 1
|
||
&& acc.length
|
||
&& batch[0]
|
||
&& acc[0]
|
||
&& String(batch[0].id) === String(acc[0].id)
|
||
) {
|
||
return acc;
|
||
}
|
||
const merged = acc.concat(batch);
|
||
const total = !Array.isArray(res) && res != null ? Number(res.total) : NaN;
|
||
if (!Number.isNaN(total) && merged.length >= total) return merged;
|
||
if (batch.length < pageSize) return merged;
|
||
return loadPage(page + 1, merged);
|
||
});
|
||
return loadPage(1, []);
|
||
}
|
||
|
||
export function getFormStatusActionButtons(headerSaved, orderId, status) {
|
||
if (!headerSaved || !orderId || !canScanAtStatus(status)) return [];
|
||
return [
|
||
{ label: '审核', action: 'approve' },
|
||
{ label: '作废', action: 'void' },
|
||
];
|
||
}
|
||
|
||
export function getViewStatusActionButtons(status) {
|
||
if (status === AUDIT_STATUS.PENDING || status === AUDIT_STATUS.REJECTED) {
|
||
return [
|
||
{ label: '编辑', action: 'edit' },
|
||
{ label: '审核', action: 'approve' },
|
||
{ label: '作废', action: 'void' },
|
||
];
|
||
}
|
||
if (status === AUDIT_STATUS.APPROVED) {
|
||
return [{ label: '消审', action: 'unapprove' }];
|
||
}
|
||
return [];
|
||
}
|
||
|
||
export function orderToFormFields(order) {
|
||
return {
|
||
order_no: order.order_no,
|
||
process_unit_id: order.process_unit_id,
|
||
process_name: order.process_name,
|
||
warehouse_id: order.warehouse_id,
|
||
warehouse_name: order.warehouse_name,
|
||
sale_system_id: order.sale_system_id,
|
||
sale_system_name: order.sale_system_name,
|
||
in_order_type: order.in_order_type || IN_ORDER_TYPE_PROCESS,
|
||
in_order_type_name: order.in_order_type_name
|
||
|| IN_ORDER_TYPE_MAP[order.in_order_type]
|
||
|| IN_ORDER_TYPE_MAP[IN_ORDER_TYPE_PROCESS],
|
||
paper_tube_weight: String(DEFAULT_PAPER_TUBE_WEIGHT),
|
||
package_weight: String(DEFAULT_PACKAGE_WEIGHT),
|
||
remark: order.remark,
|
||
item_data: order.item_data,
|
||
};
|
||
}
|
||
|
||
/** 从仓库下拉中选出默认成品仓 */
|
||
export function pickDefaultWarehouse(options, loginWarehouseId, loginWarehouseName) {
|
||
const list = options || [];
|
||
const byName = list.find((item) => item.label === DEFAULT_WAREHOUSE_NAME);
|
||
if (byName) return byName;
|
||
if (loginWarehouseId) {
|
||
const byId = list.find((item) => String(item.value) === String(loginWarehouseId));
|
||
if (byId) return byId;
|
||
if (loginWarehouseName) {
|
||
return { value: loginWarehouseId, label: loginWarehouseName };
|
||
}
|
||
}
|
||
return list[0] || null;
|
||
}
|
||
|
||
/**
|
||
* 确认并执行审核状态变更
|
||
*/
|
||
export function confirmOrderStatusAction(vm, { orderId, action, onSuccess }) {
|
||
const cfg = STATUS_ACTION_CONFIG[action];
|
||
if (!cfg || !orderId) return;
|
||
const api = vm.$u.api.fpmProcessInOrder;
|
||
const requestFn = api[cfg.apiKey];
|
||
if (typeof requestFn !== 'function') return;
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: cfg.msg,
|
||
success: (res) => {
|
||
if (!res.confirm) return;
|
||
uni.showLoading({ title: '处理中...' });
|
||
requestFn(buildStatusParam(orderId))
|
||
.then(() => {
|
||
if (typeof onSuccess === 'function') return onSuccess();
|
||
uni.hideLoading();
|
||
})
|
||
.catch((e) => {
|
||
uni.hideLoading();
|
||
if (vm.showError) vm.showError(e.message || '操作失败');
|
||
});
|
||
},
|
||
});
|
||
}
|