diff --git a/package.json b/package.json index efaad1cdca..08b09a9eab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vxe-table", - "version": "4.9.31", + "version": "4.9.32", "description": "一个基于 vue 的 PC 端表格组件,支持增删改查、虚拟树、拖拽排序,懒加载、快捷菜单、数据校验、树形结构、打印、导入导出、自定义模板、渲染器、JSON 配置式...", "scripts": { "update": "npm install --legacy-peer-deps", @@ -28,7 +28,7 @@ "style": "lib/style.css", "typings": "types/index.d.ts", "dependencies": { - "vxe-pc-ui": "^4.3.37" + "vxe-pc-ui": "^4.3.38" }, "devDependencies": { "@types/resize-observer-browser": "^0.1.11", diff --git a/packages/grid/src/grid.ts b/packages/grid/src/grid.ts index ff3b195ae7..e0a7f7cb90 100644 --- a/packages/grid/src/grid.ts +++ b/packages/grid/src/grid.ts @@ -17,7 +17,7 @@ const { getConfig, getI18n, commands, hooks, useFns, createEvent, globalEvents, const tableComponentPropKeys = Object.keys(tableComponentProps as any) -const tableComponentMethodKeys: (keyof VxeTableMethods)[] = ['clearAll', 'syncData', 'updateData', 'loadData', 'reloadData', 'reloadRow', 'loadColumn', 'reloadColumn', 'getRowNode', 'getColumnNode', 'getRowIndex', 'getVTRowIndex', 'getVMRowIndex', 'getColumnIndex', 'getVTColumnIndex', 'getVMColumnIndex', 'setRow', 'createData', 'createRow', 'revertData', 'clearData', 'isInsertByRow', 'isUpdateByRow', 'getColumns', 'getColumnById', 'getColumnByField', 'getTableColumn', 'getFullColumns', 'getData', 'getCheckboxRecords', 'getParentRow', 'getTreeParentRow', 'getRowSeq', 'getRowById', 'getRowid', 'getTableData', 'getFullData', 'setColumnFixed', 'clearColumnFixed', 'setColumnWidth', 'getColumnWidth', 'hideColumn', 'showColumn', 'resetColumn', 'refreshColumn', 'refreshScroll', 'recalculate', 'closeTooltip', 'isAllCheckboxChecked', 'isAllCheckboxIndeterminate', 'getCheckboxIndeterminateRecords', 'setCheckboxRow', 'isCheckedByCheckboxRow', 'isIndeterminateByCheckboxRow', 'toggleCheckboxRow', 'setAllCheckboxRow', 'getRadioReserveRecord', 'clearRadioReserve', 'getCheckboxReserveRecords', 'clearCheckboxReserve', 'toggleAllCheckboxRow', 'clearCheckboxRow', 'setCurrentRow', 'isCheckedByRadioRow', 'setRadioRow', 'clearCurrentRow', 'clearRadioRow', 'getCurrentRecord', 'getRadioRecord', 'getCurrentColumn', 'setCurrentColumn', 'clearCurrentColumn', 'setPendingRow', 'togglePendingRow', 'getPendingRecords', 'clearPendingRow', 'sort', 'setSort', 'clearSort', 'isSort', 'getSortColumns', 'closeFilter', 'isFilter', 'isActiveFilterByColumn', 'isRowExpandLoaded', 'clearRowExpandLoaded', 'reloadRowExpand', 'reloadRowExpand', 'toggleRowExpand', 'setAllRowExpand', 'setRowExpand', 'isExpandByRow', 'isRowExpandByRow', 'clearRowExpand', 'clearRowExpandReserve', 'getRowExpandRecords', 'getTreeExpandRecords', 'isTreeExpandLoaded', 'clearTreeExpandLoaded', 'reloadTreeExpand', 'reloadTreeChilds', 'toggleTreeExpand', 'setAllTreeExpand', 'setTreeExpand', 'isTreeExpandByRow', 'clearTreeExpand', 'clearTreeExpandReserve', 'getScroll', 'scrollTo', 'scrollToRow', 'scrollToColumn', 'clearScroll', 'updateFooter', 'updateStatus', 'setMergeCells', 'removeInsertRow', 'removeMergeCells', 'getMergeCells', 'clearMergeCells', 'setMergeFooterItems', 'removeMergeFooterItems', 'getMergeFooterItems', 'clearMergeFooterItems', 'getCustomStoreData', 'openTooltip', 'getCellLabel', 'getCellElement', 'focus', 'blur', 'connect'] +const tableComponentMethodKeys: (keyof VxeTableMethods)[] = ['clearAll', 'syncData', 'updateData', 'loadData', 'reloadData', 'reloadRow', 'loadColumn', 'reloadColumn', 'getRowNode', 'getColumnNode', 'getRowIndex', 'getVTRowIndex', 'getVMRowIndex', 'getColumnIndex', 'getVTColumnIndex', 'getVMColumnIndex', 'setRow', 'createData', 'createRow', 'revertData', 'clearData', 'isInsertByRow', 'isUpdateByRow', 'getColumns', 'getColumnById', 'getColumnByField', 'getTableColumn', 'getFullColumns', 'getData', 'getCheckboxRecords', 'getParentRow', 'getTreeParentRow', 'getRowSeq', 'getRowById', 'getRowid', 'getTableData', 'getFullData', 'setColumnFixed', 'clearColumnFixed', 'setColumnWidth', 'getColumnWidth', 'hideColumn', 'showColumn', 'resetColumn', 'refreshColumn', 'refreshScroll', 'recalculate', 'closeTooltip', 'isAllCheckboxChecked', 'isAllCheckboxIndeterminate', 'getCheckboxIndeterminateRecords', 'setCheckboxRow', 'setCheckboxRowKey', 'isCheckedByCheckboxRow', 'isCheckedByCheckboxRowKey', 'isIndeterminateByCheckboxRow', 'isIndeterminateByCheckboxRowKey', 'toggleCheckboxRow', 'setAllCheckboxRow', 'getRadioReserveRecord', 'clearRadioReserve', 'getCheckboxReserveRecords', 'clearCheckboxReserve', 'toggleAllCheckboxRow', 'clearCheckboxRow', 'setCurrentRow', 'isCheckedByRadioRow', 'isCheckedByRadioRowKey', 'setRadioRow', 'setRadioRowKey', 'clearCurrentRow', 'clearRadioRow', 'getCurrentRecord', 'getRadioRecord', 'getCurrentColumn', 'setCurrentColumn', 'clearCurrentColumn', 'setPendingRow', 'togglePendingRow', 'getPendingRecords', 'clearPendingRow', 'sort', 'setSort', 'clearSort', 'isSort', 'getSortColumns', 'closeFilter', 'isFilter', 'isActiveFilterByColumn', 'isRowExpandLoaded', 'clearRowExpandLoaded', 'reloadRowExpand', 'reloadRowExpand', 'toggleRowExpand', 'setAllRowExpand', 'setRowExpand', 'isExpandByRow', 'isRowExpandByRow', 'clearRowExpand', 'clearRowExpandReserve', 'getRowExpandRecords', 'getTreeExpandRecords', 'isTreeExpandLoaded', 'clearTreeExpandLoaded', 'reloadTreeExpand', 'reloadTreeChilds', 'toggleTreeExpand', 'setAllTreeExpand', 'setTreeExpand', 'isTreeExpandByRow', 'clearTreeExpand', 'clearTreeExpandReserve', 'getScroll', 'scrollTo', 'scrollToRow', 'scrollToColumn', 'clearScroll', 'updateFooter', 'updateStatus', 'setMergeCells', 'removeInsertRow', 'removeMergeCells', 'getMergeCells', 'clearMergeCells', 'setMergeFooterItems', 'removeMergeFooterItems', 'getMergeFooterItems', 'clearMergeFooterItems', 'getCustomStoreData', 'openTooltip', 'getCellLabel', 'getCellElement', 'focus', 'blur', 'connect'] const gridComponentEmits: VxeGridEmits = [ ...tableComponentEmits, diff --git a/packages/table/module/edit/hook.ts b/packages/table/module/edit/hook.ts index faddfd9f32..d245e4d285 100644 --- a/packages/table/module/edit/hook.ts +++ b/packages/table/module/edit/hook.ts @@ -10,7 +10,7 @@ import type { TableEditMethods, TableEditPrivateMethods } from '../../../../type const { getConfig, renderer, hooks, getI18n } = VxeUI -const tableEditMethodKeys: (keyof TableEditMethods)[] = ['insert', 'insertAt', 'insertNextAt', 'remove', 'removeCheckboxRow', 'removeRadioRow', 'removeCurrentRow', 'getRecordset', 'getInsertRecords', 'getRemoveRecords', 'getUpdateRecords', 'getEditRecord', 'getActiveRecord', 'getSelectedCell', 'clearEdit', 'clearActived', 'clearSelected', 'isEditByRow', 'isActiveByRow', 'setEditRow', 'setActiveRow', 'setEditCell', 'setActiveCell', 'setSelectCell'] +const tableEditMethodKeys: (keyof TableEditMethods)[] = ['insert', 'insertAt', 'insertNextAt', 'insertChild', 'insertChildAt', 'insertChildNextAt', 'remove', 'removeCheckboxRow', 'removeRadioRow', 'removeCurrentRow', 'getRecordset', 'getInsertRecords', 'getRemoveRecords', 'getUpdateRecords', 'getEditRecord', 'getActiveRecord', 'getSelectedCell', 'clearEdit', 'clearActived', 'clearSelected', 'isEditByRow', 'isActiveByRow', 'setEditRow', 'setActiveRow', 'setEditCell', 'setActiveCell', 'setSelectCell'] hooks.add('tableEditModule', { setupTable ($xeTable) { @@ -104,7 +104,7 @@ hooks.add('tableEditModule', { }) } - const handleInsertRowAt = (records: any, row: any, isInsertNextRow?: boolean) => { + const handleInsertRowAt = (records: any, targetRow: any, isInsertNextRow?: boolean) => { const { treeConfig } = props const { mergeList, editStore } = reactData const { tableFullTreeData, afterFullData, tableFullData, fullDataRowIdData, fullAllDataRowIdData } = internalData @@ -115,7 +115,7 @@ hooks.add('tableEditModule', { records = [records] } const newRecords: any[] = reactive($xeTable.defineField(records.map((record: any) => Object.assign(treeConfig && transform ? { [mapChildrenField]: [], [childrenField]: [] } : {}, record)))) - if (XEUtils.eqNull(row)) { + if (XEUtils.eqNull(targetRow)) { // 如果为虚拟树 if (treeConfig && transform) { insertTreeRow(newRecords, false) @@ -137,7 +137,7 @@ hooks.add('tableEditModule', { }) } } else { - if (row === -1) { + if (targetRow === -1) { // 如果为虚拟树 if (treeConfig && transform) { insertTreeRow(newRecords, true) @@ -161,7 +161,7 @@ hooks.add('tableEditModule', { } else { // 如果为虚拟树 if (treeConfig && transform) { - const matchMapObj = XEUtils.findTree(tableFullTreeData, item => row[rowField] === item[rowField], { children: mapChildrenField }) + const matchMapObj = XEUtils.findTree(tableFullTreeData, item => targetRow[rowField] === item[rowField], { children: mapChildrenField }) if (matchMapObj) { const { parent: parentRow } = matchMapObj const parentMapChilds = parentRow ? parentRow[mapChildrenField] : tableFullTreeData @@ -191,7 +191,7 @@ hooks.add('tableEditModule', { // 源 if (parentRow) { - const matchObj = XEUtils.findTree(tableFullTreeData, item => row[rowField] === item[rowField], { children: childrenField }) + const matchObj = XEUtils.findTree(tableFullTreeData, item => targetRow[rowField] === item[rowField], { children: childrenField }) if (matchObj) { const parentChilds = matchObj.items let targetIndex = matchObj.index @@ -213,12 +213,12 @@ hooks.add('tableEditModule', { } let afIndex = -1 // 如果是可视索引 - if (XEUtils.isNumber(row)) { - if (row < afterFullData.length) { - afIndex = row + if (XEUtils.isNumber(targetRow)) { + if (targetRow < afterFullData.length) { + afIndex = targetRow } } else { - afIndex = $xeTable.findRowIndexOf(afterFullData, row) + afIndex = $xeTable.findRowIndexOf(afterFullData, targetRow) } // 如果是插入指定行的下一行 if (isInsertNextRow) { @@ -228,7 +228,7 @@ hooks.add('tableEditModule', { throw new Error(getI18n('vxe.error.unableInsert')) } afterFullData.splice(afIndex, 0, ...newRecords) - tableFullData.splice($xeTable.findRowIndexOf(tableFullData, row), 0, ...newRecords) + tableFullData.splice($xeTable.findRowIndexOf(tableFullData, targetRow), 0, ...newRecords) // 刷新单元格合并 mergeList.forEach((mergeItem: any) => { const { row: mergeRowIndex, rowspan: mergeRowspan } = mergeItem @@ -268,6 +268,21 @@ hooks.add('tableEditModule', { }) } + const handleInsertChildRowAt = (records: any, parentRow: any, targetRow: any, isInsertNextRow?: boolean) => { + const { treeConfig } = props + const treeOpts = computeTreeOpts.value + const { transform, rowField, parentField } = treeOpts + if (treeConfig && transform) { + if (!XEUtils.isArray(records)) { + records = [records] + } + return handleInsertRowAt(records.map((item: any) => Object.assign({}, item, { [parentField]: parentRow[rowField] })), targetRow, isInsertNextRow) + } else { + errLog('vxe.error.errProp', ['tree-config.treeConfig=false', 'tree-config.treeConfig=true']) + } + return Promise.resolve({ row: null, rows: [] }) + } + const handleClearEdit = (evnt: Event | null, targetRow?: any) => { const { mouseConfig } = props const { editStore } = reactData @@ -316,7 +331,7 @@ hooks.add('tableEditModule', { * * @param {*} records */ - insert (records: any) { + insert (records) { return handleInsertRowAt(records, null) }, /** @@ -325,13 +340,22 @@ hooks.add('tableEditModule', { * 如果 row 为 -1 则从插入到底部,如果为树结构,则插入到目标节点底部 * 如果 row 为有效行则插入到该行的位置,如果为树结构,则有插入到效的目标节点该行的位置 * @param {Object/Array} records 新的数据 - * @param {Row} row 指定行 + * @param {Row} targetRow 指定行 */ - insertAt (records: any, row: any) { - return handleInsertRowAt(records, row) + insertAt (records, targetRow) { + return handleInsertRowAt(records, targetRow) + }, + insertNextAt (records, targetRow) { + return handleInsertRowAt(records, targetRow, true) + }, + insertChild (records, parentRow) { + return handleInsertChildRowAt(records, parentRow, null) + }, + insertChildAt (records, parentRow, targetRow) { + return handleInsertChildRowAt(records, parentRow, targetRow) }, - insertNextAt (records: any, row: any) { - return handleInsertRowAt(records, row, true) + insertChildNextAt (records, parentRow, targetRow) { + return handleInsertChildRowAt(records, parentRow, targetRow, true) }, /** * 删除指定行数据 diff --git a/packages/table/module/export/hook.ts b/packages/table/module/export/hook.ts index 8327148f61..c5ae5b45bd 100644 --- a/packages/table/module/export/hook.ts +++ b/packages/table/module/export/hook.ts @@ -2,7 +2,7 @@ import { inject, nextTick } from 'vue' import XEUtils from 'xe-utils' import { VxeUI } from '../../../ui' import { isColumnInfo, mergeBodyMethod, getCellValue } from '../../src/util' -import { parseFile, formatText } from '../../../ui/src/utils' +import { parseFile, formatText, eqEmptyValue } from '../../../ui/src/utils' import { createHtmlPage, getExportBlobByContent } from './util' import { warnLog, errLog } from '../../../ui/src/log' @@ -320,6 +320,10 @@ hooks.add('tableExportModule', { return XEUtils.isBoolean(cellValue) ? (cellValue ? 'TRUE' : 'FALSE') : cellValue } + const toStringValue = (cellValue: any) => { + return eqEmptyValue(cellValue) ? '' : `${cellValue}` + } + const getBodyLabelData = (opts: any, columns: any[], datas: any[]) => { const { isAllExpand, mode } = opts const { treeConfig } = props @@ -347,7 +351,7 @@ hooks.add('tableExportModule', { _expand: hasRowChild && $xeTable.isTreeExpandByRow(row) } columns.forEach((column, $columnIndex) => { - let cellValue: string | number | boolean = '' + let cellValue: string | number | boolean | null = '' const renderOpts = column.editRender || column.cellRender let bodyExportMethod = column.exportMethod || columnOpts.exportMethod if (!bodyExportMethod && renderOpts && renderOpts.name) { @@ -382,7 +386,7 @@ hooks.add('tableExportModule', { if (opts.original) { cellValue = getCellValue(row, column) } else { - cellValue = `${$xeTable.getCellLabel(row, column)}` + cellValue = $xeTable.getCellLabel(row, column) if (column.type === 'html') { htmlCellElem.innerHTML = cellValue cellValue = htmlCellElem.innerText.trim() @@ -395,7 +399,7 @@ hooks.add('tableExportModule', { } } } - item[column.id] = XEUtils.toValueString(cellValue) + item[column.id] = toStringValue(cellValue) }) expandMaps.set(row, 1) rest.push(Object.assign(item, row)) @@ -408,7 +412,7 @@ hooks.add('tableExportModule', { _row: row } columns.forEach((column, $columnIndex) => { - let cellValue: string | number | boolean = '' + let cellValue: string | number | boolean | null = '' const renderOpts = column.editRender || column.cellRender let bodyExportMethod = column.exportMethod || columnOpts.exportMethod if (!bodyExportMethod && renderOpts && renderOpts.name) { @@ -440,7 +444,7 @@ hooks.add('tableExportModule', { if (opts.original) { cellValue = getCellValue(row, column) } else { - cellValue = `${$xeTable.getCellLabel(row, column)}` + cellValue = $xeTable.getCellLabel(row, column) if (column.type === 'html') { htmlCellElem.innerHTML = cellValue cellValue = htmlCellElem.innerText.trim() @@ -453,7 +457,7 @@ hooks.add('tableExportModule', { } } } - item[column.id] = XEUtils.toValueString(cellValue) + item[column.id] = toStringValue(cellValue) }) return item }) diff --git a/styles/components/table.scss b/styles/components/table.scss index 02e6147c65..56f72d10fb 100644 --- a/styles/components/table.scss +++ b/styles/components/table.scss @@ -78,17 +78,6 @@ .vxe-ico-picker { width: 100%; } - .vxe-cell--tree-node { - .vxe-input, - .vxe-textarea, - .vxe-select, - .vxe-tree-select, - .vxe-date-picker, - .vxe-number-input, - .vxe-ico-picker { - width: 100%; - } - } } } } @@ -110,6 +99,9 @@ width: 100%; } & > .vxe-cell--tree-node { + .vxe-default-input, + .vxe-default-textarea, + .vxe-default-select, .vxe-input, .vxe-textarea, .vxe-select, @@ -138,17 +130,6 @@ & > .vxe-ico-picker { width: 100%; } - & > .vxe-cell--tree-node { - .vxe-input, - .vxe-textarea, - .vxe-select, - .vxe-tree-select, - .vxe-date-picker, - .vxe-number-input, - .vxe-ico-picker { - width: 100%; - } - } } .vxe-cell, .vxe-table--filter-template {