feat(import): 优化货物类型导入失败明细及下载模板功能
- 引入 exceljs 动态加载,支持对后端失败明细 Excel 进行内容加工 - 导入失败明细取消标红错误字段,改为清晰错误原因文本说明 - 调整失败明细各列宽度,增加失败原因列换行提升可读性 - 失败明细原因列格式统一为“第N行:错误,规则”方式,便于定位 - 下载模板由后端生成改为前端本地生成,并内置示例数据 - 模板表头严格使用真实模板列名,示例数据涵盖一级及二级货物类型 - 模板编码列设置为文本格式避免前导零丢失,表头自动换行显示完整说明
This commit is contained in:
@@ -155,8 +155,7 @@ import { getToken } from '@/utils/auth';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
// 导入失败明细中,每个错误文本对应的「标红列」与「规则说明」
|
||||
const FAIL_RED_FILL = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFFFC7CE' } };
|
||||
// 导入失败明细中,每个错误文本对应的「字段列」与「规则说明」
|
||||
const CARGO_TYPE_FAIL_RULES = [
|
||||
{ match: '请选择货物类型级别', col: '类型', rule: '仅允许:一级货物类型/二级货物类型' },
|
||||
{ match: '货物类型编码格式不正确', col: '货物类型编码', rule: '一级2位数字,二级4位数字' },
|
||||
@@ -171,11 +170,22 @@ const CARGO_TYPE_FAIL_RULES = [
|
||||
{ match: '备注不能超过200字', col: '备注', rule: '备注不能超过200字' },
|
||||
];
|
||||
|
||||
// 失败明细各列目标宽度(Excel 字符数),避免内容拥挤
|
||||
const CARGO_TYPE_FAIL_COL_WIDTH = {
|
||||
类型: 14,
|
||||
上级货物类型: 16,
|
||||
上级货物类型编码: 20,
|
||||
货物类型: 16,
|
||||
货物类型编码: 16,
|
||||
备注: 18,
|
||||
导入失败原因: 60,
|
||||
};
|
||||
|
||||
/**
|
||||
* 加工后端返回的导入失败明细 Excel:
|
||||
* - 依据「错误文本 -> 字段列」映射,给违规的那一栏标红
|
||||
* - 将「导入失败原因」列改写为(第N行:错误,规则)格式
|
||||
* 未匹配到的未知错误,标红原因列本身并原样提示
|
||||
* - 将「导入失败原因」列改写为(第N行:错误,规则)格式,便于定位
|
||||
* - 适当加宽各列、原因列自动换行,避免内容拥挤
|
||||
* 不再对单元格标红,仅通过文字清晰说明错误
|
||||
*/
|
||||
export const decorateCargoTypeFailDetail = async workbook => {
|
||||
const ws = workbook.worksheets[0];
|
||||
@@ -198,14 +208,17 @@ export const decorateCargoTypeFailDetail = async workbook => {
|
||||
const decorated = parts.map(part => {
|
||||
const rule = CARGO_TYPE_FAIL_RULES.find(r => part.includes(r.match));
|
||||
if (rule) {
|
||||
const targetCol = colMap[rule.col];
|
||||
if (targetCol) row.getCell(targetCol).fill = FAIL_RED_FILL;
|
||||
return `第${rowNumber}行:${part},${rule.rule}`;
|
||||
}
|
||||
reasonCell.fill = FAIL_RED_FILL;
|
||||
return `第${rowNumber}行:${part}`;
|
||||
});
|
||||
reasonCell.value = decorated.join(';');
|
||||
reasonCell.alignment = { wrapText: true, vertical: 'top' };
|
||||
});
|
||||
// 加宽各列(未列在映射中的列给默认宽度)
|
||||
Object.keys(colMap).forEach(name => {
|
||||
const col = ws.getColumn(colMap[name]);
|
||||
col.width = CARGO_TYPE_FAIL_COL_WIDTH[name] || 18;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -544,9 +557,43 @@ export default {
|
||||
},
|
||||
handleTemplate() {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-system/cargo-type/export-template', {})
|
||||
.then(res => {
|
||||
downloadXls(res.data, '货物类型导入模板.xlsx');
|
||||
// 本地生成带示例的导入模板(不依赖后端空模板),表头与可正常导入的模板一致
|
||||
import('exceljs')
|
||||
.then(ExcelJS => {
|
||||
const ExcelJSLib = ExcelJS.default || ExcelJS;
|
||||
const wb = new ExcelJSLib.Workbook();
|
||||
const ws = wb.addWorksheet('货物类型导入模板');
|
||||
const headers = [
|
||||
'*类型',
|
||||
'上级货物类型(如为一级则不需填写)',
|
||||
'上级货物类型编码(如为一级则不需填写)',
|
||||
'*货物类型',
|
||||
'*货物类型编码',
|
||||
'备注',
|
||||
];
|
||||
ws.addRow(headers);
|
||||
// 示例数据:一级 + 二级各一条,演示层级关系与填写格式(当前环境可正常导入)
|
||||
ws.addRow(['一级货物类型', null, null, '电子产品2', '117', '消费类电子']);
|
||||
ws.addRow(['二级货物类型', '电子产品2', '117', '手机2', '11014', '智能手机']);
|
||||
// 列宽:表头含长说明,适度加宽
|
||||
const widths = [14, 24, 26, 16, 16, 18];
|
||||
ws.columns.forEach((col, i) => {
|
||||
col.width = widths[i] || 16;
|
||||
});
|
||||
// 编码列设为文本,避免前导零丢失
|
||||
[3, 5].forEach(idx => {
|
||||
const col = ws.getColumn(idx);
|
||||
if (col) col.numFmt = '@';
|
||||
});
|
||||
// 表头自动换行,长说明完整显示
|
||||
ws.getRow(1).eachCell(cell => {
|
||||
cell.alignment = { wrapText: true, vertical: 'middle' };
|
||||
});
|
||||
return wb.xlsx.writeBuffer();
|
||||
})
|
||||
.then(buf => {
|
||||
const blob = new Blob([buf], { type: 'application/vnd.ms-excel' });
|
||||
downloadXls(blob, '货物类型导入模板.xlsx');
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
|
||||
Reference in New Issue
Block a user