UNPKG

@blueking/vxe-table

Version:

一个基于 vue 的 PC 端表格组件,支持增删改查、虚拟树、列拖拽,懒加载、快捷菜单、数据校验、树形结构、打印、导入导出、自定义模板、渲染器、JSON 配置式...

1,566 lines (1,565 loc) 380 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _vue = require("vue"); var _xeUtils = _interopRequireDefault(require("xe-utils")); var _dom = require("../../ui/src/dom"); var _utils = require("../../ui/src/utils"); var _ui = require("../../ui"); var _cell = _interopRequireDefault(require("./cell")); var _body = _interopRequireDefault(require("./body")); var _header = _interopRequireDefault(require("./header")); var _footer = _interopRequireDefault(require("./footer")); var _props = _interopRequireDefault(require("./props")); var _emits = _interopRequireDefault(require("./emits")); var _util = require("./util"); var _vn = require("../../ui/src/vn"); var _log = require("../../ui/src/log"); var _panel = _interopRequireDefault(require("../module/custom/panel")); var _panel2 = _interopRequireDefault(require("../module/filter/panel")); var _importPanel = _interopRequireDefault(require("../module/export/import-panel")); var _exportPanel = _interopRequireDefault(require("../module/export/export-panel")); var _panel3 = _interopRequireDefault(require("../module/menu/panel")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const { getConfig, getIcon, getI18n, renderer, formats, createEvent, globalResize, interceptor, hooks, globalEvents, GLOBAL_EVENT_KEYS, useFns, renderEmptyElement } = _ui.VxeUI; const customStorageKey = 'VXE_CUSTOM_STORE'; const settingColumnWidth = 40; var _default = exports.default = (0, _vue.defineComponent)({ name: 'VxeTable', props: _props.default, emits: _emits.default, setup(props, context) { const { slots, emit } = context; const xID = _xeUtils.default.uniqueId(); // 使用已安装的组件,如果未安装则不渲染 const VxeUILoadingComponent = _ui.VxeUI.getComponent('VxeLoading'); const VxeUITooltipComponent = _ui.VxeUI.getComponent('VxeTooltip'); const $xeTabs = (0, _vue.inject)('$xeTabs', null); const { computeSize } = useFns.useSize(props); const reactData = (0, _vue.reactive)({ isCalcColumn: false, // 低性能的静态列 staticColumns: [], // 渲染的列分组 tableGroupColumn: [], // 可视区渲染的列 tableColumn: [], // 渲染中的数据 tableData: [], // 是否启用了横向 X 可视渲染方式加载 scrollXLoad: false, // 是否启用了纵向 Y 可视渲染方式加载 scrollYLoad: false, // 是否存在纵向滚动条 overflowY: true, // 是否存在横向滚动条 overflowX: false, // 纵向滚动条的宽度 scrollbarWidth: 0, // 横向滚动条的高度 scrollbarHeight: 0, // 最后滚动时间戳 lastScrollTime: 0, // 行高 rowHeight: 0, // 表格父容器的高度 parentHeight: 0, // 是否使用分组表头 isGroup: false, isAllOverflow: false, // 复选框属性,是否全选 isAllSelected: false, // 复选框属性,有选中且非全选状态 isIndeterminate: false, // 复选框属性,已选中的行集合 selectCheckboxMaps: {}, // 当前行 currentRow: null, // 单选框属性,选中列 currentColumn: null, // 单选框属性,选中行 selectRadioRow: null, // 表尾合计数据 footerTableData: [], // 展开列信息 expandColumn: null, // 树节点列信息 treeNodeColumn: null, hasFixedColumn: false, // 已展开的行集合 rowExpandedMaps: {}, // 懒加载中的展开行的集合 rowExpandLazyLoadedMaps: {}, // 已展开树节点集合 treeExpandedMaps: {}, // 懒加载中的树节点的集合 treeExpandLazyLoadedMaps: {}, // 树节点不确定状态的集合 treeIndeterminateMaps: {}, // 合并单元格的对象集 mergeList: [], // 合并表尾数据的对象集 mergeFooterList: [], // 刷新列标识,当列筛选被改变时,触发表格刷新数据 upDataFlag: 0, // 刷新列标识,当列的特定属性被改变时,触发表格刷新列 reColumnFlag: 0, // 已标记的对象集 pendingRowMaps: {}, // 初始化标识 initStore: { filter: false, import: false, export: false, custom: false }, // 自定义列相关的信息 customStore: { btnEl: null, isAll: false, isIndeterminate: false, activeBtn: false, activeWrapper: false, visible: false, maxHeight: 0, oldSortMaps: {}, oldFixedMaps: {}, oldVisibleMaps: {} }, customColumnList: [], // 当前选中的筛选列 filterStore: { isAllSelected: false, isIndeterminate: false, style: null, options: [], column: null, multiple: false, visible: false, maxHeight: null }, // 存放列相关的信息 columnStore: { leftList: [], centerList: [], rightList: [], resizeList: [], pxList: [], pxMinList: [], autoMinList: [], scaleList: [], scaleMinList: [], autoList: [], remainList: [] }, // 存放快捷菜单的信息 ctxMenuStore: { selected: null, visible: false, showChild: false, selectChild: null, list: [], style: null }, // 存放可编辑相关信息 editStore: { indexs: { columns: [] }, titles: { columns: [] }, // 选中源 selected: { row: null, column: null }, // 已复制源 copyed: { cut: false, rows: [], columns: [] }, // 激活 actived: { row: null, column: null }, // 当前被强制聚焦单元格,只会在鼠标点击后算聚焦 focused: { row: null, column: null }, insertMaps: {}, removeMaps: {} }, // 存放 tooltip 相关信息 tooltipStore: { row: null, column: null, content: null, visible: false, currOpts: {} }, // 存放数据校验相关信息 validStore: { visible: false }, validErrorMaps: {}, // 导入相关信息 importStore: { inited: false, file: null, type: '', modeList: [], typeList: [], filename: '', visible: false }, importParams: { mode: '', types: null, message: true }, // 导出相关信息 exportStore: { inited: false, name: '', modeList: [], typeList: [], columns: [], isPrint: false, hasFooter: false, hasMerge: false, hasTree: false, hasColgroup: false, visible: false }, exportParams: { filename: '', sheetName: '', mode: '', type: '', isColgroup: false, isMerge: false, isAllExpand: false, useStyle: false, original: false, message: true, isHeader: false, isFooter: false }, scrollVMLoading: false, isCalcCellHeight: 0, isCustomStatus: false, isDragRowMove: false, dragRow: null, isDragColMove: false, dragCol: null, dragTipText: '', _isResize: false, isLoading: false }); const internalData = { tZindex: 0, elemStore: {}, // 存放横向 X 虚拟滚动相关的信息 scrollXStore: { preloadSize: 0, offsetSize: 0, visibleSize: 0, visibleStartIndex: 0, visibleEndIndex: 0, startIndex: 0, endIndex: 0 }, // 存放纵向 Y 虚拟滚动相关信息 scrollYStore: { preloadSize: 0, offsetSize: 0, visibleSize: 0, visibleStartIndex: 0, visibleEndIndex: 0, startIndex: 0, endIndex: 0 }, // 表格宽度 tableWidth: 0, // 表格高度 tableHeight: 0, // 表头高度 headerHeight: 0, // 表尾高度 footerHeight: 0, customHeight: 0, customMinHeight: 0, customMaxHeight: 0, // 当前 hover 行 hoverRow: null, // 最后滚动位置 lastScrollLeft: 0, lastScrollTop: 0, // 单选框属性,已选中保留的行 radioReserveRow: null, // 复选框属性,已选中保留的行集合 checkboxReserveRowMap: {}, // 行数据,已展开保留的行集合 rowExpandedReserveRowMap: {}, // 树结构数据,已展开保留的行集合 treeExpandedReserveRowMap: {}, // 树结构数据,不确定状态的集合 treeIndeterminateRowMaps: {}, // 列表完整数据、条件处理后 tableFullData: [], afterFullData: [], afterTreeFullData: [], // 列表条件处理后数据集合 afterFullRowMaps: {}, // 树结构完整数据、条件处理后 tableFullTreeData: [], tableSynchData: [], tableSourceData: [], // 收集的列配置(带分组) collectColumn: [], // 完整所有列(不带分组) tableFullColumn: [], // 渲染所有列 visibleColumn: [], // 总的缓存数据集 fullAllDataRowIdData: {}, // 渲染中缓存数据 sourceDataRowIdData: {}, fullDataRowIdData: {}, fullColumnIdData: {}, fullColumnFieldData: {}, // 列选取状态 columnStatusMaps: {}, // 行选取状态 rowStatusMaps: {}, // prevDragRow: null, inited: false, tooltipTimeout: null, initStatus: false, isActivated: false }; let tableMethods = {}; let tablePrivateMethods = {}; const refElem = (0, _vue.ref)(); const refTooltip = (0, _vue.ref)(); const refCommTooltip = (0, _vue.ref)(); const refValidTooltip = (0, _vue.ref)(); const refTableMenu = (0, _vue.ref)(); const refTableFilter = (0, _vue.ref)(); const refTableCustom = (0, _vue.ref)(); const refTableViewportElem = (0, _vue.ref)(); const refTableHeader = (0, _vue.ref)(); const refTableBodyPrepend = (0, _vue.ref)(); const refTableBody = (0, _vue.ref)(); const refTableFooter = (0, _vue.ref)(); const refTableBodyAppend = (0, _vue.ref)(); const refTableLeftHeader = (0, _vue.ref)(); const refTableLeftBody = (0, _vue.ref)(); const refTableLeftFooter = (0, _vue.ref)(); const refTableRightHeader = (0, _vue.ref)(); const refTableRightBody = (0, _vue.ref)(); const refTableRightFooter = (0, _vue.ref)(); const refLeftContainer = (0, _vue.ref)(); const refRightContainer = (0, _vue.ref)(); const refCellResizeBar = (0, _vue.ref)(); const refCellResizeTip = (0, _vue.ref)(); const refEmptyPlaceholder = (0, _vue.ref)(); const refDragTipElem = (0, _vue.ref)(); const refDragRowLineElem = (0, _vue.ref)(); const refDragColLineElem = (0, _vue.ref)(); const refScrollXVirtualElem = (0, _vue.ref)(); const refScrollYVirtualElem = (0, _vue.ref)(); const refScrollXHandleElem = (0, _vue.ref)(); const refScrollXRightCornerElem = (0, _vue.ref)(); const refScrollYHandleElem = (0, _vue.ref)(); const refScrollYTopCornerElem = (0, _vue.ref)(); const refScrollYBottomCornerElem = (0, _vue.ref)(); const refScrollXSpaceElem = (0, _vue.ref)(); const refScrollYSpaceElem = (0, _vue.ref)(); const $xeGrid = (0, _vue.inject)('$xeGrid', null); let $xeToolbar; const computeShowSettingColumn = (0, _vue.computed)(() => Boolean(slots.settingColumn)); const computeTableId = (0, _vue.computed)(() => { const { id } = props; if (id) { if (_xeUtils.default.isFunction(id)) { return `${id({ $table: $xeTable, $grid: $xeGrid }) || ''}`; } return `${id}`; } return ''; }); const computeValidOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.validConfig, props.validConfig); }); /** * @deprecated */ const computeSXOpts = (0, _vue.computed)(() => { const virtualXOpts = computeVirtualXOpts.value; return virtualXOpts; }); const computeScrollXThreshold = (0, _vue.computed)(() => { const sXOpts = computeSXOpts.value; const { threshold } = sXOpts; if (threshold) { return _xeUtils.default.toNumber(threshold); } return 0; }); /** * @deprecated */ const computeSYOpts = (0, _vue.computed)(() => { const virtualYOpts = computeVirtualYOpts.value; return virtualYOpts; }); const computeVirtualXOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.scrollX, getConfig().table.virtualXConfig, props.scrollX, props.virtualXConfig); }); const computeVirtualYOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.scrollY, getConfig().table.virtualYConfig, props.scrollY, props.virtualYConfig); }); const computeScrollbarOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.scrollbarConfig, props.scrollbarConfig); }); const computeScrollYThreshold = (0, _vue.computed)(() => { const sYOpts = computeSYOpts.value; const { threshold } = sYOpts; if (threshold) { return _xeUtils.default.toNumber(threshold); } return 0; }); const computeRowHeightMaps = (0, _vue.computed)(() => { return { default: 48, medium: 44, small: 40, mini: 36 }; }); const computeDefaultRowHeight = (0, _vue.computed)(() => { const vSize = computeSize.value; const rowHeightMaps = computeRowHeightMaps.value; return rowHeightMaps[vSize || 'default']; }); const computeColumnOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.columnConfig, props.columnConfig); }); const computeCellOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.cellConfig, props.cellConfig); }); const computeRowOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.rowConfig, props.rowConfig); }); const computeRowDragOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.rowDragConfig, props.rowDragConfig); }); const computeColumnDragOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.columnDragConfig, props.columnDragConfig); }); const computeResizeOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.resizeConfig, props.resizeConfig); }); const computeResizableOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.resizableConfig, props.resizableConfig); }); const computeSeqOpts = (0, _vue.computed)(() => { return Object.assign({ startIndex: 0 }, getConfig().table.seqConfig, props.seqConfig); }); const computeRadioOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.radioConfig, props.radioConfig); }); const computeCheckboxOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.checkboxConfig, props.checkboxConfig); }); const computeTooltipOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().tooltip, getConfig().table.tooltipConfig, props.tooltipConfig); }); const computeEditOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.editConfig, props.editConfig); }); const computeSortOpts = (0, _vue.computed)(() => { return Object.assign({ orders: ['asc', 'desc', null] }, getConfig().table.sortConfig, props.sortConfig); }); const computeFilterOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.filterConfig, props.filterConfig); }); const computeMouseOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.mouseConfig, props.mouseConfig); }); const computeAreaOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.areaConfig, props.areaConfig); }); const computeKeyboardOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.keyboardConfig, props.keyboardConfig); }); const computeClipOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.clipConfig, props.clipConfig); }); const computeFNROpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.fnrConfig, props.fnrConfig); }); const computeMenuOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.menuConfig, props.menuConfig); }); const computeLeftFixedWidth = (0, _vue.computed)(() => { const { columnStore } = reactData; const { leftList } = columnStore; let leftWidth = 0; for (let i = 0; i < leftList.length; i++) { const column = leftList[i]; leftWidth += column.renderWidth; } return leftWidth; }); const computeRightFixedWidth = (0, _vue.computed)(() => { const { columnStore } = reactData; const { rightList } = columnStore; let leftWidth = 0; for (let i = 0; i < rightList.length; i++) { const column = rightList[i]; leftWidth += column.renderWidth; } return leftWidth; }); const computeHeaderMenu = (0, _vue.computed)(() => { const menuOpts = computeMenuOpts.value; const headerOpts = menuOpts.header; return headerOpts && headerOpts.options ? headerOpts.options : []; }); const computeBodyMenu = (0, _vue.computed)(() => { const menuOpts = computeMenuOpts.value; const bodyOpts = menuOpts.body; return bodyOpts && bodyOpts.options ? bodyOpts.options : []; }); const computeFooterMenu = (0, _vue.computed)(() => { const menuOpts = computeMenuOpts.value; const footerOpts = menuOpts.footer; return footerOpts && footerOpts.options ? footerOpts.options : []; }); const computeIsMenu = (0, _vue.computed)(() => { const menuOpts = computeMenuOpts.value; const headerMenu = computeHeaderMenu.value; const bodyMenu = computeBodyMenu.value; const footerMenu = computeFooterMenu.value; return !!(props.menuConfig && (0, _utils.isEnableConf)(menuOpts) && (headerMenu.length || bodyMenu.length || footerMenu.length)); }); const computeMenuList = (0, _vue.computed)(() => { const { ctxMenuStore } = reactData; const rest = []; ctxMenuStore.list.forEach(list => { list.forEach(item => { rest.push(item); }); }); return rest; }); const computeExportOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.exportConfig, props.exportConfig); }); const computeImportOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.importConfig, props.importConfig); }); const computePrintOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.printConfig, props.printConfig); }); const computeExpandOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.expandConfig, props.expandConfig); }); const computeTreeOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.treeConfig, props.treeConfig); }); const computeEmptyOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.emptyRender, props.emptyRender); }); const computeLoadingOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.loadingConfig, props.loadingConfig); }); const computeCellOffsetWidth = (0, _vue.computed)(() => { return props.border ? Math.max(2, Math.ceil(reactData.scrollbarWidth / reactData.tableColumn.length)) : 1; }); const computeCustomOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().table.customConfig, props.customConfig); }); const computeAutoWidthColumnList = (0, _vue.computed)(() => { const { visibleColumn } = internalData; const { tableColumn } = reactData; return tableColumn.length || visibleColumn.length ? visibleColumn.filter(column => column.width === 'auto' || column.minWidth === 'auto') : []; }); const computeFixedColumnSize = (0, _vue.computed)(() => { const { tableColumn } = reactData; const { collectColumn } = internalData; let fixedSize = 0; // 只判断第一层 if (tableColumn.length && collectColumn.length) { collectColumn.forEach(column => { if (column.renderFixed) { fixedSize++; } }); } return fixedSize; }); const computeIsMaxFixedColumn = (0, _vue.computed)(() => { const fixedColumnSize = computeFixedColumnSize.value; const columnOpts = computeColumnOpts.value; const { maxFixedSize } = columnOpts; if (maxFixedSize) { return fixedColumnSize >= maxFixedSize; } return false; }); const computeTableBorder = (0, _vue.computed)(() => { const { border } = props; if (border === true) { return 'full'; } if (border) { return border; } return 'default'; }); const computeIsAllCheckboxDisabled = (0, _vue.computed)(() => { const { treeConfig } = props; const { tableData } = reactData; const { tableFullData } = internalData; const checkboxOpts = computeCheckboxOpts.value; const { strict, checkMethod } = checkboxOpts; if (strict) { if (tableData.length || tableFullData.length) { if (checkMethod) { if (treeConfig) { // 暂时不支持树形结构 } // 如果所有行都被禁用 return tableFullData.every(row => !checkMethod({ row })); } return false; } return true; } return false; }); const computeVirtualScrollBars = (0, _vue.computed)(() => { const { overflowX, scrollXLoad, overflowY, scrollYLoad } = reactData; return { x: overflowX && scrollXLoad, y: overflowY && scrollYLoad }; }); const refMaps = { refElem, refTooltip, refValidTooltip, refTableFilter, refTableCustom, refTableMenu, refTableHeader, refTableBody, refTableFooter, refTableLeftHeader, refTableLeftBody, refTableLeftFooter, refTableRightHeader, refTableRightBody, refTableRightFooter, refLeftContainer, refRightContainer, refCellResizeBar, refCellResizeTip, refScrollXVirtualElem, refScrollYVirtualElem, refScrollXHandleElem, refScrollYHandleElem, refScrollXSpaceElem, refScrollYSpaceElem }; const computeMaps = { computeSize, computeTableId, computeValidOpts, computeVirtualXOpts, computeVirtualYOpts, computeScrollbarOpts, computeColumnOpts, computeScrollXThreshold, computeScrollYThreshold, computeDefaultRowHeight, computeCellOpts, computeRowOpts, computeRowDragOpts, computeColumnDragOpts, computeResizeOpts, computeResizableOpts, computeSeqOpts, computeRadioOpts, computeCheckboxOpts, computeTooltipOpts, computeEditOpts, computeSortOpts, computeFilterOpts, computeMouseOpts, computeAreaOpts, computeKeyboardOpts, computeClipOpts, computeFNROpts, computeHeaderMenu, computeBodyMenu, computeFooterMenu, computeIsMenu, computeMenuOpts, computeExportOpts, computeImportOpts, computePrintOpts, computeExpandOpts, computeTreeOpts, computeEmptyOpts, computeLoadingOpts, computeCellOffsetWidth, computeCustomOpts, computeLeftFixedWidth, computeRightFixedWidth, computeFixedColumnSize, computeIsMaxFixedColumn, computeIsAllCheckboxDisabled, computeVirtualScrollBars, computeSXOpts, computeSYOpts }; const $xeTable = { xID, props: props, context, reactData, internalData, getRefMaps: () => refMaps, getComputeMaps: () => computeMaps, xegrid: $xeGrid }; const eqCellValue = (row1, row2, field) => { const val1 = _xeUtils.default.get(row1, field); const val2 = _xeUtils.default.get(row2, field); if ((0, _utils.eqEmptyValue)(val1) && (0, _utils.eqEmptyValue)(val2)) { return true; } if (_xeUtils.default.isString(val1) || _xeUtils.default.isNumber(val1)) { return '' + val1 === '' + val2; } return _xeUtils.default.isEqual(val1, val2); }; const getNextSortOrder = column => { const sortOpts = computeSortOpts.value; const { orders } = sortOpts; const currOrder = column.order || null; const oIndex = orders.indexOf(currOrder) + 1; return orders[oIndex < orders.length ? oIndex : 0]; }; const getCustomStorageMap = id => { const version = getConfig().version; const rest = _xeUtils.default.toStringJSON(localStorage.getItem(customStorageKey) || ''); const maps = rest && rest._v === version ? rest : { _v: version }; return (id ? maps[id] : maps) || {}; }; const setCustomStorageMap = (id, data) => { const version = getConfig().version; const maps = getCustomStorageMap(); maps[id] = data || undefined; maps._v = version; localStorage.setItem(customStorageKey, _xeUtils.default.toJSONString(maps)); }; const getRecoverRowMaps = keyMaps => { const { fullAllDataRowIdData } = internalData; const restKeys = {}; _xeUtils.default.each(keyMaps, (row, rowid) => { if (fullAllDataRowIdData[rowid]) { restKeys[rowid] = row; } }); return restKeys; }; const handleReserveRow = reserveRowMap => { const { fullDataRowIdData } = internalData; const reserveList = []; _xeUtils.default.each(reserveRowMap, (item, rowid) => { if (fullDataRowIdData[rowid] && $xeTable.findRowIndexOf(reserveList, fullDataRowIdData[rowid].row) === -1) { reserveList.push(fullDataRowIdData[rowid].row); } }); return reserveList; }; const handleVirtualXVisible = () => { const { elemStore, visibleColumn } = internalData; const leftFixedWidth = computeLeftFixedWidth.value; const rightFixedWidth = computeRightFixedWidth.value; const bodyScrollElem = (0, _util.getRefElem)(elemStore['main-body-scroll']); if (bodyScrollElem) { const { scrollLeft, clientWidth } = bodyScrollElem; const startWidth = scrollLeft + leftFixedWidth; const endWidth = scrollLeft + clientWidth - rightFixedWidth; let toVisibleIndex = -1; let cWidth = 0; let visibleSize = 0; for (let colIndex = 0, colLen = visibleColumn.length; colIndex < colLen; colIndex++) { cWidth += visibleColumn[colIndex].renderWidth; if (toVisibleIndex === -1 && startWidth < cWidth) { toVisibleIndex = colIndex; } if (toVisibleIndex >= 0) { visibleSize++; if (cWidth > endWidth) { break; } } } return { toVisibleIndex: Math.max(0, toVisibleIndex), visibleSize: Math.max(1, visibleSize) }; } return { toVisibleIndex: 0, visibleSize: 6 }; }; const computeRowHeight = () => { const tableHeader = refTableHeader.value; const tableBody = refTableBody.value; const tableBodyElem = tableBody ? tableBody.$el : null; const defaultRowHeight = computeDefaultRowHeight.value; let rowHeight = 0; if (tableBodyElem) { const tableHeaderElem = tableHeader ? tableHeader.$el : null; let firstTrElem; firstTrElem = tableBodyElem.querySelector('tr'); if (!firstTrElem && tableHeaderElem) { firstTrElem = tableHeaderElem.querySelector('tr'); } if (firstTrElem) { rowHeight = firstTrElem.clientHeight; } } if (!rowHeight) { rowHeight = defaultRowHeight; } // 最低支持 18px 行高 return Math.max(18, rowHeight); }; const handleVirtualYVisible = () => { const { showOverflow } = props; const { rowHeight } = reactData; const { elemStore, afterFullData, fullAllDataRowIdData } = internalData; const bodyScrollElem = (0, _util.getRefElem)(elemStore['main-body-scroll']); if (bodyScrollElem) { const { scrollTop, clientHeight } = bodyScrollElem; const endHeight = scrollTop + clientHeight; let toVisibleIndex = -1; let offsetTop = 0; let visibleSize = 0; if (showOverflow) { toVisibleIndex = Math.floor(scrollTop / rowHeight); visibleSize = Math.ceil(clientHeight / rowHeight) + 1; } else { for (let rIndex = 0, rLen = afterFullData.length; rIndex < rLen; rIndex++) { const row = afterFullData[rIndex]; const rowid = (0, _util.getRowid)($xeTable, row); const rowRest = fullAllDataRowIdData[rowid]; offsetTop += rowRest ? rowRest.height || rowHeight : rowHeight; if (toVisibleIndex === -1 && scrollTop < offsetTop) { toVisibleIndex = rIndex; } if (toVisibleIndex >= 0) { visibleSize++; if (offsetTop > endHeight) { break; } } } } return { toVisibleIndex: Math.max(0, toVisibleIndex), visibleSize: Math.max(6, visibleSize) }; } return { toVisibleIndex: 0, visibleSize: 6 }; }; const calculateMergerOffsetIndex = (list, offsetItem, type) => { for (let mcIndex = 0, len = list.length; mcIndex < len; mcIndex++) { const mergeItem = list[mcIndex]; const { startIndex, endIndex } = offsetItem; const mergeStartIndex = mergeItem[type]; const mergeSpanNumber = mergeItem[type + 'span']; const mergeEndIndex = mergeStartIndex + mergeSpanNumber; if (mergeStartIndex < startIndex && startIndex < mergeEndIndex) { offsetItem.startIndex = mergeStartIndex; } if (mergeStartIndex < endIndex && endIndex < mergeEndIndex) { offsetItem.endIndex = mergeEndIndex; } if (offsetItem.startIndex !== startIndex || offsetItem.endIndex !== endIndex) { mcIndex = -1; } } }; const setMerges = (merges, mList, rowList) => { if (merges) { const { treeConfig } = props; const { visibleColumn } = internalData; if (!_xeUtils.default.isArray(merges)) { merges = [merges]; } if (treeConfig && merges.length) { (0, _log.errLog)('vxe.error.noTree', ['merge-cells | merge-footer-items']); } merges.forEach(item => { let { row, col, rowspan, colspan } = item; if (rowList && _xeUtils.default.isNumber(row)) { row = rowList[row]; } if (_xeUtils.default.isNumber(col)) { col = visibleColumn[col]; } if ((rowList ? row : _xeUtils.default.isNumber(row)) && col && (rowspan || colspan)) { rowspan = _xeUtils.default.toNumber(rowspan) || 1; colspan = _xeUtils.default.toNumber(colspan) || 1; if (rowspan > 1 || colspan > 1) { const mcIndex = _xeUtils.default.findIndexOf(mList, item => (item._row === row || (0, _util.getRowid)($xeTable, item._row) === (0, _util.getRowid)($xeTable, row)) && (item._col.id === col || item._col.id === col.id)); const mergeItem = mList[mcIndex]; if (mergeItem) { mergeItem.rowspan = rowspan; mergeItem.colspan = colspan; mergeItem._rowspan = rowspan; mergeItem._colspan = colspan; } else { const mergeRowIndex = rowList ? $xeTable.findRowIndexOf(rowList, row) : row; const mergeColIndex = tableMethods.getVTColumnIndex(col); mList.push({ row: mergeRowIndex, col: mergeColIndex, rowspan, colspan, _row: row, _col: col, _rowspan: rowspan, _colspan: colspan }); } } } }); } }; const removeMerges = (merges, mList, rowList) => { const rest = []; if (merges) { const { treeConfig } = props; const { visibleColumn } = internalData; if (!_xeUtils.default.isArray(merges)) { merges = [merges]; } if (treeConfig && merges.length) { (0, _log.errLog)('vxe.error.noTree', ['merge-cells | merge-footer-items']); } merges.forEach(item => { let { row, col } = item; if (rowList && _xeUtils.default.isNumber(row)) { row = rowList[row]; } if (_xeUtils.default.isNumber(col)) { col = visibleColumn[col]; } const mcIndex = _xeUtils.default.findIndexOf(mList, item => (item._row === row || (0, _util.getRowid)($xeTable, item._row) === (0, _util.getRowid)($xeTable, row)) && (item._col.id === col || item._col.id === col.id)); if (mcIndex > -1) { const rItems = mList.splice(mcIndex, 1); rest.push(rItems[0]); } }); } return rest; }; const clearAllSort = () => { const { tableFullColumn } = internalData; tableFullColumn.forEach(column => { column.order = null; }); }; const calcTableHeight = key => { const { parentHeight } = reactData; const val = props[key]; let num = 0; if (val) { if (val === '100%' || val === 'auto') { num = parentHeight; } else { const excludeHeight = $xeTable.getExcludeHeight(); if ((0, _dom.isScale)(val)) { num = Math.floor((_xeUtils.default.toInteger(val) || 1) / 100 * parentHeight); } else { num = _xeUtils.default.toNumber(val); } num = Math.max(40, num - excludeHeight); } } return num; }; const handleCustomRestore = storeData => { let { collectColumn } = internalData; const { resizableData, sortData, visibleData, fixedData } = storeData; let hasCustomSort = false; // 处理还原 if (resizableData || sortData || visibleData || fixedData) { _xeUtils.default.eachTree(collectColumn, (column, index, items, path, parentColumn) => { const colKey = column.getKey(); // 支持一级 if (!parentColumn) { if (fixedData && fixedData[colKey] !== undefined) { column.fixed = fixedData[colKey]; } if (sortData && _xeUtils.default.isNumber(sortData[colKey])) { hasCustomSort = true; column.renderSortNumber = sortData[colKey]; } } if (resizableData && _xeUtils.default.isNumber(resizableData[colKey])) { column.resizeWidth = resizableData[colKey]; } if (visibleData && _xeUtils.default.isBoolean(visibleData[colKey])) { column.visible = visibleData[colKey]; } }); // 如果自定义了顺序 if (hasCustomSort) { collectColumn = _xeUtils.default.orderBy(collectColumn, 'renderSortNumber'); internalData.collectColumn = collectColumn; internalData.tableFullColumn = getColumnList(collectColumn); } reactData.isCustomStatus = true; } else { reactData.isCustomStatus = false; } }; /** * 还原自定义列操作状态 */ const restoreCustomStorage = () => { const { customConfig } = props; const tableId = computeTableId.value; const customOpts = computeCustomOpts.value; const { storage, restoreStore } = customOpts; const isAllCustom = storage === true; const storageOpts = isAllCustom ? {} : Object.assign({}, storage || {}); const isCustomResizable = isAllCustom || storageOpts.resizable; const isCustomVisible = isAllCustom || storageOpts.visible; const isCustomFixed = isAllCustom || storageOpts.fixed; const isCustomSort = isAllCustom || storageOpts.sort; if ((customConfig ? (0, _utils.isEnableConf)(customOpts) : customOpts.enabled) && (isCustomResizable || isCustomVisible || isCustomFixed || isCustomSort)) { if (!tableId) { (0, _log.errLog)('vxe.error.reqProp', ['id']); return; } const storeData = getCustomStorageMap(tableId); if (restoreStore) { return Promise.resolve(restoreStore({ id: tableId, type: 'restore', storeData })).then(storeData => { if (!storeData) { return; } return handleCustomRestore(storeData); }).catch(e => e); } else { return handleCustomRestore(storeData); } } }; /** * 更新数据列的 Map * 牺牲数据组装的耗时,用来换取使用过程中的流畅 */ const cacheColumnMap = () => { const { tableFullColumn, collectColumn } = internalData; const fullColumnIdData = internalData.fullColumnIdData = {}; const fullColumnFieldData = internalData.fullColumnFieldData = {}; const mouseOpts = computeMouseOpts.value; const columnOpts = computeColumnOpts.value; const columnDragOpts = computeColumnDragOpts.value; const { isCrossDrag, isSelfToChildDrag } = columnDragOpts; const customOpts = computeCustomOpts.value; const { storage } = customOpts; const rowOpts = computeRowOpts.value; const isGroup = collectColumn.some(_utils.hasChildrenList); let isAllOverflow = !!props.showOverflow; let expandColumn; let treeNodeColumn; let checkboxColumn; let radioColumn; let htmlColumn; let hasFixed; const handleFunc = (column, index, items, path, parentColumn) => { const { id: colid, field, fixed, type, treeNode } = column; const rest = { $index: -1, _index: -1, column, colid, index, items, parent: parentColumn || null, width: 0 }; if (field) { if (fullColumnFieldData[field]) { (0, _log.errLog)('vxe.error.colRepet', ['field', field]); } fullColumnFieldData[field] = rest; } else { if (storage && !type || columnOpts.drag && (isCrossDrag || isSelfToChildDrag)) { (0, _log.errLog)('vxe.error.reqProp', [`${column.getTitle() || type || ''} -> column.field`]); } } if (!hasFixed && fixed) { hasFixed = fixed; } if (!htmlColumn && type === 'html') { htmlColumn = column; } if (treeNode) { if (process.env.NODE_ENV === 'development') { if (treeNodeColumn) { (0, _log.warnLog)('vxe.error.colRepet', ['tree-node', treeNode]); } } if (!treeNodeColumn) { treeNodeColumn = column; } } else if (type === 'expand') { if (process.env.NODE_ENV === 'development') { if (expandColumn) { (0, _log.warnLog)('vxe.error.colRepet', ['type', type]); } } if (!expandColumn) { expandColumn = column; } } if (process.env.NODE_ENV === 'development') { if (type === 'checkbox') { if (checkboxColumn) { (0, _log.warnLog)('vxe.error.colRepet', ['type', type]); } if (!checkboxColumn) { checkboxColumn = column; } } else if (type === 'radio') { if (radioColumn) { (0, _log.warnLog)('vxe.error.colRepet', ['type', type]); } if (!radioColumn) { radioColumn = column; } } } if (isAllOverflow && column.showOverflow === false) { isAllOverflow = false; } if (fullColumnIdData[colid]) { (0, _log.errLog)('vxe.error.colRepet', ['colId', colid]); } fullColumnIdData[colid] = rest; }; if (isGroup) { _xeUtils.default.eachTree(collectColumn, (column, index, items, path, parentColumn, nodes) => { column.level = nodes.length; handleFunc(column, index, items, path, parentColumn); }); } else { tableFullColumn.forEach(handleFunc); } if (process.env.NODE_ENV === 'development') { if (expandColumn && mouseOpts.area) { (0, _log.errLog)('vxe.error.errConflicts', ['mouse-config.area', 'column.type=expand']); } } if (process.env.NODE_ENV === 'development') { if (htmlColumn) { if (!columnOpts.useKey) { (0, _log.errLog)('vxe.error.reqProp', ['column-config.useKey & column.type=html']); } if (!rowOpts.useKey) { (0, _log.errLog)('vxe.error.reqProp', ['row-config.useKey & column.type=html']); } } } reactData.isGroup = isGroup; reactData.treeNodeColumn = treeNodeColumn; reactData.expandColumn = expandColumn; reactData.isAllOverflow = isAllOverflow; }; const updateHeight = () => { internalData.customHeight = calcTableHeight('height'); internalData.customMinHeight = calcTableHeight('minHeight'); internalData.customMaxHeight = calcTableHeight('maxHeight'); }; const calcColumnAutoWidth = (column, wrapperEl) => { const cellElList = wrapperEl.querySelectorAll(`.vxe-header--column.${column.id}>.vxe-cell,.vxe-body--column.${column.id}>.vxe-cell,.vxe-footer--column.${column.id}>.vxe-cell`); const firstCellEl = cellElList[0]; let paddingSize = 0; if (firstCellEl) { const cellStyle = getComputedStyle(firstCellEl); paddingSize = Math.floor(_xeUtils.default.toNumber(cellStyle.paddingLeft) + _xeUtils.default.toNumber(cellStyle.paddingRight)) + 2; } let colWidth = column.renderAutoWidth - paddingSize; _xeUtils.default.arrayEach(cellElList, itemEl => { const cellEl = itemEl; const thElem = cellEl.parentElement; let titleWidth = 0; if (`${thElem.tagName}`.toLowerCase() === 'th') { _xeUtils.default.arrayEach(cellEl.children, btnEl => { titleWidth += btnEl.offsetWidth + 1; }); } else { const labelEl = cellEl.firstElementChild; if (labelEl) { titleWidth = labelEl.offsetWidth; } } if (titleWidth) { colWidth = Math.max(colWidth, Math.ceil(titleWidth) + 4); } }); return colWidth + paddingSize; }; const calcCellWidth = () => { const autoWidthColumnList = computeAutoWidthColumnList.value; reactData.isCalcColumn = true; return (0, _vue.nextTick)().then(() => { const { fullColumnIdData } = internalData; const el = refElem.value; if (el) { autoWidthColumnList.forEach(column => { const colid = column.id; const colRest = fullColumnIdData[colid]; const colWidth = calcColumnAutoWidth(column, el); if (colRest) { colRest.width = Math.max(colWidth, colRest.width); } column.renderAutoWidth = colWidth; }); $xeTable.analyColumnWidth(); } reactData.isCalcColumn = false; }); }; /** * 列宽算法,计算单元格列宽,动态分配可用剩余空间 * 支持 px、%、固定 混合分配 * 支持动态列表调整分配 * 支持自动分配偏移量 * 支持 width=60 width=60px width=10% min-width=60 min-width=60px min-width=10% */ const autoCellWidth = () => { const { elemStore } = internalData; const scrollbarOpts = computeScrollbarOpts.value; const tableBody = refTableBody.value; const tableAppendElem = refTableBodyAppend.value; const bodyElem = tableBody ? tableBody.$el : null; if (!bodyElem) { return; } const yHandleEl = refScrollYHandleElem.value; if (!yHandleEl) { return; } const xHandleEl = refScrollXHandleElem.value; if (!xHandleEl) { return; } let tableWidth = 0; const minCellWidth = 40; // 列宽最少限制 40px const bodyWidth = bodyElem.clientWidth; let remainWidth = bodyWidth; let meanWidth = remainWidth / 100; const { fit } = props; const { columnStore } = reactData; const { resizeList, pxMinList, autoMinList, pxList, scaleList, scaleMinList, autoList, remainList } = columnStore; // 最小宽 pxMinList.forEach(column => { const minWidth = _xeUtils.default.toInteger(column.minWidth); tableWidth += minWidth; column.renderWidth = minWidth; }); // 最小自适应 autoMinList.forEach(column => { const scaleWidth = Math.max(60, _xeUtils.default.toInteger(column.renderAutoWidth)); tableWidth += scaleWidth; column.renderWidth = scaleWidth; }); // 最小百分比 scaleMinList.forEach(column => { const scaleWidth = Math.floor(_xeUtils.default.toInteger(column.minWidth) * meanWidth); tableWidth += scaleWidth; column.renderWidth = scaleWidth; }); // 固定百分比 scaleList.forEach(column => { const scaleWidth = Math.floor(_xeUtils.default.toInteger(column.width) * meanWidth); tableWidth += scaleWidth; column.renderWidth = scaleWidth; }); // 固定宽 pxList.forEach(column => { const width = _xeUtils.default.toInteger(column.width); tableWidth += width; column.renderWidth = width; }); // 自适应宽 autoList.forEach(column => { const width = Math.max(60, _xeUtils.default.toInteger(column.renderAutoWidth)); tableWidth += width; column.renderWidth = width; }); // 调整了列宽 resizeList.forEach(column => { const width = _xeUtils.default.toInteger(column.resizeWidth); tableWidth += width; column.renderWidth = width; }); // 有右上角设置列需要加上宽度 if (computeShowSettingColumn.value) { tableWidth += settingColumnWidth; } remainWidth -= tableWidth; meanWidth = remainWidth > 0 ? Math.floor(remainWidth / (scaleMinList.length + pxMinList.length + autoMinList.length + remainList.length)) : 0; if (fit) { if (remainWidth > 0) { scaleMinList.concat(pxMinList).concat(autoMinList).forEach(column => { tableWidth += meanWidth; column.renderWidth += meanWidth; }); } } else { meanWidth = minCellWidth; } // 剩余均分 remainList.forEach(column => { const width = Math.max(meanWidth, minCellWidth); column.renderWidth = width; tableWidth += width; }); if (fit) { /** * 偏移量算法 * 如果所有列足够放的情况下,从最后动态列开始分配 */ const dynamicList = scaleList.concat(scaleMinList).concat(pxMinList).concat(autoMinList).concat(remainList); let dynamicSize = dynamicList.length - 1; if (dynamicSize > 0) { let i = bodyWidth - tableWidth; if (i > 0) { while (i > 0 && dynamicSize >= 0) { i--; dynamicList[dynamicSize--].renderWidth++; } tableWidth = bodyWidth; } } } const tableHeight = bodyElem.offsetHeight; const overflowY = yHandleEl.scrollHeight > yHandleEl.clientHeight; reactData.scrollbarWidth = overflowY ? Math.max(scrollbarOpts.width || 0, yHandleEl.offsetWidth - yHandleEl.clientWidth) : 0; reactData.overflowY = overflowY; internalData.tableWidth = tableWidth; internalData.tableHeight = tableHeight; const headerTableElem = (0, _util.getRefElem)(elemStore['main-header-table']); const footerTableElem = (0, _util.getRefElem)(elemStore['main-footer-table']); const headerHeight = headerTableElem ? headerTableElem.clientHeight : 0; const overflowX = tableWidth > bodyWidth; let footerHeight = footerTableElem ? footerTableElem.clientHeight : 0; if (tableAppendElem) { footerHeight += tableAppendElem.offsetHeight; } reactData.scrollbarHeight = overflowX ? Math.max(scrollbarOpts.height || 0, xHandleEl.offsetHeight - xHandleEl.clientHeight) : 0; internalData.headerHeight = headerHeight; internalData.footerHeight = footerHeight; reactData.overflowX = overflowX; updateHeight(); reactData.parentHeight = Math.max(internalData.headerHeight + footerHeight + 20, $xeTable.getParentHeight()); if (overflowX) { $xeTable.checkScrolling(); } }; // const updateCellOffset = () => { // const { chTimeout, chRunTime } = inte