UNPKG

fastlion-amis

Version:

一种MIS页面生成工具

402 lines (385 loc) 15.7 kB
import { tokenize } from "amis-formula" import { flatMap } from "lodash" import uniqWith from "lodash/uniqWith" import { IColumn } from "../../store/table" import { getCellValue, CROSS_SPLIT_CHAR, handleSort } from "../../store/utils/commonTableFunction" import { cartesianProduct } from "../../utils/utils" import { DATAKEYID } from "../../store/crud" export const EMPTY_GROUP_VALUE = '空白' export const INDEX_NAME = 'SF_INDEX' export const REG_EXP = new RegExp(`\\.|:|${CROSS_SPLIT_CHAR}`, 'g') const worker = new Worker('./public/worker/dataCross.js') interface ICrossField { name: string titleExpr?: string order?: 'ASC' | 'DESC' orderName?: string } export interface ICross { columnFields: ICrossField[] rowFields: Pick<ICrossField, 'name'>[] valueFields: string /** valueField位置 0:表头 1:表体 */ positionType: 0 | 1, } interface IColumnType { type: string align: string sortable: boolean map?: object fixed?: string isNumerical?: boolean quickEdit?: any } type IColumnField = ICrossField & IColumn export interface ICrossColumn extends IColumnType { label: string field: string name: string groupName: string groupLabels: string[] isCountField: boolean } const uniqArr = (fields: IColumnField[], datas: any[]) => { const map = new Map<string, { order: 'asc' | 'desc' }>(fields.map(field => [(field.orderName ?? field.name), { order: (field.order?.toLowerCase() ?? 'asc') as 'asc' | 'desc' }])) const sortData = handleSort(datas.slice(), fields.map(field => field.orderName ?? field.name), map) return uniqWith(sortData.map(data => { const obj: any = {} for (const field of fields) { obj[field.name] = data[field.name] } return obj }), (a, b) => { for (const field of fields) { if (a[field.name] !== b[field.name]) { return false } } return true }) } export const buildCrossColumn = (rowFields: IColumnField[], colFields: IColumnField[], valueFields: IColumnField[], datas: any[], countSum: boolean) => { const ret: ICrossColumn[] = [] const level = colFields.length + 1 const uniqColArr = uniqArr(colFields, datas) for (const rowField of rowFields) { const { type, label, name, align, sortable, map, fixed, pristine } = rowField const groupName = new Array(level - 1).fill(pristine.groupName ?? name).join(',') const groupLabels = new Array(level - 1).fill(pristine.groupName ?? name) ret.push({ ...pristine, type, label, name, field: name, groupName, groupLabels, map, fixed, align, sortable, isCountField: false }) } const product: [any, IColumnField, string[]][] = cartesianProduct(uniqColArr, valueFields).map(([item, field]: [any, IColumnField]) => { const cols: string[] = [] for (let i = 0; i < colFields.length; i++) { const field = colFields[i] if (i == 0) { cols.push(String(item[field.name] ?? EMPTY_GROUP_VALUE).replace(REG_EXP, '')) } else { const vals = [] for (let j = 0; j <= i; j++) { const preField = colFields[j] vals.push(String(item[preField.name] ?? EMPTY_GROUP_VALUE).replace(REG_EXP, '')) } cols.push(vals.join(CROSS_SPLIT_CHAR)) } } return [item, field, cols.concat(`${cols[cols.length - 1]}${CROSS_SPLIT_CHAR}${field.name}`)] }) for (const [uniqCol, valueField, item] of product) { const { type, label, name, align, sortable } = valueField const { quickEdit, ...rest } = valueField.pristine const has$Expr = colFields.some(field => field.titleExpr && !/\$\{[^\}]*\}/g.test(field.titleExpr)) const groupName = has$Expr ? colFields.map(field => field.titleExpr).join(CROSS_SPLIT_CHAR) : level == 1 ? '' : item.slice(0, level - 1).map(val => val.replaceAll(',', '')).join(',') const groupLabels = colFields.map(field => { const val = getCellValue(uniqCol[field.name], field) || EMPTY_GROUP_VALUE return field.titleExpr ? field.titleExpr.replaceAll(`$\{${field.name}\}`, val) : val }) ret.push({ ...rest, type, label: /\$\{[^\}]*\}/g.test(label) ? (tokenize(label, uniqCol) || EMPTY_GROUP_VALUE) : label, name: item[level - 1], field: name, groupName, groupLabels, align, sortable, quickEdit: quickEdit ? { ...quickEdit, name: item[level - 1] } : undefined, isNumerical: true, isCountField: false }) } if (countSum) { for (const valueField of valueFields) { const { type, label, name, align, sortable, pristine } = valueField const groupName = new Array(colFields.length).fill('SF_COUNT').join(',') const groupLabels = new Array(colFields.length).fill('合计') const rLabel = label.replace(REG_EXP, '') ret.push({ ...pristine, type, label: rLabel, name: `${rLabel}${CROSS_SPLIT_CHAR}${groupName}`, field: name, groupName, groupLabels, align, sortable, isNumerical: false, isCountField: true }) } } return ret } export const buildCrossData = (rowFields: IColumnField[], colFields: IColumnField[], crossColumns: ICrossColumn[], datas: any[]) => { return new Promise<any[]>((resolve, reject) => { worker.postMessage({ rowFields, colFields, crossColumns, datas, regExp: REG_EXP, EMPTY_GROUP_VALUE, CROSS_SPLIT_CHAR }) worker.onmessage = (e: MessageEvent<any[]>) => { const map = new Map<string, { order: 'asc' | 'desc' }>(rowFields.map(field => [(field.orderName ?? field.name), { order: (field.order?.toLowerCase() ?? 'asc') as 'asc' | 'desc' }])) const sortData = handleSort(e.data.slice(), rowFields.map(field => field.orderName ?? field.name), map) resolve(sortData) } worker.onmessageerror = (e) => { reject(e.data) } }) } export const buildCrossColumn1 = (rowFields: IColumnField[], colFields: IColumnField[], datas: any[]) => { const ret: ICrossColumn[] = [] const level = colFields.length // step 1 for (const rowField of rowFields) { const { type, label, name, align, sortable, map, fixed, pristine } = rowField ret.push({ ...pristine, type, label, name, field: name, groupName: new Array(level - 1).fill(name).join(','), groupLabels: [], map, fixed, align, sortable, isCountField: false }) } // step 2 ret.push({ label: '指标', name: INDEX_NAME, field: INDEX_NAME, groupName: new Array(level - 1).fill(INDEX_NAME).join(','), groupLabels: [], isCountField: false, type: 'plain', align: 'left', sortable: false, }) // step 3 const uniqColArr = uniqArr(colFields, datas) for (const item of uniqColArr) { const cols: string[] = [] for (let i = 0; i < colFields.length; i++) { const field = colFields[i] if (i == 0) { cols.push(String(item[field.name] ?? EMPTY_GROUP_VALUE).replace(REG_EXP, '')) } else { const vals = [] for (let j = 0; j <= i; j++) { const preField = colFields[j] vals.push(String(item[preField.name] ?? EMPTY_GROUP_VALUE).replace(REG_EXP, '')) } cols.push(vals.join(CROSS_SPLIT_CHAR)) } } const groupName = level == 1 ? '' : cols.slice(0, level - 1).join(',') const labels = colFields.map(field => { const val = getCellValue(item[field.name], field) || EMPTY_GROUP_VALUE return field.titleExpr ? field.titleExpr.replaceAll(`$\{${field.name}\}`, val) : val }) ret.push({ label: labels[level - 1], name: cols[level - 1], field: '', groupName, groupLabels: labels.slice(0, level - 1), isCountField: false, type: 'plain', align: 'right', sortable: false }) } return ret } export const buildCrossData1 = (rowFields: IColumnField[], colFields: IColumnField[], valueFields: IColumnField[], crossColumns: ICrossColumn[], datas: any[]) => { const uniqRowData = uniqArr(rowFields, datas) return new Promise<any[]>((resolve, reject) => { worker.postMessage({ rowFields, colFields, valueFields, crossColumns, datas, uniqRowData, regExp: REG_EXP, EMPTY_GROUP_VALUE, CROSS_SPLIT_CHAR, INDEX_NAME }) worker.onmessage = (e: MessageEvent<any[]>) => { resolve(e.data) } worker.onmessageerror = (e) => { reject(e.data) } }) } export const getChangeRows = (changeDatas: any[], rawDatas: any[], cross: ICross): any[] => { const { rowFields, columnFields, positionType } = cross if (positionType === 1) return [] return flatMap(changeDatas, changeData => { const colFields = Object.keys(changeData).filter(key => key.includes(CROSS_SPLIT_CHAR)) const rowTarget = rawDatas.filter(data => rowFields.every(field => data[field.name] == changeData[field.name])) const result = [] for (const field of colFields) { const values = field.split(CROSS_SPLIT_CHAR) const valueField = values[values.length - 1] const colTarget = rowTarget.find(data => { return columnFields.every((field, index) => { const dValue = data[field.name] return (dValue == null ? dValue : String(dValue).replace(REG_EXP, '')) == (values[index] == EMPTY_GROUP_VALUE ? null : values[index]) }) }) if (colTarget) { const temp = { ...colTarget } const index = result.findIndex(item => item[DATAKEYID] === temp[DATAKEYID]) if (index == -1) { temp[valueField] = changeData[field] result.push(temp) } else { result[index][valueField] = changeData[field] } } } return result }) } // export const buildCrossData = (rowFields: IColumnField[], colFields: IColumnField[], crossColumns: ICrossColumn[], datas: any[], countSum: boolean) => { // return new Promise<any[]>((resolve, reject) => { // const ret = datas.reduce((result, current) => { // const index = result.findIndex((data: any) => rowFields.every(field => data[field.name] == current[field.name])) // const compareFn = (field: IColumnField, index: number, name: string) => { // const value = name.split(CROSS_SPLIT_CHAR)[index] // const dValue = current[field.name] // return (dValue == null ? dValue : String(dValue).replace(REG_EXP, '')) == (value == EMPTY_GROUP_VALUE ? null : value) // } // if (index == -1) { // const target = { ...current } // for (let i = 0; i < crossColumns.length; i++) { // const { name, field, isPercentage, calculatedField, isCountField } = crossColumns[i] // if (i < rowFields.length) { // target[name] = current[field] // } else { // const isRate = isPercentage === true && (calculatedField?.length ?? 0) > 0 // const condition = colFields.every((field, index) => compareFn(field, index, name)) // if (isRate) { // target[`Z_${name}`] = condition || isCountField ? +current[field] / 100 * +current[calculatedField] : null // target[`M_${name}`] = condition || isCountField ? +current[calculatedField] : null // } // target[name] = condition || isCountField ? current[field] : null // } // } // result.push(target) // } else { // const target = { ...result[index] } // for (let i = rowFields.length; i < crossColumns.length; i++) { // const { name, field, isPercentage, calculatedField, isCountField } = crossColumns[i] // const isRate = isPercentage === true && (calculatedField?.length ?? 0) > 0 // if (isCountField) { // if (isRate) { // target[`Z_${name}`] += +current[field] / 100 * +current[calculatedField] // target[`M_${name}`] += +current[calculatedField] // target[name] = (target[`Z_${name}`] == 0 || target[`M_${name}`] == 0) ? null : target[`Z_${name}`] / target[`M_${name}`] * 100 // } else { // target[name] += +current[field] // } // } else { // const condition = colFields.every((field, index) => compareFn(field, index, name)) // if (condition) { // if (isRate) { // if (current[calculatedField] != null && !isNaN(+current[calculatedField])) { // target[`Z_${name}`] += +current[field] / 100 * +current[calculatedField] // target[`M_${name}`] += +current[calculatedField] // target[name] = (target[`Z_${name}`] == 0 || target[`M_${name}`] == 0) ? null : target[`Z_${name}`] / target[`M_${name}`] * 100 // } else { // target[name] = current[field] // } // } else { // if (current[field] != null) { // if (isNaN(+current[field])) { // target[name] = current[field] // } else { // target[name] += +current[field] // } // } // } // } // } // } // result[index] = target // } // return result // }, []) // resolve(ret) // }) // } // export const buildCrossData1 = (rowFields: IColumnField[], colFields: IColumnField[], valueFields: IColumnField[], crossColumns: ICrossColumn[], datas: any[]) => { // const uniqRowData = uniqArr(rowFields, datas) // const tempColumns = crossColumns.slice(rowFields.length + 1) // return flatMap(uniqRowData ?? [], rowData => { // const ret: any[] = [] // const rowDatas = datas.filter(data => rowFields.every(rowField => data[rowField.name] == rowData[rowField.name])) // for (let i = 0; i < valueFields.length; i++) { // const { name, label, pristine } = valueFields[i] // const { isPercentage, calculatedField } = pristine // const isRate = isPercentage === true && (calculatedField?.length ?? 0) > 0 // const obj = { ...rowData } // obj[INDEX_NAME] = label // for (const tempCol of tempColumns) { // const items: any[] = rowDatas.filter(data => { // return colFields.every((colField, index) => { // const value = tempCol.name.split(CROSS_SPLIT_CHAR)[index] // const dValue = data[colField.name] // return (dValue == null ? dValue : String(dValue).replace(REG_EXP, '')) == (value == EMPTY_GROUP_VALUE ? null : value) // }) // }) // if (items.length > 0) { // let value = null, zSum = 0, mSum = 0 // for (const item of items) { // if (item[name] != null) { // if (isRate) { // if (item[calculatedField] != null && !isNaN(+item[calculatedField])) { // zSum += (+item[name] / 100 * + item[calculatedField]) // mSum += +(item[calculatedField]) // value = (zSum == 0 || mSum == 0) ? null : (zSum / mSum * 100) // } else { // value = item[name] // } // } else { // if (isNaN(+item[name])) { // value = item[name] // } else { // value += +item[name] // } // } // } // } // obj[tempCol.name] = value ? getCellValue(value, valueFields[i]) : null // } else { // obj[tempCol.name] = null // } // } // ret.push(obj) // } // return ret // }) // }