UNPKG

e-virt-table

Version:

A powerful data table based on canvas. You can use it as data grid、Microsoft Excel or Google sheets. It supports virtual scroll、cell edit etc.

1,380 lines 45.1 kB
import Schema from 'async-validator'; import { generateShortUUID } from './util'; import Cell from './Cell'; export default class Database { constructor(ctx, options) { Object.defineProperty(this, "loading", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "ctx", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "data", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "columns", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "footerData", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "rowKeyMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "colIndexKeyMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "headerMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "rowIndexRowKeyMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "rowKeyRowIndexMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "checkboxKeyMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "selectionMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "originalDataMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "changedDataMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "validationErrorMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "itemRowKeyMap", { enumerable: true, configurable: true, writable: true, value: new WeakMap() }); Object.defineProperty(this, "bufferData", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "bufferCheckState", { enumerable: true, configurable: true, writable: true, value: { buffer: false, check: false, indeterminate: false, selectable: true, } }); Object.defineProperty(this, "sumHeight", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "filterMethod", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "positions", { enumerable: true, configurable: true, writable: true, value: [] }); //虚拟滚动位置 this.ctx = ctx; const { data = [], columns = [], footerData = [] } = options; this.data = data; this.footerData = footerData; this.columns = columns; this.setLoading(true); this.init(); } init() { this.clearBufferData(); this.rowKeyMap.clear(); this.checkboxKeyMap.clear(); this.colIndexKeyMap.clear(); this.rowIndexRowKeyMap.clear(); this.rowKeyRowIndexMap.clear(); this.originalDataMap.clear(); this.changedDataMap.clear(); this.validationErrorMap.clear(); this.itemRowKeyMap = new WeakMap(); const { ROW_KEY, ENABLE_RESERVE_SELECTION } = this.ctx.config; // 没有跨页选时清除 if (!(ENABLE_RESERVE_SELECTION && ROW_KEY)) { this.selectionMap.clear(); } this.initData(this.data); this.getData(); this.bufferCheckState.buffer = false; } /** * 清除缓存数据 */ clearBufferData() { this.bufferData = []; } /** * 初始化数据 * @param dataList * @param level */ initData(dataList, level = 0) { dataList.forEach((item, index) => { let hasChildren = item._hasChildren || false; if (Array.isArray(item.children) && item.children.length) { hasChildren = true; this.initData(item.children, level + 1); } const { ROW_KEY = '', DEFAULT_EXPAND_ALL, CELL_HEIGHT, SELECTABLE_METHOD, CHECKBOX_KEY } = this.ctx.config; const _rowKey = item[ROW_KEY]; // 行唯一标识,否则就rowKey const rowKey = _rowKey !== undefined && _rowKey !== null ? `${_rowKey}` : generateShortUUID(); this.itemRowKeyMap.set(item, rowKey); const height = item._height || CELL_HEIGHT; const readonly = item._readonly; let selectable = true; if (typeof SELECTABLE_METHOD === 'function') { const selectableMethod = SELECTABLE_METHOD; selectable = selectableMethod; } // 存储checkboxKey,处理合并单元格选中 if (CHECKBOX_KEY) { const checkboxKey = item[CHECKBOX_KEY]; if (this.checkboxKeyMap.has(checkboxKey)) { const checkboxKeys = this.checkboxKeyMap.get(checkboxKey) || []; checkboxKeys.push(rowKey); this.checkboxKeyMap.set(checkboxKey, checkboxKeys); } else { this.checkboxKeyMap.set(checkboxKey, [rowKey]); } } // 存key this.selectionMap.set(rowKey, { key: CHECKBOX_KEY ? item[CHECKBOX_KEY] : rowKey, row: item, check: this.selectionMap.get(rowKey)?.check || false, }); this.rowKeyMap.set(rowKey, { readonly, index, rowIndex: index, level, height, check: false, selectable, expand: DEFAULT_EXPAND_ALL, expandLazy: false, hasChildren, expandLoading: false, item, }); }); } /** * * @param rowKey 设置Row高度 * @param height */ setRowHeight(rowIndex, height) { const rowKey = this.rowIndexRowKeyMap.get(rowIndex); if (rowKey === undefined) { return; } const row = this.rowKeyMap.get(rowKey); row.height = height; row.item._height = height; this.clearBufferData(); // 清除缓存数据 } /** * * @returns 获取转化平铺数据 */ getAllRowsData() { let list = []; const recursiveData = (data) => { data.forEach((item) => { list.push(item); if (Array.isArray(item.children)) { recursiveData(item.children); } }); }; recursiveData(this.data); return list; } filterColumns(columns) { return columns.reduce((acc, column) => { // 检查当前列的 hide 属性 const shouldHide = typeof column.hide === 'function' ? column.hide() : column.hide; // 如果当前列不应该隐藏,则添加到结果中 if (!shouldHide) { const newColumn = { ...column }; // 复制当前列 // 递归处理子列 if (newColumn.children && Array.isArray(newColumn.children)) { newColumn.children = this.filterColumns(newColumn.children); } acc.push(newColumn); } return acc; }, []); } getColumns() { return this.filterColumns(this.columns); } setColumns(columns) { this.columns = columns; this.clearBufferData(); } setData(data) { this.data = data; if (this.columns.length) { this.init(); } } /** * 统一转化数据,给画body使用,包括过滤树状等,统一入口 * @returns */ getData() { if (this.bufferData.length > 0) { return { data: this.bufferData, sumHeight: this.sumHeight, positions: this.positions, }; } let list = []; let rowIndex = 0; this.sumHeight = 0; this.positions = []; const recursiveData = (data) => { data.forEach((item) => { list.push(item); const rowKey = this.itemRowKeyMap.get(item); const { expand, hasChildren, height } = this.rowKeyMap.get(rowKey); const top = this.sumHeight; this.sumHeight += height; this.rowIndexRowKeyMap.set(rowIndex, rowKey); this.rowKeyRowIndexMap.set(rowKey, rowIndex); this.positions.push({ top, height, bottom: this.sumHeight, }); rowIndex += 1; if (expand && hasChildren) { recursiveData(item.children); } }); }; this.rowIndexRowKeyMap.clear(); this.rowKeyRowIndexMap.clear(); let _data = this.data; if (typeof this.filterMethod === 'function') { _data = this.filterMethod(_data); } recursiveData(_data); this.bufferData = list; return { data: list, sumHeight: this.sumHeight, positions: this.positions, }; } setFooterData(data) { this.footerData = data; } getFooterData() { return this.footerData; } /** * 设置过滤方法 */ setFilterMethod(filterMethod) { this.filterMethod = filterMethod; } /** * 清空过滤方法 */ clearFilterMethod() { this.filterMethod = undefined; } /** * 根据rowKey,控制指定展开行 * @param rowKey * @param expand */ expandItem(rowKey, expand = false) { const row = this.rowKeyMap.get(rowKey); row.expand = expand; this.clearBufferData(); // 清除缓存数据 this.ctx.emit('draw'); } setExpandRowKeys(rowKeys, expand = true) { rowKeys.forEach((rowkey) => { const row = this.rowKeyMap.get(rowkey); row.expand = expand; }); this.clearBufferData(); // 清除缓存数据 this.ctx.emit('draw'); } getExpandRowKeys() { let list = []; this.rowKeyMap.forEach((row, key) => { if (row.expand) { list.push(key); } }); return list; } expandAll(expand) { this.rowKeyMap.forEach((row) => { row.expand = expand; }); this.clearBufferData(); // 清除缓存数据 this.ctx.emit('draw'); } expandLoading(rowKey, loading = false) { const row = this.rowKeyMap.get(rowKey); row.expandLoading = loading; this.clearBufferData(); // 清除缓存数据 this.ctx.emit('draw'); } setExpandChildren(rowKey, children) { const row = this.rowKeyMap.get(rowKey); row.expand = true; row.expandLazy = true; row.item.children = children; this.initData(row.item.children, row.level + 1); this.clearBufferData(); // 清除缓存数据 } getIsExpandLoading(rowKey) { const row = this.rowKeyMap.get(rowKey); return row.expandLoading; } getIsExpandLazy(rowKey) { const row = this.rowKeyMap.get(rowKey); return row.expandLazy; } /** * 根据rowKey获取是否展开 * @param rowKey * @returns */ getIsExpand(rowKey) { const row = this.rowKeyMap.get(rowKey); return row.expand; } /** * 根据rowKey获取行数据 * @param rowKey * @returns */ getRowForRowKey(rowKey) { return this.rowKeyMap.get(rowKey); } getRowForRowIndex(rowIndex) { const rowKey = this.getRowKeyForRowIndex(rowIndex); return this.rowKeyMap.get(rowKey); } /** * 根据rowIndex获取rowKey * @param rowKey * @returns */ getRowKeyForRowIndex(rowIndex) { return this.rowIndexRowKeyMap.get(rowIndex) || ''; } getRowKeyByItem(item) { return this.itemRowKeyMap.get(item); } getRowIndexForRowKey(rowKey) { return this.rowKeyRowIndexMap.get(rowKey); } /** * 根据rowIndex和colIndex获取单元格数据 * @param rowIndex * @param colIndex * @returns */ getItemValueForRowIndexAndColIndex(rowIndex, colIndex) { const has = this.rowIndexRowKeyMap.has(rowIndex) && this.colIndexKeyMap.get(colIndex); if (!has) { return null; } const rowKey = this.rowIndexRowKeyMap.get(rowIndex); const key = this.colIndexKeyMap.get(colIndex); if (rowKey === undefined || key === undefined) { return null; } return { rowKey, key, value: this.getItemValue(rowKey, key), }; } /** * 根据rowKey和key获取单元格数据 * @param rowKey * @param key * @returns */ getItemValue(rowKey, key) { const row = this.rowKeyMap.get(rowKey); if (row && row.item) { if (row.item[key] === undefined) { return null; } return row.item[key]; } return null; } /** * 批量设置数据 * @param list * @param history * @returns */ async batchSetItemValue(_list, history = false) { let historyList = []; const rowKeyList = new Set(); let errList = []; let changeList = _list.map((item) => { const { rowKey, key } = item; let _value = item.value; let value = _value; const row = this.getRowDataItemForRowKey(rowKey); const oldValue = this.getItemValue(rowKey, key); // 判断数字 const cell = this.getVirtualBodyCellByKey(rowKey, key); if (cell?.type === 'number') { // 处理数字 if (['', undefined, null].includes(_value)) { value = null; } else if (/^-?\d+(\.\d+)?$/.test(`${_value}`)) { value = Number(_value); } else { value = oldValue; errList.push({ ...item, value, oldValue, row, }); } } return { ...item, value, oldValue, row, }; }); // 过滤错误的 changeList = changeList.filter((item) => { return !errList.some((err) => item.rowKey === err.rowKey && item.key === err.key); }); if (errList.length) { const err = { code: 'ERR_BATCH_SET_NUMBER_VALUE', message: 'Assignment failed, not a numeric type', data: errList, }; this.ctx.emit('error', err); } if (!changeList.length) { return; } const { BEFORE_VALUE_CHANGE_METHOD } = this.ctx.config; if (typeof BEFORE_VALUE_CHANGE_METHOD === 'function') { const beforeCellValueChange = BEFORE_VALUE_CHANGE_METHOD; const values = await beforeCellValueChange(changeList); changeList = values; } changeList.forEach((data) => { const { value, rowKey, key, oldValue } = data; rowKeyList.add(rowKey); this.setItemValue(rowKey, key, value); historyList.push({ rowKey, key, oldValue, newValue: value, }); }); // 触发change事件 let rows = []; rowKeyList.forEach((rowKey) => { rows.push(this.ctx.database.getRowDataItemForRowKey(rowKey)); }); const promsieValidators = changeList.map(({ rowKey, key }) => this.getValidator(rowKey, key)); Promise.all(promsieValidators).then(() => { if (this.validationErrorMap.size === 0 && this.changedDataMap.size > 0) { this.ctx.emit('validateChangedData', this.getChangedData()); } }); this.ctx.emit('change', changeList, rows); // 推历史记录 if (history) { this.ctx.history.pushState({ changeList: historyList, scrollX: this.ctx.scrollX, scrollY: this.ctx.scrollY, type: 'multiple', }); } this.ctx.emit('draw'); } /** *设置单一数据 * @param rowKey * @param key * @param value * @param history 是否添加历史记录 * @param reDraw 是否刷新重绘 * @param isEditor 是否是编辑器 * @returns */ async setItemValue(rowKey, key, _value, history = false, reDraw = false, isEditor = false) { // 异常情况 if (!this.rowKeyMap.has(rowKey)) { return {}; } const { item } = this.rowKeyMap.get(rowKey); let oldValue = item[key]; let value = _value; // 只读返回旧值 if (this.ctx.database.getReadonly(rowKey, key)) { return { oldValue, newValue: oldValue, }; } // 处理对象 if (item[key] !== null && typeof item[key] === 'object') { oldValue = JSON.parse(JSON.stringify(item[key])); } const changeKey = `${rowKey}\u200b_${key}`; // 设置原始值,只设置一次 if (!this.originalDataMap.has(changeKey)) { this.originalDataMap.set(changeKey, oldValue); } const originalValue = this.originalDataMap.get(changeKey); const row = this.getRowDataItemForRowKey(rowKey); // 是否是否是编辑器进来的 if (isEditor) { const cell = this.getVirtualBodyCellByKey(rowKey, key); if (cell?.type === 'number') { // 处理数字 if (['', undefined, null].includes(_value)) { value = null; } else if (/^-?\d+(\.\d+)?$/.test(`${_value}`)) { value = Number(_value); } else { value = oldValue; const err = { code: 'ERR_SET_NUMBER_VALUE', message: 'Assignment failed, not a numeric type', data: [ { rowKey, key, value, oldValue, row, }, ], }; this.ctx.emit('error', err); } } if (value === oldValue) { return { oldValue, newValue: oldValue, }; } const { BEFORE_VALUE_CHANGE_METHOD } = this.ctx.config; if (typeof BEFORE_VALUE_CHANGE_METHOD === 'function') { const beforeCellValueChange = BEFORE_VALUE_CHANGE_METHOD; const values = await beforeCellValueChange([ { rowKey, key, value, oldValue: item[key], row, }, ]); if (values && values.length) { value = values[0].value; } } // 设置改变值 this.changedDataMap.set(changeKey, value); item[key] = value; const changeItem = { rowKey, key, oldValue, value, row, }; // 实时校验错误 this.getValidator(rowKey, key).then(() => { if (this.validationErrorMap.size === 0 && this.changedDataMap.size > 0) { this.ctx.emit('validateChangedData', this.getChangedData()); } }); this.ctx.emit('change', [changeItem], [row]); this.ctx.emit('editChange', { rowKey, key, oldValue, value, originalValue, row, }); } else { this.changedDataMap.set(changeKey, value); item[key] = value; } // 迭代改变值事件,有改变一次值就触发,包括批量的 if (this.ctx.hasEvent('iterationChange')) { this.ctx.emit('iterationChange', { rowKey, key, oldValue, value, originalValue: this.originalDataMap.get(changeKey), row, }); } // 添加历史记录 if (history) { this.ctx.history.pushState({ type: 'single', scrollX: this.ctx.scrollX, scrollY: this.ctx.scrollY, changeList: [ { rowKey, key, oldValue, newValue: value, }, ], }); } // 重绘 if (reDraw) { this.ctx.emit('draw'); } return { oldValue, newValue: value, }; } /** * 根据rowKey 获取行数据 * @param rowKey * @returns */ getRowDataItemForRowKey(rowKey) { if (!this.rowKeyMap.has(rowKey)) { return {}; } const { item } = this.rowKeyMap.get(rowKey); return item; } /** * * @param rowKey 设置选中状态ByCheckboxKey,用于合并Checkbox单元格时声明唯一key * @param check */ setRowSelectionByCheckboxKey(rowKey, check) { const { CHECKBOX_KEY } = this.ctx.config; if (CHECKBOX_KEY) { if (!this.rowKeyMap.has(rowKey)) { return false; } const { item } = this.rowKeyMap.get(rowKey); const checkboxKey = item[CHECKBOX_KEY]; if (this.checkboxKeyMap.has(checkboxKey)) { const rowKeys = this.checkboxKeyMap.get(checkboxKey) || []; rowKeys.forEach((rowKey) => { const selection = this.selectionMap.get(rowKey); if (selection) { selection.check = check; } }); } } } /** * 根据rowKey 取反选中 * @param rowKey */ toggleRowSelection(rowKey) { const row = this.rowKeyMap.get(rowKey); const selection = this.selectionMap.get(rowKey); if (!selection) { return; } selection.check = !selection.check; this.setRowSelectionByCheckboxKey(rowKey, selection.check); this.ctx.emit('toggleRowSelection', row); const rows = this.getSelectionRows(); this.ctx.emit('selectionChange', rows); // 清除缓存 this.bufferCheckState.buffer = false; this.ctx.emit('draw'); } /** * 根据rowKey 设置选中状态 * @param rowKey */ setRowSelection(rowKey, check, draw = true) { const selection = this.selectionMap.get(rowKey); if (!selection) { return; } selection.check = check; this.setRowSelectionByCheckboxKey(rowKey, selection.check); this.ctx.emit('setRowSelection', check, selection.row); if (draw) { // 清除缓存 this.bufferCheckState.buffer = false; this.ctx.emit('draw'); } } getSelectionRows() { let rows = []; this.selectionMap.forEach((selection) => { if (selection.check) { rows.push(selection.row); } }); return rows; } /** * 根据rowKey 获取选中状态 * @param rowKey */ getRowSelection(rowKey) { const selection = this.selectionMap.get(rowKey); if (!selection) { return false; } return selection.check; } /** * 根据rowKey 获取选中状态 * @param rowKey */ getRowSelectable(rowKey) { const { selectable, item, rowIndex } = this.rowKeyMap.get(rowKey); if (typeof selectable === 'function') { return selectable({ row: item, rowIndex, }); } return selectable; } /** * 全选 * @param rowKey */ toggleAllSelection() { this.rowKeyMap.forEach((row, rowKey) => { let _selectable = row.selectable; if (typeof _selectable === 'function') { _selectable = _selectable({ row: row.item, rowIndex: row.rowIndex, }); } if (_selectable) { this.setRowSelection(rowKey, true, false); } }); const rows = this.getSelectionRows(); this.ctx.emit('toggleAllSelection', rows); this.ctx.emit('selectionChange', rows); // 清除缓存 this.bufferCheckState.buffer = false; this.ctx.emit('draw'); } /** * 清除选中 * @param rowKey */ clearSelection(ignoreReserve = false) { // 清除选中,点击表头清除时要忽略跨页选的 if (ignoreReserve) { this.rowKeyMap.forEach((_, rowKey) => { this.setRowSelection(rowKey, false, false); }); } else { this.selectionMap.forEach((_, rowKey) => { this.setRowSelection(rowKey, false, false); }); } const rows = this.getSelectionRows(); this.ctx.emit('clearSelection'); this.ctx.emit('selectionChange', rows); // 清除缓存 this.bufferCheckState.buffer = false; this.ctx.emit('draw'); } /** * 获取选中状态,表头用 * @param rowKey */ getCheckedState() { // 缓存,解决性能问题 const { buffer, ...bufferState } = this.bufferCheckState; if (buffer) { return bufferState; } const total = this.rowKeyMap.size; let totalChecked = 0; let totalSelectable = 0; const reserveTotal = this.selectionMap.size; const reserveHasChecked = Array.from(this.selectionMap.values()).some((item) => item.check); this.rowKeyMap.forEach((row, rowKey) => { if (this.selectionMap.get(rowKey)?.check) { totalChecked += 1; } let _selectable = row.selectable; if (typeof _selectable === 'function') { _selectable = _selectable({ row: row.item, rowIndex: row.rowIndex, }); } if (_selectable) { totalSelectable += 1; } }); // 存在跨页时,变更半选中 const reserveIndeterminate = reserveTotal > total && totalChecked === 0 && reserveHasChecked; const indeterminate = (totalSelectable && totalSelectable > totalChecked && totalChecked > 0) || reserveIndeterminate; const selectable = totalSelectable !== 0; const check = !!totalSelectable && totalSelectable === totalChecked; // 缓存 this.bufferCheckState = { buffer: true, check, indeterminate, selectable, }; return { check, indeterminate, selectable, }; } /** * 更新列索引 * @param list 表头末级列表 */ updateColIndexKeyMap(list = []) { this.colIndexKeyMap.clear(); list.forEach((column) => { this.colIndexKeyMap.set(column.colIndex, column.key); }); } getColumnByColIndex(colIndex) { const key = this.colIndexKeyMap.get(colIndex); if (key && this.headerMap.has(key)) { return this.headerMap.get(key)?.column; } } getColumnByKey(key) { const column = this.headerMap.get(key); if (column) { return column; } } getColIndexForKey(key) { if (key && this.headerMap.has(key)) { return this.headerMap.get(key)?.colIndex; } } getColHeaderByIndex(colIndex) { const key = this.colIndexKeyMap.get(colIndex); if (key && this.headerMap.has(key)) { return this.headerMap.get(key); } return undefined; } /** * 获取以改变数据 */ getChangedData() { let list = []; this.changedDataMap.forEach((value, key) => { const originalValue = this.originalDataMap.get(key); const rowKey = key.split('\u200b_')[0]; const colKey = key.split('\u200b_')[1]; if (originalValue !== value) { list.push({ rowKey, colKey, originalValue, row: this.ctx.database.getRowDataItemForRowKey(rowKey), value, }); } }); return list; } getChangedRows() { const rowKeyList = new Set(); this.changedDataMap.forEach((value, key) => { const originalValue = this.originalDataMap.get(key); const rowKey = key.split('\u200b_')[0]; if (originalValue !== value) { rowKeyList.add(rowKey); } }); let rows = []; rowKeyList.forEach((rowKey) => { rows.push(this.ctx.database.getRowDataItemForRowKey(rowKey)); }); return rows; } /** * 获取改变值是否已经改变 * @param rowKey * @param key * @returns */ isHasChangedData(rowKey, key) { const changedKey = `${rowKey}\u200b_${key}`; if (!this.changedDataMap.has(changedKey)) { return false; } const originalValue = this.originalDataMap.get(changedKey); const changedValue = this.changedDataMap.get(changedKey); return originalValue !== changedValue; } getPositionForRowIndex(rowIndex) { if (rowIndex < this.positions.length) { return this.positions[rowIndex]; } return { height: 0, top: 0, bottom: 0, }; } setHeader(key, cellHeader) { if (!key) { return false; } this.headerMap.set(key, cellHeader); return true; } getReadonly(rowKey, key) { const { DISABLED } = this.ctx.config; // 禁用编辑 if (DISABLED) { return true; } const row = this.rowKeyMap.get(rowKey); const colHeader = this.headerMap.get(key); if (!row || !colHeader) { return true; } const rowReadonly = row.readonly; const colReadonly = colHeader.readonly; const { BODY_CELL_READONLY_METHOD } = this.ctx.config; // 自定义只读 if (typeof BODY_CELL_READONLY_METHOD === 'function' && colHeader) { const cellReadonlyMethod = BODY_CELL_READONLY_METHOD; const readonly = cellReadonlyMethod({ row: row.item, rowIndex: row.rowIndex, colIndex: colHeader.colIndex, column: colHeader.column, value: this.getItemValue(rowKey, key), }); if (readonly !== undefined) { return readonly; } } return colReadonly || rowReadonly; } clearValidate() { this.validationErrorMap.clear(); } hasValidationError() { return this.validationErrorMap.size !== 0; } getValidator(rowKey, key) { return new Promise((resolve) => { const row = this.rowKeyMap.get(rowKey); const colHeader = this.headerMap.get(key); const { BODY_CELL_RULES_METHOD } = this.ctx.config; if (colHeader === undefined) { return resolve([]); } const column = colHeader.column; let rules = column.rules; // 自定义只读 if (typeof BODY_CELL_RULES_METHOD === 'function') { const cellRulesMethod = BODY_CELL_RULES_METHOD; const _rules = cellRulesMethod({ row: row.item, rowIndex: row.rowIndex, colIndex: colHeader.colIndex, column, value: this.getItemValue(rowKey, key), }); if (_rules) { rules = _rules; } } if (rules) { let descriptor = {}; let data = {}; data[key] = this.getItemValue(rowKey, key); if (Array.isArray(rules)) { const _rules = rules.map((item) => { return { ...item, row: row.item, column, rowIndex: row.rowIndex, colIndex: colHeader.colIndex, }; }); descriptor[key] = _rules; } else { descriptor[key] = { ...rules, row: row.item, column, rowIndex: row.rowIndex, colIndex: colHeader.colIndex, }; } const validator = new Schema(descriptor); validator .validate(data) .then(() => { this.clearValidationError(rowKey, key); resolve([]); }) .catch(({ errors }) => { const _errors = errors.map((error) => ({ ...error, column, key, row: row.item, rowKey, })); this.setValidationError(rowKey, key, _errors); resolve(_errors); }); } else { resolve([]); } }); } getHeightByRowIndexRowSpan(rowIndex, rowSpan) { let sumHeight = 0; for (let i = 0; i < rowSpan; i++) { const position = this.positions[rowIndex + i]; if (position) { sumHeight += position.height; } } return sumHeight; } getSpanInfo(cell) { const { rowIndex, key, rowKey, row, value, colIndex, relationRowKeys, relationColKeys, rowspan, height, width, colspan, mergeRow, mergeCol, } = cell; if (rowspan === 1 && colspan === 1) { return { xArr: [colIndex, colIndex], yArr: [rowIndex, rowIndex], rowspan, colspan, height, width, offsetTop: 0, offsetLeft: 0, dataList: [ { rowKey, key, row, value, }, ], }; } let topIndex = rowIndex; let bottomIndex = rowIndex; let leftIndex = colIndex; let rightIndex = colIndex; let dataList = []; let offsetTop = 0; let offsetLeft = 0; let mergeHeight = 0; let mergeWidth = 0; // 列合并的 if (rowspan !== 1 && mergeRow) { mergeWidth = width; const curValue = relationRowKeys.reduce((acc, key) => { const value = this.getItemValue(rowKey, key) ?? ''; return `${acc}${value}`; }, ''); // 先查找向上的相同值,根据关联值查询 for (let i = rowIndex - 1; i >= 0; i--) { const _rowKey = this.rowIndexRowKeyMap.get(i) || ''; const pValue = relationRowKeys.reduce((acc, key) => { const value = this.getItemValue(_rowKey, key) ?? ''; return `${acc}${value}`; }, ''); if (curValue === pValue) { topIndex = i; } else { break; } } // 再查找向下的相同值,根据关联值查询 for (let i = rowIndex; i <= this.ctx.maxRowIndex; i++) { const _rowKey = this.rowIndexRowKeyMap.get(i) || ''; const pValue = relationRowKeys.reduce((acc, key) => { const value = this.getItemValue(_rowKey, key) ?? ''; return `${acc}${value}`; }, ''); if (curValue === pValue) { bottomIndex = i; } else { break; } } for (let i = topIndex; i < rowIndex; i++) { const { height } = this.positions[i]; offsetTop += height; } for (let i = topIndex; i <= bottomIndex; i++) { const { height } = this.positions[i]; // 合并高度 mergeHeight += height; // 根据下标查找rowkey const _rowKey = this.rowIndexRowKeyMap.get(i) || ''; const { item } = this.rowKeyMap.get(_rowKey); const value = this.getItemValue(_rowKey, key); // 需要改变值 dataList.push({ rowKey: _rowKey, key, value, row: item, }); } } // 行合并的 if (colspan !== 1 && mergeCol) { mergeHeight = height; // 向左 for (let i = colIndex - 1; i >= 0; i--) { const column = this.getColumnByColIndex(i); if (!column) { break; } const curValue = this.getItemValue(rowKey, key); const pValue = this.getItemValue(rowKey, column.key); if (curValue === pValue && relationColKeys.includes(column.key)) { leftIndex = i; } else { break; } } // 向右 for (let i = colIndex; i <= this.ctx.maxColIndex; i++) { const column = this.getColumnByColIndex(i); if (!column) { break; } const curValue = this.getItemValue(rowKey, key); const pValue = this.getItemValue(rowKey, column.key); if (curValue === pValue && relationColKeys.includes(column.key)) { rightIndex = i; } else { break; } } for (let i = leftIndex; i < colIndex; i++) { const column = this.getColumnByColIndex(i); if (!column) { break; } // 合并宽度 offsetLeft += column.width || 100; } for (let i = leftIndex; i <= rightIndex; i++) { const column = this.getColumnByColIndex(i); if (!column) { break; } // 合并宽度 mergeWidth += column.width || 100; // 需要改变值 dataList.push({ rowKey, key: column.key, value: this.getItemValue(rowKey, column.key), row, }); } } return { xArr: [leftIndex, rightIndex], yArr: [topIndex, bottomIndex], rowspan, colspan, height: mergeHeight, width: mergeWidth, offsetTop, offsetLeft, dataList, }; } setLoading(loading) { this.loading = loading; this.ctx.emit('loadingChange', loading); } getLoading() { return this.loading; } setValidationErrorByRowIndex(rowIndex, key, message) { const rowKey = this.rowIndexRowKeyMap.get(rowIndex); const _key = `${rowKey}\u200b_${key}`; const errors = [ { message, }, ]; this.validationErrorMap.set(_key, errors); } setValidationError(rowKey, key, errors) { const _key = `${rowKey}\u200b_${key}`; this.validationErrorMap.set(_key, errors); } clearValidationError(rowKey, key) { const _key = `${rowKey}\u200b_${key}`; if (this.validationErrorMap.has(_key)) { this.validationErrorMap.delete(_key); } } getValidationError(rowKey, key) { const _key = `${rowKey}\u200b_${key}`; return this.validationErrorMap.get(_key) || []; } // 获取虚拟单元格 getVirtualBodyCell(rowIndex, colIndex) { const column = this.getColumnByColIndex(colIndex); const row = this.getRowForRowIndex(rowIndex); if (!column || !row) { return; } const cell = new Cell(this.ctx, rowIndex, colIndex, 0, 0, 0, 0, column, row.item, 'body'); return cell; } getVirtualBodyCellByKey(rowKey, key) { const rowIndex = this.getRowIndexForRowKey(rowKey); const colIndex = this.getColIndexForKey(key); if (rowIndex === undefined || colIndex === undefined) { return; } return this.getVirtualBodyCell(rowIndex, colIndex); } hasMergeCell(xArr, yArr) { let hasMergeCell = false; for (let i = yArr[0]; i <= yArr[1]; i++) { for (let j = xArr[0]; j <= xArr[1]; j++) { const cell = this.getVirtualBodyCell(i, j); if (cell && (cell.rowspan !== 1 || cell.colspan !== 1)) { hasMergeCell = true; break; } } } return hasMergeCell; } } //# sourceMappingURL=Database.js.map