You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
4.6 KiB
175 lines
4.6 KiB
7 months ago
|
|
||
|
|
||
|
import { utils, write } from 'xlsx';
|
||
|
import { dateUtil } from '/nerv-lib/util/date-util';
|
||
|
import { NsMessage } from '/nerv-lib/component/message';
|
||
|
import axios from 'axios';
|
||
|
import { createVNode, h, render as vueRender } from 'vue';
|
||
|
import { NsXlsxImport } from '/nerv-lib/component/xlsx';
|
||
|
import { NsModal } from '/nerv-lib/component/modal';
|
||
|
|
||
|
interface MapItem {
|
||
|
title: string;
|
||
|
dataIndex: string;
|
||
|
format?: Function;
|
||
|
}
|
||
|
type objProps = {
|
||
|
[name: string]: any;
|
||
|
};
|
||
|
|
||
|
type argType = {
|
||
|
data: Array<object>;
|
||
|
xlsxMap: Array<MapItem>;
|
||
|
xlsxName: string;
|
||
|
limit?: number;
|
||
|
};
|
||
|
|
||
|
const DATE_FORMAT = 'YYYY-MM-DD ';
|
||
|
const MODAL_INFO = {
|
||
|
title: '警告',
|
||
|
okText: '确认',
|
||
|
cancelText: '取消',
|
||
|
};
|
||
|
|
||
|
const errorTable = [
|
||
|
{
|
||
|
title: '行数',
|
||
|
dataIndex: 'row',
|
||
|
},
|
||
|
{
|
||
|
title: '错误内容',
|
||
|
dataIndex: 'reason',
|
||
|
},
|
||
|
];
|
||
|
|
||
|
const sheet2blob = (sheet: any, sheetName = 'sheet1') => {
|
||
|
sheetName = sheetName || 'sheet1';
|
||
|
const workbook = {
|
||
|
SheetNames: [sheetName],
|
||
|
Sheets: {},
|
||
|
};
|
||
|
workbook['Sheets'][sheetName] = sheet;
|
||
|
// 生成excel的配置项
|
||
|
const wbout = write(workbook, {
|
||
|
bookType: 'xlsx', // 要生成的文件类型
|
||
|
bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
|
||
|
type: 'binary',
|
||
|
});
|
||
|
const blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' });
|
||
|
// 字符串转ArrayBuffer
|
||
|
function s2ab(s: any) {
|
||
|
const buf = new ArrayBuffer(s.length);
|
||
|
const view = new Uint8Array(buf);
|
||
|
for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
|
||
|
return buf;
|
||
|
}
|
||
|
return blob;
|
||
|
};
|
||
|
|
||
|
function openDownloadDialog(url: string | Blob | MediaSource, saveName: string) {
|
||
|
if (typeof url == 'object' && url instanceof Blob) {
|
||
|
url = URL.createObjectURL(url); // 创建blob地址
|
||
|
}
|
||
|
const aLink = document.createElement('a');
|
||
|
aLink.href = url;
|
||
|
aLink.download = saveName || '';
|
||
|
document.body.appendChild(aLink);
|
||
|
aLink.click();
|
||
|
document.body.removeChild(aLink);
|
||
|
}
|
||
|
|
||
|
function download(table: unknown[][], name: string, config: any) {
|
||
|
let isHandle;
|
||
|
if (config && config['isHandle']) {
|
||
|
isHandle = config['isHandle'];
|
||
|
}
|
||
|
let sheet;
|
||
|
//isHandle 表示自己处理表格样式和数据,不需要再做处理
|
||
|
isHandle ? (sheet = table) : (sheet = utils.aoa_to_sheet(table));
|
||
|
openDownloadDialog(sheet2blob(sheet), `${name}.xlsx`);
|
||
|
}
|
||
|
|
||
|
function DataHandler(data: Array<object>, keyMap: Array<MapItem>) {
|
||
|
const tableHeader: string[] = []; // 表头
|
||
|
const tableHeaderKeyMap: objProps = {}; // 表头索引map
|
||
|
|
||
|
keyMap?.map((item: MapItem) => {
|
||
|
tableHeader.push(item['title']);
|
||
|
tableHeaderKeyMap[item['title']] = item['dataIndex'];
|
||
|
});
|
||
|
|
||
|
const table = [tableHeader];
|
||
|
data.forEach((item: object) => {
|
||
|
const rowData: any[] = [];
|
||
|
tableHeader.forEach((th: string, index: number) => {
|
||
|
let temp = item[tableHeaderKeyMap[th]];
|
||
|
const format = keyMap[index].format;
|
||
|
if (format) temp = format(temp, item);
|
||
|
rowData.push(temp);
|
||
|
});
|
||
|
table.push(rowData);
|
||
|
});
|
||
|
return table;
|
||
|
}
|
||
|
|
||
|
export const importFile = (
|
||
|
{ api, params }: any,
|
||
|
reload: Function,
|
||
|
event: any,
|
||
|
successBack?: Function,
|
||
|
errorBack?: Function,
|
||
|
) => {
|
||
|
const formData = new FormData();
|
||
|
params &&
|
||
|
Object.keys(params).map((item) => {
|
||
|
formData.append(item, params[item]);
|
||
|
});
|
||
|
formData.append('file', event);
|
||
|
axios
|
||
|
.post(api, formData)
|
||
|
.then((res) => {
|
||
|
if (res) {
|
||
|
NsMessage.success('导入成功', 1, () => {
|
||
|
reload && reload();
|
||
|
successBack && successBack(res);
|
||
|
});
|
||
|
}
|
||
|
})
|
||
|
.catch((err) => {
|
||
|
if (errorBack) {
|
||
|
errorBack(err);
|
||
|
} else {
|
||
|
NsMessage.error(err.response?.data?.msg || '导入失败');
|
||
|
const error = err.response?.data?.data;
|
||
|
if (error)
|
||
|
xlsxExport({
|
||
|
data: error,
|
||
|
xlsxMap: errorTable,
|
||
|
xlsxName: '导入数据不规范说明YYYY-MM-DD',
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
const checkLimit = (data: Array<object>, limit?: number) => {
|
||
|
if (limit && data.length > limit) {
|
||
|
NsModal.confirm({
|
||
|
...MODAL_INFO,
|
||
|
content: `本次数据下载超过${limit}条,请重新选择数据!`,
|
||
|
});
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
};
|
||
|
|
||
|
export const xlsxExport = ({ data, xlsxMap, xlsxName, limit }: argType) => {
|
||
|
if (!checkLimit(data, limit)) return;
|
||
|
const name = dateUtil(new Date()).format(xlsxName);
|
||
|
download(DataHandler(data, xlsxMap), `${name}`, '');
|
||
|
};
|
||
|
|
||
|
export const xlsxImport = (data: any) => {
|
||
|
const div = document.createElement('div');
|
||
|
const vm = h(NsXlsxImport, { ...data }, '');
|
||
|
vueRender(vm, div);
|
||
|
};
|