UNPKG

fastlion-amis

Version:

一种MIS页面生成工具

1,733 lines (1,523 loc) 57.3 kB
import { types, getParent, SnapshotIn, flow, getEnv, getRoot, IAnyModelType, isAlive, Instance } from 'mobx-state-tree'; import { iRendererStore } from './iRenderer'; import { resolveVariable } from '../utils/tpl-builtin'; import isEqual from 'lodash/isEqual'; import find from 'lodash/find'; import { isBreakpoint, createObject, isObject, isVisible, guid, findTree, flattenTree, eachTree, difference, immutableExtends, extendObject, hasVisibleExpression, filterTree, findTreeIndex, spliceTree, isMobile, getTextWidth, standardValueText, numberFormatter, } from '../utils/helper'; import { evalExpression } from '../utils/tpl'; import { IFormStore } from './form'; import { getStoreById } from './manager'; import { EventEnum, EventSub } from '../utils/sub'; import { DATAKEYID } from './crud'; import { getCellValue, getHeadRows, handleFilter, handleSort } from './utils/commonTableFunction'; import { cloneDeep, isNil, last } from 'lodash'; export const Column = types .model('Column', { label: types.optional(types.frozen(), undefined), type: types.optional(types.string, 'plain'), name: types.maybe(types.string), value: types.frozen(), groupName: '', toggled: false, toggable: true, expandable: false, checkdisable: false, isPrimary: false, searchable: types.maybe(types.frozen()), enableSearch: true, sortable: false, filterable: types.optional(types.frozen(), undefined), fixed: '', align: '', index: 0, rawIndex: 0, breakpoint: types.optional(types.frozen(), undefined), pristine: types.optional(types.frozen(), undefined), remark: types.optional(types.frozen(), undefined), className: '', map: types.frozen(), }) .actions(self => ({ toggleToggle() { self.toggled = !self.toggled; const table = getParent(self, 2) as ITableStore; if (!table.activeToggaleColumns.length) { self.toggled = true; } table.persistSaveToggledColumns(); }, setToggled(value: boolean) { self.toggled = value; }, setEnableSearch(value: boolean) { self.enableSearch = value; }, setMapValue(obj: object) { self.map = { ...self.map, ...obj } // self.map = Object.assign(obj, self.map) } })); export type IColumn = Instance<typeof Column>; export type SColumn = SnapshotIn<typeof Column>; export const Row = types .model('Row', { storeType: 'Row', id: types.identifier, parentId: '', key: types.string, pristine: types.frozen({} as any), data: types.frozen({} as any), rowSpans: types.frozen({} as any), index: types.number, newIndex: types.number, path: '', // 行数据的位置 expandable: false, checkdisable: false, checked: false, // 是否选中 isHover: false, children: types.optional( types.array(types.late((): IAnyModelType => Row)), [] ), depth: types.number, // 当前children位于第几层,便于使用getParent获取最顶层TableStore }) .views(self => ({ get modified() { if (!self.data) { return false; } return Object.keys(self.data).some( key => !isEqual(self.data[key], self.pristine[key]) ); }, getDataWithModifiedChilden() { let data = { ...self.data }; if (data.children && self.children) { data.children = self.children.map(item => item.getDataWithModifiedChilden() ); } return data; }, get collapsed(): boolean { const table = getParent(self, self.depth * 2) as ITableStore; if (table.dragging) { return true; } let from: IRow = self as any; while (from && (from as any) !== table) { if (!table.isExpanded(from)) { return true; } from = getParent(from, 2); } return false; }, get expanded(): boolean { return !this.collapsed; }, get moved() { return self.index !== self.newIndex; }, get locals(): any { let children: Array<any> | null = null; if (self.children.length) { children = self.children.map(item => item.locals); } const parent = getParent(self, 2) as ITableStore; return createObject( extendObject((getParent(self, self.depth * 2) as ITableStore).data, { index: self.index, // todo 以后再支持多层,目前先一层 parent: parent.storeType === Row.name ? parent.data : undefined }), children ? { ...self.data, children } : self.data ); }, get checkable(): boolean { const table = getParent(self, self.depth * 2) as ITableStore; return table && table.itemCheckableOn ? evalExpression(table.itemCheckableOn, (self as IRow).locals) : true; }, get draggable(): boolean { const table = getParent(self, self.depth * 2) as ITableStore; return table && table.itemDraggableOn ? evalExpression(table.itemDraggableOn, (self as IRow).locals) : true; } })) .actions(self => ({ toggle() { (getParent(self, self.depth * 2) as ITableStore).toggle(self as IRow); }, toggleExpanded() { (getParent(self, self.depth * 2) as ITableStore).toggleExpanded( self as IRow ); }, change(values: object, savePristine?: boolean) { self.data = immutableExtends(self.data, values); savePristine && (self.pristine = self.data); }, reset() { self.newIndex = self.index; self.data = self.pristine; }, setCheckdisable(bool: boolean) { self.checkdisable = bool; }, setCheckded(bool: boolean) { self.checked = bool; }, setIsHover(value: boolean) { self.isHover = value; }, replaceWith(data: any) { Object.keys(data).forEach(key => { if (key !== 'id') { (self as any)[key] = data[key]; } }); if (Array.isArray(data.children)) { const arr = data.children; const pool = arr.concat(); // 把多的删了先 if (self.children.length > arr.length) { self.children.splice(arr.length, self.children.length - arr.length); } let index = 0; const len = self.children.length; while (pool.length) { // 因为父级id未更新,所以需要将子级的parentId正确指向父级id const item = { ...pool.shift(), parentId: self.id }!; if (index < len) { self.children[index].replaceWith(item); } else { const row = Row.create(item); self.children.push(row); } index++; } } } })); export type IRow = Instance<typeof Row>; export type SRow = SnapshotIn<typeof Row>; export const TableStore = iRendererStore .named('TableStore') .props({ visitMap: types.optional(types.frozen(), {}), rowSpanIndexsInterval: types.optional(types.frozen(), {}),// rowSpan区间记录 textWidhMap: types.optional(types.frozen(), {}), headTextWidhMap: types.optional(types.frozen(), {}), modefiedMap: types.optional(types.frozen(), { dataSetId: '', modifiedDataSet: {} }), // ui属性表格项目可见性 columns: types.array(Column), rawColumns: types.array(Column), // Jay rows: types.array(Row), expandedRows: types.array(types.string), primaryField: 'id', orderBy: '', orderDir: types.optional( types.union(types.literal('asc'), types.literal('desc')), 'asc' ), draggable: false, dragging: false, selectable: false, multiple: true, showIndex: false, footable: types.frozen(), expandConfig: types.frozen(), isNested: false, columnsTogglable: types.optional( types.union(types.boolean, types.literal('auto')), 'auto' ), itemCheckableOn: '', itemDraggableOn: '', hideCheckToggler: false, combineNum: 0, sortItemsRaw: types.optional(types.array(types.frozen()), []), combineFromIndex: 0, formsRef: types.optional(types.array(types.frozen()), []), maxKeepItemSelectionLength: 0, keepItemSelectionOnPageChange: false, // Jay stickyWidths: types.optional(types.frozen(), {}), mobileUI: false, //Aug, orderColumns: types.optional(types.frozen(), new Map<string, { order: string, map?: object }>()), filterColumns: types.optional(types.frozen(), new Map()), originColumns: types.optional(types.array(types.frozen()), []), tableLayout: '', tableExpandLength: 0, columnsInfo: types.optional(types.frozen(), {}) }) .views(self => { function getColumnsExceptBuiltinTypes() { return self.columns.filter(item => !/^__/.test(item.type)); } // Jay function getRawColumnsExceptBuiltinTypes() { return self.rawColumns.filter(item => !/^__/.test(item.type)); } function getForms() { return self.formsRef.map(item => ({ store: getStoreById(item.id) as IFormStore, rowIndex: item.rowIndex })); } function getFilteredColumns() { let filterCols = self.columns.filter( item => { // 提取公共条件 const commonConditions = self.selectable && !self.dragging && self.rows.length; const isMobileCheck = isMobile() ? true : !self.hideCheckToggler; const hasFootableColumnsOrNested = getFootableColumns().length || self.isNested; // return item && // isVisible( // item.pristine, // hasVisibleExpression(item.pristine) ? self.data : {} // ) && // (item.type === '__checkme' // ? self.selectable && // !self.dragging && // (isMobile() ? true : !self.hideCheckToggler) && // self.rows.length // : item.type === '__dragme' // ? self.dragging // : item.type === '__expandme' // ? (getFootableColumns().length || self.isNested) && !self.dragging // : (item.toggled || !item.toggable) && // (!self.footable || // !item.breakpoint || // !isBreakpoint(item.breakpoint))) if (!item || !isVisible( item.pristine, hasVisibleExpression(item.pristine) ? self.data : {} )) return false // 这几个是特殊展示的 switch (item.type) { case '__checkme': return commonConditions && isMobileCheck case '__dragme': return self.dragging case '__expandme': return hasFootableColumnsOrNested && !self.dragging case '__pseudoColumn': return self.showIndex default: return (item.toggled || !item.toggable) // && (!self.footable || // !item.breakpoint || // !isBreakpoint(item.breakpoint)) } } ); if (self.footable && isMobile() && self.tableLayout === 'vertical') { const expandedCol = filterCols.find(item => item.type === '__expandme'); filterCols = filterCols.filter(item => item.type !== '__expandme'); expandedCol && filterCols.push(expandedCol); } return filterCols; } function getFootableColumns() { return self.columns.filter(item => item.type === '__checkme' || item.type === '__dragme' || item.type === '__expandme' || item.type === '__pseudoColumn' ? false : (item.toggled || !item.toggable) && self.footable && item.breakpoint && isBreakpoint(item.breakpoint) ); } function getLeftFixedColumns() { if (self.dragging) { return []; } let columns = getFilteredColumns().filter(item => item.fixed === 'left'); // 有才带过去,没有就不带了 if (columns.length) { columns = getFilteredColumns().filter( item => item.fixed === 'left' || /^__/.test(item.type) ); } return columns; } function getRightFixedColumns() { if (self.dragging) { return []; } return getFilteredColumns().filter(item => item.fixed === 'right'); } function isSelected(row: IRow): boolean { return !!~getSelectedRows().indexOf(row); } function isExpanded(row: IRow): boolean { return self.expandedRows.includes(row.id); } function getTogglable() { if (self.columnsTogglable === 'auto') { return self.columns.filter(item => !/^__/.test(item.type)).length > 5; } return self.columnsTogglable; } function getToggableColumns() { return self.columns.filter( item => isVisible(item.pristine, self.data) && item.toggable !== false ); } function getActiveToggableColumns() { return getToggableColumns().filter(item => item.toggled); } function getModifiedRows(rows: IRow[] = [], modifiedRows: IRow[] = []): any[] { rows = rows && rows.length ? rows : self.rows; // 每次初始化的时候初始化的时候初始化修改map for (const item of rows) { if (item.children && item.children.length) { getModifiedRows(item.children, modifiedRows); } } // 如果没有修改的数据-有可能是前端分页导致的数据-尝试检查是否要更新编辑数据缓存 return Object.values(self.modefiedMap.modifiedDataSet); } function getFlatRows() { const ret: IRow[] = [] const fn = (rows: IRow[]) => { rows.forEach(row => { ret.push(row) row.expanded && row.children.length > 0 && fn(row.children) }) } fn(self.rows) return ret // return flattenTree<IRow>(self.rows) } function getModified() { return getModifiedRows().length; } function getMovedRows() { return flattenTree(self.rows).filter((item: IRow) => item.moved); } function getMoved() { return getMovedRows().length; } function getHovedRow(): IRow | undefined { return flattenTree<IRow>(self.rows).find((item: IRow) => item.isHover); } function getUnSelectedRows() { return flattenTree<IRow>(self.rows).filter((item: IRow) => !item.checked); } function getSelectedRows() { return flattenTree<IRow>(self.rows).filter((item: IRow) => item.checked); } function getData(superData: any): any { return createObject(superData, { items: self.rows.map(item => item.data), selectedItems: getSelectedRows().map(item => item.data), unSelectedItems: getUnSelectedRows().map(item => item.data) }); } function hasColumnHidden() { return self.columns.findIndex(column => !column.toggled) !== -1; } function getTableHeadRows() { const filtedColumns = getFilteredColumns() return getHeadRows(filtedColumns) } function getColumnGroup() { const columns = getFilteredColumns(); const len = columns.length; if (!len) { return []; } const groups: Array<{ label: string; index: number; colSpan: number; rowSpan: number; has: Array<any>; }> = [ { label: columns[0].groupName, colSpan: 1, rowSpan: 1, index: columns[0].index, has: [columns[0]] } ]; const needGroup = columns.find(item => !!item.groupName) if (needGroup) { for (let i = 1; i < len; i++) { let prev = groups[groups.length - 1]; const current = columns[i]; if (current.groupName && current.groupName === prev.label) { prev.colSpan++; prev.has.push(current); } else { groups.push({ label: current.groupName, colSpan: 1, rowSpan: 1, index: current.index, has: [current] }); } } } if (groups.length === 1 && !groups[0].label) { groups.pop(); } return groups.map(item => { const rowSpan = !item.label || (item.has.length === 1 && item.label === item.has[0].label) ? 2 : 1; return { ...item, rowSpan, label: rowSpan === 2 ? item.label || item.has[0].label : item.label }; }); } function getFirstToggledColumnIndex() { const column = self.columns.find( column => !/^__/.test(column.type) && column.toggled ); return column == null ? null : column.index; } function getSearchableColumns() { return self.columns.filter( column => column.searchable && isObject(column.searchable) ); } function getActivedSearchableColumns() { return self.columns.filter( column => column.searchable && isObject(column.searchable) && column.enableSearch ); } return { get columnsData() { return getColumnsExceptBuiltinTypes(); }, get rawColumnsData() { return getRawColumnsExceptBuiltinTypes(); }, get forms() { return getForms(); }, get searchableColumns() { return getSearchableColumns(); }, get activedSearchableColumns() { return getSearchableColumns().filter(column => column.enableSearch); }, get filteredColumns() { return getFilteredColumns(); }, get footableColumns() { return getFootableColumns(); }, get leftFixedColumns() { return getLeftFixedColumns(); }, get rightFixedColumns() { return getRightFixedColumns(); }, get toggableColumns() { return getToggableColumns(); }, get activeToggaleColumns() { return getActiveToggableColumns(); }, get someChecked() { return !!getSelectedRows().length; }, get allChecked(): boolean { return !!( getSelectedRows().length === (self as ITableStore).checkableRows.length && (self as ITableStore).checkableRows.length ); }, isSelected, get allExpanded() { return !!( self.expandedRows.length === self.tableExpandLength && self.tableExpandLength ); }, isExpanded, get toggable() { return getTogglable(); }, get modified() { return getModified(); }, get modifiedRows() { return getModifiedRows(); }, get unSelectedRows() { return getUnSelectedRows(); }, get selectedRows() { return getSelectedRows(); }, get checkableRows() { return flattenTree<IRow>(self.rows).filter( (item: IRow) => item.checkable ); }, get expandableRows() { return self.rows.filter(item => item.expandable); }, get moved() { return getMoved(); }, get movedRows() { return getMovedRows(); }, get hoverRow() { return getHovedRow(); }, get disabledHeadCheckbox() { const selectedLength = self.data?.selectedItems?.length; const maxLength = self.maxKeepItemSelectionLength; if (!self.data || !self.keepItemSelectionOnPageChange || !maxLength) { return false; } return maxLength === selectedLength; }, get firstToggledColumnIndex() { return getFirstToggledColumnIndex(); }, getData, get columnGroup() { return getColumnGroup(); }, get tableHeadRows() { return getTableHeadRows() }, get flatRows() { return getFlatRows() }, getRowById(id: string) { return findTree(self.rows, item => item.id === id); }, getItemsByName(name: string): any { return this.forms .filter(form => form.rowIndex === parseInt(name, 10)) .map(item => item.store); }, // 是否隐藏了某列 hasColumnHidden() { return hasColumnHidden(); }, getExpandedRows() { const list: Array<IRow> = []; eachTree(self.rows, i => { if (self.expandedRows.includes(i.id)) { list.push(i as any); } }); return list; } }; }) .actions(self => { function update(config: Partial<STableStore>) { config.primaryField !== void 0 && (self.primaryField = config.primaryField); config.selectable !== void 0 && (self.selectable = config.selectable); config.columnsTogglable !== void 0 && (self.columnsTogglable = config.columnsTogglable); config.draggable !== void 0 && (self.draggable = config.draggable); config.hideCheckToggler !== void 0 && (self.hideCheckToggler = !!config.hideCheckToggler); if (typeof config.orderBy === 'string') { setOrderByInfo( config.orderBy, config.orderDir === 'desc' ? 'desc' : 'asc' ); } config.orderColumns !== void 0 && (self.orderColumns = config.orderColumns) config.filterColumns !== void 0 && (self.filterColumns = config.filterColumns) config.multiple !== void 0 && (self.multiple = config.multiple); config.showIndex !== void 0 && (self.showIndex = config.showIndex); config.footable !== void 0 && (self.footable = config.footable); config.expandConfig !== void 0 && (self.expandConfig = config.expandConfig); config.itemCheckableOn !== void 0 && (self.itemCheckableOn = config.itemCheckableOn); config.itemDraggableOn !== void 0 && (self.itemDraggableOn = config.itemDraggableOn); config.hideCheckToggler !== void 0 && (self.hideCheckToggler = !!config.hideCheckToggler); config.combineNum !== void 0 && (self.combineNum = parseInt(config.combineNum as any, 10) || 0); config.combineFromIndex !== void 0 && (self.combineFromIndex = parseInt(config.combineFromIndex as any, 10) || 0); config.maxKeepItemSelectionLength !== void 0 && (self.maxKeepItemSelectionLength = config.maxKeepItemSelectionLength); config.keepItemSelectionOnPageChange !== void 0 && (self.keepItemSelectionOnPageChange = config.keepItemSelectionOnPageChange); // Aug config.mobileUI !== void 0 && (self.mobileUI = !!config.mobileUI); config.tableLayout && (self.tableLayout = config.tableLayout); config.columnsInfo !== void 0 && (self.columnsInfo = config.columnsInfo); if (config.columns && Array.isArray(config.columns)) { let columns: Array<SColumn> = config.columns .filter(column => column) .concat(); if (!columns.length) { columns.push({ type: 'text', label: '空' }); } //Aug // columns['unshift']({ // type: '__expandme', // toggable: false, // fixed: 'left', // className: 'Table-expandCell', // label: self.mobileUI ? false : undefined, // }); if (config.showIndex) { columns.unshift({ type: '__pseudoColumn', toggable: false, // Jay fixed: 'left', className: 'Table-pseudoColumn' }); } columns.unshift({ type: '__checkme', // fixed: 'left', fixed: self.mobileUI ? (config.tableLayout == 'horizontal' ? 'left' : undefined) : 'left',//Aug toggable: false, className: 'Table-checkCell', label: self.mobileUI ? false : undefined,//Aug }); columns.unshift({ type: '__dragme', toggable: false, className: 'Table-dragCell' }); columns = columns.map((item, index) => ({ ...item, index, rawIndex: index - (config.showIndex ? 4 : 3), type: item.type || 'plain', pristine: item, toggled: item.toggled !== false, breakpoint: item.breakpoint, isPrimary: index === (config.showIndex ? 4 : 3), className: item.className || '' })); self.columns.replace(columns as any); } // Jay if (config.rawColumns && Array.isArray(config.rawColumns)) { let columns: Array<SColumn> = config.rawColumns .filter(column => column) .concat(); if (!columns.length) { columns.push({ type: 'text', label: '空' }); } //Aug // columns['unshift']({ // type: '__expandme', // toggable: false, // fixed: 'left', // className: 'Table-expandCell', // label: self.mobileUI ? false : undefined, // }); if (config.showIndex) { columns.unshift({ type: '__pseudoColumn', toggable: false, // Jay fixed: 'left', className: 'Table-pseudoColumn' }); } columns.unshift({ type: '__checkme', // fixed: 'left', fixed: self.mobileUI ? (config.tableLayout == 'horizontal' ? 'left' : undefined) : 'left',//Aug toggable: false, className: 'Table-checkCell', label: self.mobileUI ? false : undefined,//Aug }); columns.unshift({ type: '__dragme', toggable: false, className: 'Table-dragCell' }); columns = columns.map((item, index) => ({ ...item, index, rawIndex: index - (config.showIndex ? 4 : 3), type: item.type || 'plain', pristine: item, toggled: item.toggled !== false, breakpoint: item.breakpoint, isPrimary: index === (config.showIndex ? 4 : 3), className: item.className || '' })); self.rawColumns.replace(columns as any); } } // 初始化 forced 是否强制更新 function initmodifiedMap(forced: boolean = false) { const modefiedMap = cloneDeep(self.modefiedMap) // 如果强制更新 或者 datasetid发生变化 即数据源发生变化 就进行初始化 if (forced || (self.data.dataSetId !== modefiedMap.dataSetId)) { if (self.data.dataSetId !== modefiedMap.dataSetId) modefiedMap.dataSetId = self.data.dataSetId // 即数据源发生变化更新数据源id // 发现数据集更新了就进行修改数据的更新 modefiedMap.modifiedDataSet = {} // 强制更新一下视图 } self.modefiedMap = modefiedMap } function updateColumns(columns: Array<SColumn>) { if (columns && Array.isArray(columns)) { columns = columns.filter(column => column).concat(); if (!columns.length) { columns.push({ type: 'text', label: '空' }); } // columns.unshift({ // type: '__expandme', // toggable: false, // // Jay // fixed: 'left', // className: 'Table-expandCell' // }); if (self.showIndex) { columns.unshift({ type: '__pseudoColumn', toggable: false, // Jay fixed: 'left', className: 'Table-pseudoColumn' }); } columns.unshift({ type: '__checkme', fixed: self.mobileUI ? (self.tableLayout == 'horizontal' ? 'left' : undefined) : 'left', toggable: false, className: 'Table-checkCell' }); columns.unshift({ type: '__dragme', toggable: false, // Jay fixed: 'left', className: 'Table-dragCell' }); columns = columns.map((item, index) => ({ ...item, index, rawIndex: index - (self.showIndex ? 4 : 3), type: item.type || 'plain', pristine: item.pristine || item, toggled: item.toggled !== false, breakpoint: item.breakpoint, isPrimary: index === (self.showIndex ? 4 : 3) })); self.columns.replace(columns as any); } } // 更新折叠操作列 function updateOperation(foldColumns: string[]) { const temp = self.columns.map((col) => { const item = col.pristine if (item?.type === 'operation') { const props = { style: { overFlow: 'hidden' }, label: [ { type: 'button', label: '', size: 'xs', level: 'link', icon: foldColumns?.includes('scale' + item.name) ? 'fa fa-step-backward' : 'fa fa-step-forward', actionType: 'scale' + item.name }, item.label[1] ] } return { ...col, ...props, pristine: { ...item, ...props, } } } return col }) self.columns.replace(temp); } function combineCell(arr: Array<SRow>, keys: Array<string>): Array<SRow> { if (!keys.length || !arr.length) { return arr; } const key: string = keys.shift() as string; let rowIndex = 0; let row = arr[rowIndex]; row.rowSpans[key] = 1; // 区间数组 const currentInterval = [] let value = resolveVariable(key, row.data); for (let i = 1, len = arr.length; i < len; i++) { const current = arr[i]; // 合并部分 if (isEqual(resolveVariable(key, current.data), value)) { row.rowSpans[key] += 1; current.rowSpans[key] = 0; } else { if (row.rowSpans[key] > 1) { // 先行记录一次 combineCell(arr.slice(rowIndex, i), keys.concat()); } rowIndex = i; row = current; row.rowSpans[key] = 1; value = resolveVariable(key, row.data); } } // 区间对象 //走完数组 if (row.rowSpans[key] > 1 && keys.length) { combineCell(arr.slice(rowIndex, arr.length), keys.concat()); } return arr; } function autoCombineCell( arr: Array<SRow>, columns: Array<IColumn>, maxCount: number, fromIndex = 0 ): Array<SRow> { if (!columns.length || !maxCount || !arr.length) { return arr; } // 如果是嵌套模式,通常第一列都是存在差异的,所以从第二列开始。 fromIndex = fromIndex || (arr.some(item => Array.isArray(item.children) && item.children.length) ? 1 : 0); const keys: Array<string> = []; const len = columns.length; for (let i = 0; i < len; i++) { const column = columns[i]; // maxCount 可能比实际配置的 columns 还有多。 if (!column) { break; } if ('__' === column.type.substring(0, 2)) { maxCount++; continue; } const key = column.name; if (!key) { break; } keys.push(key); } while (fromIndex--) { keys.shift(); } while (keys.length > maxCount) { keys.pop(); } return combineCell(arr, keys); } function initChildren( children: Array<any>, depth: number, pindex: number, parentId: string, path: string = '' ): any { depth += 1; return children.map((item, index) => { item = isObject(item) ? item : { item }; const id = guid(); return { // id: String(item && (item as any)[self.primaryField] || `${pindex}-${depth}-${key}`), id: id, parentId, key: String(`${pindex}-${depth}-${index}`), path: `${path}${index}`, depth: depth, index: index, newIndex: index, pristine: item, data: item, rowSpans: {}, children: item && Array.isArray(item.children) ? initChildren( item.children, depth, index, id, `${path}${index}.` ) : [], expandable: !!( (item && Array.isArray(item.children) && item.children.length) || (self.footable && self.footableColumns.length) ) }; }); } function setTextWidth(textWidhMap: any) { self.textWidhMap = textWidhMap } function initRows( rows: Array<any>, getEntryId?: (entry: any, index: number) => string, reUseRow?: boolean, option?: { caculateWidth: boolean, autoUnfold: number }, ) { // clearSelectItem() window['getTableRows'] = () => { // 生产调试用的方法 if (window['onDebugMode']) { return self } } // self.expandedRows.clear(); let arr: Array<SRow> = [] // 更新了数据源也进行重新计算 // 移动端每次都算 移动端不会更新数据源 if (isMobile() || !isNil(option?.caculateWidth)) { self.textWidhMap = {} } for (const index in rows) { const item = rows[index] let id = getEntryId ? getEntryId(item, +index) : guid(); arr.push({ // id: getEntryId ? getEntryId(item, key) : String(item && (item as any)[self.primaryField] || `${key}-1-${key}`), id: id, key: String(`${index}-1-${index}`), depth: 1, // 最大父节点默认为第一层,逐层叠加 index: +index, newIndex: +index, pristine: item, path: `${index}`, data: item, checked: selectedIds.includes(item[DATAKEYID]), rowSpans: {}, children: item && Array.isArray(item.children) ? initChildren(item.children, 1, +index, id, `${index}.`) : [], expandable: !!( (item && Array.isArray(item.children) && item.children.length) || (self.footable && self.footableColumns.length) ) }) } if (self.combineNum) { arr = autoCombineCell( arr, self.columns, self.combineNum, self.combineFromIndex ); const rowSpanIndexsInterval = {} // 遍历计算-rowspan区间 arr.map(_ => { Object.keys(_.rowSpans).map(key => { // 如果有rowSpan if (_.rowSpans[key]) { rowSpanIndexsInterval[key] = rowSpanIndexsInterval[key] || [0] rowSpanIndexsInterval[key].push(_.rowSpans[key] + (last(rowSpanIndexsInterval[key]) || 0)) } }) }) self.rowSpanIndexsInterval = { ...rowSpanIndexsInterval } } // 有更新数据才走这里 if (self.modifiedRows.length) // 合并修改信息到新数组 for (const item of arr) { const dataId = item.data[DATAKEYID] const modifiedData = !!dataId && self.modefiedMap.modifiedDataSet[dataId] if (modifiedData) { item.data = { ...modifiedData } } } replaceRow(arr, reUseRow); self.isNested = self.rows.some(item => item.children.length); const expand = self.footable && self.footable.expand; if ( expand === 'first' || (self.expandConfig && self.expandConfig.expand === 'first') ) { self.rows.length && self.expandedRows.push(self.rows[0].id); } else if ( (expand === 'all' && !self.footable.accordion) || (self.expandConfig && self.expandConfig.expand === 'all' && !self.expandConfig.accordion) ) { self.expandedRows.replace(self.rows.map(item => item.id)); } // 如果有 autoUnfold 字段 if (!isNil(option?.autoUnfold)) { self.expandedRows.replace(getToggleExpandLevelIds(option?.autoUnfold || 0)) } // 获取展开后总长度 self.tableExpandLength = getToggleExpandLevelIds(Infinity).length // console.log(self, option?.autoUnfold, getToggleExpandLevelIds(option?.autoUnfold || 0), self.rows) self.dragging = false; } // 尽可能的复用 row function replaceRow(arr: Array<SRow>, reUseRow?: boolean) { if (reUseRow === false) { self.rows.replace(arr.map(item => Row.create(item))); return; } const pool = arr.concat(); // 把多的删了先 if (self.rows.length > arr.length) { self.rows.splice(arr.length, self.rows.length - arr.length); } let index = 0; const len = self.rows.length; while (pool.length) { const item = pool.shift()!; if (index < len) { self.rows[index].replaceWith(item); } else { const row = Row.create(item); self.rows.push(row); } index++; } } function updateSelected(selected: Array<any>, valueField?: string) { clearSelectItem() self.rows.forEach(item => { if (~selected.indexOf(item.pristine)) { item.setCheckded(true) selectedIds.push(item.data[DATAKEYID]) } else if ( find( selected, a => a[valueField || 'value'] && a[valueField || 'value'] == item.pristine[valueField || 'value'] ) ) { item.setCheckded(true) selectedIds.push(item.data[DATAKEYID]) } }); updateCheckDisable(); } function toggleAll() { const maxLength = self.maxKeepItemSelectionLength; const keep = self.keepItemSelectionOnPageChange; if (self.allChecked) { clearSelectItem() } else { const selectedItems = self.data?.selectedItems; if ( keep && maxLength && selectedItems && maxLength >= selectedItems.length ) { // 剩下的没有被选中的项 const restCheckableRows = self.checkableRows.filter( item => !item.checked ); // 没被选中的项中,下标小于最大允许选中项数量 const checkableRows = restCheckableRows.filter( (item, i) => i < maxLength - selectedItems.length ); self.checkableRows.map((item, index) => { // 如果存在可以选中的项 if (checkableRows[index]) { item.setCheckded(true) selectedIds.push(item.data[DATAKEYID]) } }); // self.selectedRows.replace([...self.selectedRows, ...checkableRows]); } else { // 全选可选中的项 self.checkableRows.map(item => { item.setCheckded(true) selectedIds.push(item.data[DATAKEYID]) }); } } } // 全部反选 function checkReverse() { self.rows.forEach(item => { if (item.checkable) { if (item.checked) { const idx = selectedIds.indexOf(item.data[DATAKEYID]) selectedIds.splice(idx, 1) } else { selectedIds.push(item.data[DATAKEYID]) } item.setCheckded(!item.checked) } }); } function checkAll() { if (!self.allChecked) { const maxLength = self.maxKeepItemSelectionLength; const keep = self.keepItemSelectionOnPageChange; const selectedItems = self.data?.selectedItems; if ( keep && maxLength && selectedItems && maxLength >= selectedItems.length ) { // 剩下的没有被选中的项 const restCheckableRows = self.checkableRows.filter( item => !item.checked ); // 没被选中的项中,下标小于最大允许选中项数量 const checkableRows = restCheckableRows.filter( (item, i) => i < maxLength - selectedItems.length ); //可选中的,下标小于最大允许选中项数量未选中全选 self.checkableRows.map((item, index) => { // 如果存在可以选中的项 if (checkableRows[index]) { item.setCheckded(true) selectedIds.push(item.data[DATAKEYID]) } }); } else { // 可选中的全选 self.checkableRows.map(item => { item.setCheckded(true) selectedIds.push(item.data[DATAKEYID]) }); } } } function dealHeaderValue() { return self.columnsData.map(column => { if (!column.pristine?.hidden) { if (!(column.label instanceof Array)) { const groupName = column.groupName ?? '' return `${groupName ? (groupName + '·') : ''}${column.label.replaceAll('\n', '').replaceAll('\r', '')}` } } return undefined }).filter(v => v !== undefined).join('\t') } function dealCellValue(originValue: any, columnName: string, clearFormat: boolean = false, originColumn?: any, isCopyCell?: boolean) { const column = originColumn || self.columnsData.find(column => column.name === columnName) return getCellValue(originValue, column, clearFormat, isCopyCell) } function getCopyCellValue(rowIndex: number, columnName: string, clearFormat: boolean = false) { const items = self.rows.map(row => row.data) const value = items[rowIndex][columnName] return dealCellValue(value, columnName, clearFormat, undefined, true) } function getCopyRowValue(rowIndex: number, hasHead: boolean = false, clearFormat: boolean = false) { const columnFields = self.columnsData.filter(column => !column.pristine?.hidden).map(column => column.name) as string[] if (self.selectedRows.length > 0) { const items = self.selectedRows.map(rows => rows.data) const rowValues = items.map((item: any) => columnFields.map(field => dealCellValue(item[field], field, clearFormat)).join('\t')).join('\n') return hasHead ? dealHeaderValue() + '\n' + rowValues : rowValues } else { const items = self.rows.map(row => row.data) const rowValue = columnFields.map(field => dealCellValue(items[rowIndex][field], field, clearFormat)).join('\t') return hasHead ? dealHeaderValue() + '\n' + rowValue : rowValue } } function getCopyColumnValue(columnNames: string | string[], clearFormat: boolean = false): string { const items = self.selectedRows.length > 0 ? self.selectedRows.map(row => row.data) : self.rows.map(row => row.data) if (Array.isArray(columnNames)) { return items.map((item: any) => columnNames.map((name) => dealCellValue(item[name], name, clearFormat)).join('\t')).join('\n') } else { return items.map((item: any) => dealCellValue(item[columnNames], columnNames, clearFormat)).join('\n') } } // allpage: 前端分页下复制全部数据 function getCopyPageValue(allPage = false, clearFormat: boolean = false) { const items = allPage ? self.data.itemsRaw.slice() : self.rows.map(row => row.data) //复制全部数据时先排序 const orderMap = self.orderColumns const temp = allPage ? handleSort(items.slice(), Array.from(orderMap.keys()), orderMap) : items.slice() const columnFields = self.columnsData.filter(column => !column.pristine?.hidden) // 复制所有数据的时候移出每一行的引号 防止被excel误认 const value = temp.map((item: obj) => columnFields.map((column) => dealCellValue(item[column.name || ''], column.name || '', clearFormat, column).replace(/'|"/, '')).join('\t')) return dealHeaderValue() + '\n' + value.join('\n'); } // 记录最近一次点击的多选框,主要用于 shift 多选时判断上一个选的是什么 let lastCheckedRow: any = null; const selectedIds: string[] = [] function toggle(row: IRow) { if (!row.checkable) { return; } lastCheckedRow = row; // 如果是多选 进行反选 if (self.multiple) { row.setCheckded(!row.checked) if (row.checked) { selectedIds.push(row.data[DATAKEYID]) } else { const idx = selectedIds.indexOf(row.data[DATAKEYID]) selectedIds.splice(idx, 1) } } else { // 如果是单选 移除其他的再选择一个 // 选中说明已经有一个选中 if (row.checked) { row.setCheckded(false) const idx = selectedIds.indexOf(row.data[DATAKEYID]) selectedIds.splice(idx, 1) } else { // 清除其他选中的 self.selectedRows.map(item => { item.setCheckded(false) const idx = selectedIds.indexOf(item.data[DATAKEYID]) selectedIds.splice(idx, 1) }) row.setCheckded(true) selectedIds.push(row.data[DATAKEYID]) } } } // 按住 shift 的时候点击选项 function toggleShift(row: IRow) { // 如果是同一个或非 multiple 模式下就和不用 shift 一样 if (!lastCheckedRow || row === lastCheckedRow || !self.multiple) { toggle(row); return; } const maxLength = self.maxKeepItemSelectionLength; const checkableRows = self.checkableRows; const lastCheckedRowIndex = checkableRows.findIndex( row => row === lastCheckedRow ); const rowIndex = checkableRows.findIndex(rowItem => row === rowItem); const minIndex = lastCheckedRowIndex > rowIndex ? rowIndex : lastCheckedRowIndex; const maxIndex = lastCheckedRowIndex > rowIndex ? lastCheckedRowIndex : rowIndex; const rows = checkableRows.slice(minIndex, maxIndex); rows.push(row); // 将当前行也加入进行判断 for (const rowItem of rows) { const idx = self.selectedRows.indexOf(rowItem); if (idx === -1) { // 如果上一个是选中状态,则将之间的所有 check 都变成可选 if (lastCheckedRow.checked) { if (maxLength) { if (self.selectedRows.length < maxLength) { rowItem.setCheckded(true) selectedIds.push(rowItem.data[DATAKEYID]) } } else { rowItem.setCheckded(true) selectedIds.push(rowItem.data[DATAKEYID]) } } } else { if (!lastCheckedRow.checked) { rowItem.setCheckded(false) const idx = selectedIds.indexOf(rowItem.data[DATAKEYID]) selectedIds.splice(idx, 1) } } } lastCheckedRow = row; } /** *@description 清空已选项 *@date 2023-08-16 */ function clearSelectItem() { self.selectedRows.map(item => item.setCheckded(false)); selectedIds.splice(0, selectedIds.length) } function updateCheckDisable() { if (!self.data) { return; } const maxLength = self.maxKeepItemSelectionLength; const selectedItems = self.data.selectedItems; self.selectedRows.map(item => item.setCheckdisable(false)); if (maxLength && maxLength <= selectedItems.length) { self.unSelectedRows.map( item => !item.checked && item.setCheckdisable(true) ); } else { self.unSelectedRows.map( item => item.checkdisable && item.setCheckdisable(false) ); } } function clear() { clearSelectItem() } // 获取指定层级下需要打开的按钮 function getToggleExpandLevelIds(level: number) { const allRowStack: string[] = [] const getAllIds = (arr: any[]) => { for (const item of arr) { allRowStack.push(item.id) // 如果有子节点的情况并且需要打开 if (level - item.depth > 1 && item.children) { // 递归展开所有id getAllIds(item.children) } } } getAllIds(self.rows) return allRowStack } function toggleExpandAll() { // row 堆栈方便处理所有row及其子row if (self.allExpanded) { self.expandedRows.clear(); } else { const expandIdArr = getToggleExpandLevelIds(Infinity) // 设置一下总 self.expandedRows.replace( expandIdArr ); } } function toggleExpanded(row: IRow) { const idx = self.expandedRows.indexOf(row.id); if (~idx) { self.expandedRows.splice(idx, 1); } else if (self.footable && self.footable.accordion) { self.expandedRows.replace([row.id]); } else if (self.expandConfig && self.expandConfig.accordion) { let rows = self .getExpandedRows() .filter(item => item.depth !== row.depth); rows.push(row); self.expandedRows.replace(rows.map(item => item.id)); } else { self.expandedRows.push(row.id); } } function collapseAllAtDepth(depth: number) { let rows = self.getExpandedRows().filter(item => item.depth !== depth); self.expandedRows.replace(rows.map(item => item.id)); } function setOrderByInfo(key: string, direction: 'asc' | 'desc') { self.orderBy = key; self.orderDir = direction; } // 强制赋予数据源id function updateDataSetId(dataSetId: string) { if (self.modefiedMap.dataSetId !== dataSetId) { initmodifiedMap() } } function handleMutilSort(loadDataOnce: boolean = false, caseSensitive = false) { const orderMap = self.orderColumns const filterMap = self.filterColumns const { page = 1, perPage = self.data.itemRaws.length } = self.data let items = loadDataOnce ? self.data.itemsRaw.slice() : self.data.items.slice() if (orderMap.size > 0) { items = handleSort(items, Array.from(orderMap.keys()), orderMap, self.modefiedMap.modifiedDataSet) } if (filterMap.size > 0) { items = handleFilter(items, Array.from(filterMap.keys()), filterMap, caseSensitive) } const result = loadDataOnce ? items.slice((page - 1) * perPage, page * perPage) : items.slice() initRows(result) return items } function bulkQuickChange(changedRows: typeof self.rows, changeList: any[]) { changedRows.forEach((item, index) => { item.data = changeList[index].rowData }); } function reset() { initmodifiedMap(true) self.rows.forEach(item => item.reset()); EventSub.emit(EventEnum.ClearVistiMap) let rows = self.rows.conca