plus-base-on-baosight
Version:
Expansion based on the Baosight methods.
241 lines (235 loc) • 7.15 kB
text/typescript
import { nextTick } from 'vue';
import type { ExcelStyle, ExcelRow, ExcelFont, ExcelAlignment, ExcelBorder, ExcelBorders, ColDef } from '@ag-grid-community/core';
type ExcelStyle_ = Omit<ExcelStyle, 'id'>;
type ExportStyle = ExcelStyle_ | (() => ExcelStyle_);
interface ExportConfigCellContent {
value: string | null;
type?: 'string' | 'number';
merge?: number;
style?: ExportStyle;
}
interface ExportConfigCell {
[key: `cell${number}`]: ExportConfigCellContent;
style?: ExportStyle;
}
interface ExportConfigLine {
[key: `line${number}`]: ExportConfigCell;
style?: ExportStyle;
}
interface ExportConfig {
top: ExportConfigLine;
bottom: ExportConfigLine;
}
type ExportTopConfig = ExportConfig['top'];
type ExportBottomConfig = ExportConfig['bottom'];
interface ExportColEvent {
export: {
setCellStyle: (style: ExcelStyle_) => void;
setCellStyleID: (id: string) => void;
setHeaderStyle: (style: ExcelStyle_) => void;
setHeaderStyleID: (id: string) => void;
};
}
type ExportCol = ColDef & ExportColEvent;
export class AgGridCustomExportManager {
#erFormHelper: any = null;
#orginStylesTop: ExcelStyle[] = [];
#orginStylesBottom: ExcelStyle[] = [];
#orginStylesCustom: ExcelStyle[] = [];
#soruce: { [key: string]: any }[] = [];
#fileName = '';
#gridId = '';
#exportConfig = {
sheetName: 'Sheet1',
columnKeys: [],
prependContent: {},
appendContent: {},
};
constructor(erFormHelper: any, grid: string, fileName?: string) {
this.#erFormHelper = erFormHelper;
this.#gridId = grid;
fileName && (this.#fileName = fileName);
}
#api() {
const api = this.#erFormHelper?.getGridApi(this.#gridId) ?? null;
return api;
}
#formatParams(type: 'top' | 'bottom', params: ExportConfigLine) {
let res = [] as any[];
let styles = deepClone(params.style ?? {});
Object.entries(params).map(([line, lineCfg]: [string, ExportConfigCell]) => {
// pos 是从1开始的
const pos_line = Number.parseInt(line.split('line')[1]);
// 处理cell
let cells: any[] = [];
Object.entries(lineCfg).map(([cell, cellCfg]: [string, ExportConfigCellContent]) => {
const pos_cell = Number.parseInt(cell.split('cell')[1]);
// 设置单元格内容数据格式
const cellContent: { [key: string]: any } = {};
// 单元格值
const valType = cellCfg.type === 'number' ? 'Number' : 'String';
cellContent['data'] = {
type: valType,
value: valType === 'String' ? String(cellCfg.value) : Number(cellCfg.value),
};
// 设置单元格样式
if (cellCfg.style) {
const id = `-auto-style-${type === 'top' ? 'top' : 'bottom'}-cell${pos_cell}`;
if (typeof cellCfg.style === 'object') {
styles = deepMerge(styles, cellCfg.style);
}
if (typeof cellCfg.style === 'function') {
styles = deepMerge(styles, cellCfg.style());
}
if (type === 'top') {
this.#orginStylesTop.push({ ...styles, id });
} else {
this.#orginStylesBottom.push({ ...styles, id });
}
cellContent['styleId'] = id;
}
// 横向合并单元格
if (cellCfg.merge) {
cellContent['mergeAcross'] = cellCfg.merge - 1;
}
cells[pos_cell - 1] = cellContent;
});
// 填充undefined
cells = Array.from(cells).map((v) => (!v ? { cells: [] } : v));
res[pos_line - 1] = { cells };
});
// 填充undefined
res = Array.from(res).map((v) => (!v ? { cells: [] } : v));
return res;
}
addLineTop(params: ExportTopConfig) {
this.#exportConfig.prependContent = this.#formatParams('top', params);
}
addLineBottom(params: ExportBottomConfig) {
this.#exportConfig.appendContent = this.#formatParams('bottom', params);
}
addStyles(...styles: ExcelStyle[]) {
const res = { status: 0, msg: '' };
const ids = this.#orginStylesTop
.concat(this.#orginStylesBottom)
.concat(this.#orginStylesCustom)
.map((style) => style.id);
styles.map((style) => {
if (ids.includes(style.id)) {
res.status--;
res.msg += `ID[${style.id}]已存在;`;
} else {
this.#orginStylesCustom.push(style);
}
});
return res;
}
#addCol(col: ColDef) {
const col_ = col as any;
col_['export'] = {
setCellStyleID: (id: string) => {
col.cellClass = id;
},
setHeaderStyleID: (id: string) => {
col.headerClass = id;
},
setHeaderStyle: (style: ExcelStyle) => {
const id = `-auto-style-head-${col.field}`;
this.addStyles({ ...style, id });
col.headerClass = id;
},
setCellStyle: (style: ExcelStyle) => {
const id = `-auto-style-cell-${col.field}`;
this.addStyles({ ...style, id });
col.cellClass = id;
},
};
return col_ as ExportCol;
}
setColumnStyle(callback: (col: ExportCol) => void) {
this.#erFormHelper.setGridColumnOptions('exportGrid', (col: ColDef) => {
callback.call(window, this.#addCol(col));
});
}
createStyles(styles: ExcelStyle_) {
return styles;
}
getStyles() {
return this.#orginStylesTop.concat(this.#orginStylesBottom).concat(this.#orginStylesCustom);
}
setSoruce(source: { [key: string]: any }[]) {
const api = this.#api();
if (api) {
this.#soruce = source;
return true;
}
return false;
}
clearSource() {
const api = this.#api();
if (api) {
this.#soruce = [];
return true;
}
return false;
}
setFileName(fileName: string) {
this.#fileName = fileName;
}
export(sheetName = 'Sheet1'): Promise<boolean> {
return new Promise((resolve, reject) => {
nextTick(async () => {
const api = this.#api();
// 清空原本数据源
api.setRowData([]);
api.setRowData(this.#soruce);
this.#exportConfig.sheetName = sheetName;
this.#exportConfig.columnKeys = this.#erFormHelper.getGridColumnFields(this.#gridId, 'Array');
const exportData = api?.getSheetDataForExcel(this.#exportConfig);
if (exportData) {
api.exportMultipleSheetsAsExcel({
data: [exportData],
fileName: `${this.#fileName ?? `export_${new Date().getTime()}`}.xlsx`,
});
resolve(true);
}
reject(false);
});
});
}
}
export type { ExcelStyle_, ExcelFont, ExcelAlignment, ExcelBorder, ExcelBorders };
function deepMerge(obj1: { [key: string]: any }, obj2?: { [key: string]: any }) {
if (obj2) {
const result = deepClone(obj1);
for (const key in obj2) {
const value = obj2[key];
const isNorObj = typeof obj2[key] === 'object' && obj2[key] !== null;
const isArr = Array.isArray(value);
const isData = value instanceof Date;
if (value && isNorObj && !isArr && !isData) {
result[key] = deepMerge(result[key] || {}, obj2[key]);
} else {
result[key] = value;
}
}
return result;
} else {
return deepClone(obj1);
}
}
function deepClone(obj: { [key: string]: any }, hash = new WeakMap()) {
if (obj === null) return null;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
const cloneObj: { [key: string]: any } = Array.isArray(obj) ? [] : {};
hash.set(obj, cloneObj);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}