121 lines
4.4 KiB
JavaScript
121 lines
4.4 KiB
JavaScript
import { importBlob } from '@/api/common';
|
||
import { downloadXls } from '@/utils/util';
|
||
import { ElLoading } from 'element-plus';
|
||
import NProgress from 'nprogress';
|
||
import 'nprogress/nprogress.css';
|
||
|
||
const excelPattern = /\.(xls|xlsx)$/;
|
||
|
||
const isExcelResponse = res => {
|
||
const contentType = res.headers['content-type'] || res.data.type || '';
|
||
return contentType.includes('application/vnd.ms-excel');
|
||
};
|
||
|
||
const parseBlobJson = async blob => {
|
||
const text = await blob.text();
|
||
return text ? JSON.parse(text) : {};
|
||
};
|
||
|
||
/**
|
||
* 加工后端返回的导入失败明细 Excel。
|
||
* 若传入 failDetailDecorator(函数),则动态加载 exceljs 读取 blob 流,
|
||
* 交由 decorator 完成改写原因列、调整列宽等加工,再导出新的 blob;
|
||
* 否则原样返回 blob(保持旧行为,不影响未使用 decorator 的模块)。
|
||
* @param {Blob} blob 后端返回的失败明细 Excel 流
|
||
* @param {String} businessName 业务名称(如「货物类型」)
|
||
* @param {Function} [failDetailDecorator] async (workbook, { businessName }) => void
|
||
* @returns {Promise<Blob>}
|
||
*/
|
||
const decorateFailExcel = async (blob, businessName, failDetailDecorator) => {
|
||
if (typeof failDetailDecorator !== 'function') return blob;
|
||
const ExcelJS = await import('exceljs');
|
||
const buf = await blob.arrayBuffer();
|
||
const workbook = new ExcelJS.Workbook();
|
||
await workbook.xlsx.load(buf);
|
||
await failDetailDecorator(workbook, { businessName });
|
||
const out = await workbook.xlsx.writeBuffer();
|
||
return new Blob([out], { type: 'application/vnd.ms-excel' });
|
||
};
|
||
|
||
/**
|
||
* 打开导入弹窗
|
||
* @param {Object} vm 组件实例
|
||
* @param {String} businessName 业务名称,用于失败明细文件命名
|
||
* @param {Function} [onSuccess] 导入成功回调
|
||
* @param {Object} [options] 扩展项
|
||
* @param {Function} [options.failDetailDecorator] 失败明细加工函数,用于对后端返回的失败 Excel 标红/改写原因
|
||
*/
|
||
export const openImportDialog = (vm, businessName, onSuccess, options = {}) => {
|
||
const { failDetailDecorator } = options;
|
||
const excelOption = vm.excelOption || vm.excelOptionConfig;
|
||
const column = vm.findColumn(excelOption?.column || [], 'excelFile');
|
||
if (!column) {
|
||
vm.$message.error('导入配置异常,请稍后重试');
|
||
return;
|
||
}
|
||
column.httpRequest = (option, uploadColumn) =>
|
||
handleImportExcel(vm, option, uploadColumn, businessName, onSuccess, { failDetailDecorator });
|
||
vm.excelBox = true;
|
||
};
|
||
|
||
export const handleImportExcel = async (vm, option, column, businessName, onSuccess, options = {}) => {
|
||
const { failDetailDecorator } = options;
|
||
const file = option.file;
|
||
const fileName = file && file.name ? file.name.toLowerCase() : '';
|
||
if (!excelPattern.test(fileName)) {
|
||
vm.$message.error('请上传 .xls,.xlsx 标准格式文件');
|
||
if (typeof option.onError === 'function') {
|
||
option.onError(new Error('请上传 .xls,.xlsx 标准格式文件'));
|
||
}
|
||
return;
|
||
}
|
||
|
||
NProgress.start();
|
||
const loading = ElLoading.service({
|
||
lock: true,
|
||
text: '导入中',
|
||
background: 'rgba(255, 255, 255, 0.7)',
|
||
});
|
||
try {
|
||
const res = await importBlob(column.action, file);
|
||
if (isExcelResponse(res)) {
|
||
const decorated = await decorateFailExcel(res.data, businessName, failDetailDecorator);
|
||
downloadXls(
|
||
decorated,
|
||
`${businessName}导入失败明细${vm.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`
|
||
);
|
||
vm.$message.warning('部分数据导入失败,已下载失败明细');
|
||
} else {
|
||
const result = await parseBlobJson(res.data);
|
||
if (result.code !== 200) {
|
||
vm.$message.error(result.msg || '导入失败');
|
||
if (typeof option.onError === 'function') {
|
||
option.onError(new Error(result.msg || '导入失败'));
|
||
}
|
||
return;
|
||
}
|
||
vm.$message.success('导入完成');
|
||
}
|
||
vm.excelBox = false;
|
||
vm.excelForm = {};
|
||
if (typeof onSuccess === 'function') {
|
||
onSuccess();
|
||
} else if (typeof vm.onLoad === 'function') {
|
||
vm.onLoad(vm.page, vm.query);
|
||
} else if (typeof vm.initTree === 'function') {
|
||
vm.initTree();
|
||
}
|
||
if (typeof option.onSuccess === 'function') {
|
||
option.onSuccess(res);
|
||
}
|
||
} catch (error) {
|
||
vm.$message.error(error.message || '导入失败');
|
||
if (typeof option.onError === 'function') {
|
||
option.onError(error);
|
||
}
|
||
} finally {
|
||
loading.close();
|
||
NProgress.done();
|
||
}
|
||
};
|