diff --git a/examples/views/table/TableTest2.vue b/examples/views/table/TableTest2.vue index 243a81f6eb..3abc4b6b1f 100644 --- a/examples/views/table/TableTest2.vue +++ b/examples/views/table/TableTest2.vue @@ -13,8 +13,9 @@ ref="tableRef" id="bbbbb" :row-config="{useKey: true,drag:true}" + :row-drag-config="{trigger:'row',disabledMethod:disabledRowMethod}" :column-config="{useKey: true,drag: true}" - :column-drag-config="{isCrossDrag:true,isToChildDrag:true,isSelfToChildDrag:true}" + :column-drag-config="{isCrossDrag:true,isToChildDrag:true,isSelfToChildDrag:true,trigger:'default',disabledMethod:disabledColumnMethod}" :custom-config="customConfig" :loading="demo1.loading" :import-config="{modes: importModes}" @@ -38,10 +39,10 @@ - - + + - + item.value === cellValue) return item ? item.label : '' diff --git a/package.json b/package.json index c73481cc92..214ed40e54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vxe-table", - "version": "3.11.31", + "version": "3.11.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": "^3.3.35" + "vxe-pc-ui": "^3.3.36" }, "devDependencies": { "@babel/plugin-transform-modules-commonjs": "^7.25.7", diff --git a/packages/table/module/keyboard/mixin.ts b/packages/table/module/keyboard/mixin.ts index 21e9a29b65..ec38b87a3e 100644 --- a/packages/table/module/keyboard/mixin.ts +++ b/packages/table/module/keyboard/mixin.ts @@ -1,5 +1,5 @@ import XEUtils from 'xe-utils' -import { browse, hasClass, getAbsolutePos, addClass, removeClass, getEventTargetNode } from '../../../ui/src/dom' +import { browse, hasClass, getAbsolutePos, addClass, removeClass } from '../../../ui/src/dom' function getTargetOffset (target: any, container: any) { let offsetTop = 0 @@ -186,31 +186,6 @@ export default { this.handleSelected(params, evnt) }) }, - /** - * 表头单元格按下事件 - */ - triggerHeaderCellMousedownEvent (evnt: any, params: any) { - const { mouseConfig, mouseOpts } = this - if (mouseConfig && mouseOpts.area && this.handleHeaderCellAreaEvent) { - const cell = evnt.currentTarget - const triggerSort = getEventTargetNode(evnt, cell, 'vxe-cell--sort').flag - const triggerFilter = getEventTargetNode(evnt, cell, 'vxe-cell--filter').flag - this.handleHeaderCellAreaEvent(evnt, Object.assign({ cell, triggerSort, triggerFilter }, params)) - } - this.focus() - this.closeMenu() - }, - /** - * 单元格按下事件 - */ - triggerCellMousedownEvent (evnt: any, params: any) { - const cell = evnt.currentTarget - params.cell = cell - this.handleCellMousedownEvent(evnt, params) - this.focus() - this.closeFilter() - this.closeMenu() - }, handleCellMousedownEvent (evnt: any, params: any) { const { editConfig, editOpts, handleSelected, checkboxConfig, checkboxOpts, mouseConfig, mouseOpts } = this if (mouseConfig && mouseOpts.area && this.handleMousedownCellAreaEvent) { diff --git a/packages/table/src/body.ts b/packages/table/src/body.ts index b326baab7e..89ae7af7e3 100644 --- a/packages/table/src/body.ts +++ b/packages/table/src/body.ts @@ -93,6 +93,8 @@ function renderColumn (h: any, _vm: any, $xetable: any, seq: any, rowid: any, fi areaOpts, validErrorMaps } = $xetable + const rowDragOpts = $xetable.computeRowDragOpts + const { disabledMethod: dragDisabledMethod } = rowDragOpts const { selectCellToRow } = areaOpts const cellOpts = $xetable.computeCellOpts const { type, cellRender, editRender, align, showOverflow, className, treeNode, slots } = column @@ -126,6 +128,14 @@ function renderColumn (h: any, _vm: any, $xetable: any, seq: any, rowid: any, fi const bindMouseleave = tableListeners['cell-mouseleave'] const triggerDblclick = (editRender && editConfig && editOpts.trigger === 'dblclick') const params = { $table: $xetable, $grid: $xetable.$xegrid, isEdit: false, seq, rowid, row, rowIndex, $rowIndex, _rowIndex, column, columnIndex, $columnIndex, _columnIndex, fixed: fixedType, type: renderType, isHidden: fixedHiddenColumn, level: rowLevel, visibleData: afterFullData, data: tableData, items } + let isColDragCell = false + let isDisabledDrag = false + if (rowOpts.drag) { + isColDragCell = rowDragOpts.trigger === 'row' || (column.dragSort && rowDragOpts.trigger === 'cell') + } + if (isColDragCell) { + isDisabledDrag = !!(dragDisabledMethod && dragDisabledMethod(params)) + } // hover 进入事件 if (showTitle || showTooltip || showAllTip || bindMouseenter || tooltipConfig) { tdOns.mouseenter = (evnt: any) => { @@ -158,7 +168,7 @@ function renderColumn (h: any, _vm: any, $xetable: any, seq: any, rowid: any, fi } } // 按下事件处理 - if (checkboxOpts.range || mouseConfig) { + if (isColDragCell || checkboxOpts.range || mouseConfig) { tdOns.mousedown = (evnt: any) => { $xetable.triggerCellMousedownEvent(evnt, params) } @@ -314,6 +324,8 @@ function renderColumn (h: any, _vm: any, $xetable: any, seq: any, rowid: any, fi 'col--edit': isEdit, 'col--ellipsis': hasEllipsis, 'fixed--hidden': fixedHiddenColumn, + 'is--drag-cell': isColDragCell, + 'is--drag-disabled': isDisabledDrag, 'col--dirty': isDirty, 'col--active': editConfig && isEdit && (actived.row === row && (actived.column === column || editOpts.mode === 'row')), 'col--valid-error': !!errorValidItem, diff --git a/packages/table/src/cell.ts b/packages/table/src/cell.ts index 3d5c01fede..a29c24cc5c 100644 --- a/packages/table/src/cell.ts +++ b/packages/table/src/cell.ts @@ -53,22 +53,24 @@ function renderCellDragIcon (h: CreateElement, params: any) { const tableProps = $table const { dragConfig } = tableProps const rowDragOpts = $table.computeRowDragOpts - const { icon, disabledMethod } = rowDragOpts + const { icon, trigger, disabledMethod } = rowDragOpts const rDisabledMethod = disabledMethod || (dragConfig ? dragConfig.rowDisabledMethod : null) const isDisabled = rDisabledMethod && rDisabledMethod(params) + const ons: Record = {} + if (trigger !== 'cell') { + ons.mousedown = (evnt: MouseEvent) => { + if (!isDisabled) { + $table.handleCellDragMousedownEvent(evnt, params) + } + } + ons.mouseup = $table.handleCellDragMouseupEvent + } return h('span', { key: 'dg', class: ['vxe-cell--drag-handle', { 'is--disabled': isDisabled }], - on: { - mousedown (evnt: DragEvent) { - if (!isDisabled) { - $table.handleCellDragMousedownEvent(evnt, params) - } - }, - mouseup: $table.handleCellDragMouseupEvent - } + on: ons }, [ h('i', { class: icon || (dragConfig ? dragConfig.rowIcon : '') || getIcon().TABLE_DRAG_ROW @@ -106,23 +108,25 @@ function renderHeaderCellDragIcon (h: CreateElement, params: VxeTableDefines.Cel const { $table, column } = params const columnOpts = $table.computeColumnOpts const columnDragOpts = $table.computeColumnDragOpts - const { showIcon, icon, isCrossDrag, visibleMethod, disabledMethod } = columnDragOpts - const isDisabled = disabledMethod && disabledMethod(params) + const { showIcon, icon, trigger, isCrossDrag, visibleMethod, disabledMethod } = columnDragOpts if (columnOpts.drag && showIcon && (!visibleMethod || visibleMethod(params))) { if (!column.fixed && (isCrossDrag || !column.parentId)) { + const isDisabled = disabledMethod && disabledMethod(params) + const ons: Record = {} + if (trigger !== 'cell') { + ons.mousedown = (evnt: MouseEvent) => { + if (!isDisabled) { + $table.handleHeaderCellDragMousedownEvent(evnt, params) + } + } + ons.mouseup = $table.handleHeaderCellDragMouseupEvent + } return h('span', { key: 'dg', class: ['vxe-cell--drag-handle', { 'is--disabled': isDisabled }], - on: { - mousedown (evnt: MouseEvent) { - if (!isDisabled) { - $table.handleHeaderCellDragMousedownEvent(evnt, params) - } - }, - mouseup: $table.handleHeaderCellDragMouseupEvent - } + on: ons }, [ h('i', { class: icon || getIcon().TABLE_DRAG_COLUMN diff --git a/packages/table/src/header.ts b/packages/table/src/header.ts index 17559feef4..e4de97f0ca 100644 --- a/packages/table/src/header.ts +++ b/packages/table/src/header.ts @@ -12,9 +12,14 @@ const { renderer } = VxeUI const cellType = 'header' const renderRows = (h: CreateElement, _vm: any, cols: VxeTableDefines.ColumnInfo[], $rowIndex: number) => { - const { $parent: $xetable, fixedType } = _vm - const { $listeners: tableListeners, resizable: allResizable, border, columnKey, headerCellClassName, headerCellStyle, showHeaderOverflow: allColumnHeaderOverflow, headerAlign: allHeaderAlign, align: allAlign, highlightCurrentColumn, currentColumn, scrollXLoad, overflowX, scrollbarWidth, sortOpts, mouseConfig, columnOpts } = $xetable + const $xeTable = _vm.$parent + const $xeGrid = $xeTable.xegrid + const { fixedType } = _vm + + const { resizable: allResizable, border, columnKey, headerCellClassName, headerCellStyle, showHeaderOverflow: allColumnHeaderOverflow, headerAlign: allHeaderAlign, align: allAlign, currentColumn, scrollXLoad, overflowX, scrollbarWidth, mouseConfig, columnOpts } = $xeTable + const columnDragOpts = $xeTable.computeColumnDragOpts + const { disabledMethod: dragDisabledMethod } = columnDragOpts return cols.map((column: any, $columnIndex: any) => { const { type, showHeaderOverflow, headerAlign, align, headerClassName, editRender, cellRender } = column // const { enabled } = tooltipOpts @@ -29,30 +34,40 @@ const renderRows = (h: CreateElement, _vm: any, cols: VxeTableDefines.ColumnInfo const showTitle = headOverflow === 'title' const showTooltip = headOverflow === true || headOverflow === 'tooltip' let hasEllipsis = showTitle || showTooltip || showEllipsis - const thOns: any = {} const hasFilter = column.filters && column.filters.some((item: any) => item.checked) - const columnIndex = $xetable.getColumnIndex(column) - const _columnIndex = $xetable.getVTColumnIndex(column) - const params = { $table: $xetable, $grid: $xetable.xegrid, $rowIndex, column, columnIndex, $columnIndex, _columnIndex, fixed: fixedType, type: cellType, isHidden: fixedHiddenColumn, hasFilter } + const columnIndex = $xeTable.getColumnIndex(column) + const _columnIndex = $xeTable.getVTColumnIndex(column) + const params = { $table: $xeTable, $grid: $xeGrid, $rowIndex, column, columnIndex, $columnIndex, _columnIndex, fixed: fixedType, type: cellType, isHidden: fixedHiddenColumn, hasFilter } + const thAttrs: Record = { + colid, + colspan: column.colSpan > 1 ? column.colSpan : null, + rowspan: column.rowSpan > 1 ? column.rowSpan : null + } + const thOns: any = { + click: (evnt: MouseEvent) => $xeTable.triggerHeaderCellClickEvent(evnt, params), + dblclick: (evnt: MouseEvent) => $xeTable.triggerHeaderCellDblclickEvent(evnt, params) + } // 虚拟滚动不支持动态高度 if (scrollXLoad && !hasEllipsis) { showEllipsis = hasEllipsis = true } - if (columnOpts.isCurrent || highlightCurrentColumn || tableListeners['header-cell-click'] || sortOpts.trigger === 'cell') { - thOns.click = (evnt: any) => $xetable.triggerHeaderCellClickEvent(evnt, params) - } - if (tableListeners['header-cell-dblclick']) { - thOns.dblclick = (evnt: any) => $xetable.triggerHeaderCellDblclickEvent(evnt, params) + const isColDragCell = columnOpts.drag && columnDragOpts.trigger === 'cell' + let isDisabledDrag = false + if (isColDragCell) { + isDisabledDrag = !!(dragDisabledMethod && dragDisabledMethod(params)) } // 按下事件处理 - if (mouseConfig) { - thOns.mousedown = (evnt: any) => $xetable.triggerHeaderCellMousedownEvent(evnt, params) + if (mouseConfig || isColDragCell) { + thOns.mousedown = (evnt: any) => $xeTable.triggerHeaderCellMousedownEvent(evnt, params) } - // 拖拽行事件 + // 拖拽列事件 if (columnOpts.drag) { - thOns.dragstart = $xetable.handleHeaderCellDragDragstartEvent - thOns.dragend = $xetable.handleHeaderCellDragDragendEvent - thOns.dragover = $xetable.handleHeaderCellDragDragoverEvent + thOns.dragstart = $xeTable.handleHeaderCellDragDragstartEvent + thOns.dragend = $xeTable.handleHeaderCellDragDragendEvent + thOns.dragover = $xeTable.handleHeaderCellDragDragoverEvent + if (isColDragCell) { + thOns.mouseup = $xeTable.handleHeaderCellDragMouseupEvent + } } return h('th', { class: ['vxe-header--column', colid, { @@ -66,13 +81,10 @@ const renderRows = (h: CreateElement, _vm: any, cols: VxeTableDefines.ColumnInfo 'is--sortable': column.sortable, 'col--filter': !!column.filters, 'is--filter-active': hasFilter, + 'is--drag-disabled': isDisabledDrag, 'col--current': currentColumn === column }, getClass(headerClassName, params), getClass(headerCellClassName, params)], - attrs: { - colid, - colspan: column.colSpan > 1 ? column.colSpan : null, - rowspan: column.rowSpan > 1 ? column.rowSpan : null - }, + attrs: thAttrs, style: headerCellStyle ? (XEUtils.isFunction(headerCellStyle) ? headerCellStyle(params) : headerCellStyle) : null, on: thOns, key: columnKey || columnOpts.useKey || columnOpts.drag || isColGroup ? colid : $columnIndex diff --git a/packages/table/src/methods.ts b/packages/table/src/methods.ts index 0399949da5..2c68c5696d 100644 --- a/packages/table/src/methods.ts +++ b/packages/table/src/methods.ts @@ -5156,6 +5156,79 @@ const Methods = { $xeTable.handleColumnSortEvent(evnt, column) } }, + /** + * 表头单元格按下事件 + */ + triggerHeaderCellMousedownEvent (evnt: any, params: any) { + const $xeTable = this + const props = $xeTable + + const { mouseConfig } = props + const mouseOpts = $xeTable.computeMouseOpts + const columnOpts = $xeTable.computeColumnOpts + const columnDragOpts = $xeTable.computeColumnDragOpts + const { trigger, disabledMethod } = columnDragOpts + const cell = evnt.currentTarget + const triggerInput = cell && cell.tagName && cell.tagName.toLowerCase() === 'input' + const triggerCheckbox = getEventTargetNode(evnt, cell, 'vxe-cell--checkbox').flag + const triggerSort = getEventTargetNode(evnt, cell, 'vxe-cell--sort').flag + const triggerFilter = getEventTargetNode(evnt, cell, 'vxe-cell--filter').flag + let triggerDrag = false + if (!(triggerInput || triggerCheckbox || triggerSort || triggerFilter)) { + if (columnOpts.drag && trigger === 'cell' && !(disabledMethod && disabledMethod(params))) { + triggerDrag = true + $xeTable.handleHeaderCellDragMousedownEvent(evnt, params) + } + } + if (!triggerDrag && mouseConfig && mouseOpts.area && $xeTable.handleHeaderCellAreaEvent) { + $xeTable.handleHeaderCellAreaEvent(evnt, Object.assign({ cell, triggerSort, triggerFilter }, params)) + } + $xeTable.focus() + if ($xeTable.closeMenu) { + $xeTable.closeMenu() + } + }, + /** + * 单元格按下事件 + */ + triggerCellMousedownEvent (evnt: any, params: any) { + const $xeTable = this + + const { column } = params + const { type, treeNode } = column + const isRadioType = type === 'radio' + const isCheckboxType = type === 'checkbox' + const isExpandType = type === 'expand' + const rowOpts = $xeTable.computeRowOpts + const rowDragOpts = $xeTable.computeRowDragOpts + const { trigger, disabledMethod } = rowDragOpts + const cell = evnt.currentTarget + params.cell = cell + const triggerInput = cell && cell.tagName && cell.tagName.toLowerCase() === 'input' + const triggerRadio = isRadioType && getEventTargetNode(evnt, cell, 'vxe-cell--radio').flag + const triggerCheckbox = isCheckboxType && getEventTargetNode(evnt, cell, 'vxe-cell--checkbox').flag + const triggerTreeNode = treeNode && getEventTargetNode(evnt, cell, 'vxe-tree--btn-wrapper').flag + const triggerExpandNode = isExpandType && getEventTargetNode(evnt, cell, 'vxe-table--expanded').flag + let isColDragCell = false + if (rowOpts.drag) { + isColDragCell = trigger === 'row' || (column.dragSort && trigger === 'cell') + } + let triggerDrag = false + if (!(triggerInput || triggerRadio || triggerCheckbox || triggerTreeNode || triggerExpandNode)) { + if (isColDragCell && !(disabledMethod && disabledMethod(params))) { + triggerDrag = true + $xeTable.handleCellDragMousedownEvent(evnt, params) + } + } + if (!triggerDrag && $xeTable.handleCellMousedownEvent) { + $xeTable.handleCellMousedownEvent(evnt, params) + } + $xeTable.focus() + $xeTable.closeFilter() + if ($xeTable.closeMenu) { + $xeTable.closeMenu() + } + }, /** * 行拖拽 */ @@ -5395,12 +5468,13 @@ const Methods = { const props = $xeTable const reactData = $xeTable + evnt.stopPropagation() const { dragConfig } = props const rowDragOpts = $xeTable.computeRowDragOpts - const { dragStartMethod } = rowDragOpts + const { trigger, dragStartMethod } = rowDragOpts const { row } = params const dragEl = evnt.currentTarget as HTMLElement - const tdEl = dragEl.parentElement?.parentElement as HTMLElement + const tdEl = trigger === 'cell' || trigger === 'row' ? dragEl : dragEl.parentElement?.parentElement as HTMLElement const trEl = tdEl.parentElement as HTMLElement const dStartMethod = dragStartMethod || (dragConfig ? dragConfig.dragStartMethod : null) reactData.isDragRowMove = false @@ -5652,11 +5726,12 @@ const Methods = { const $xeTable = this const reactData = $xeTable + evnt.stopPropagation() const columnDragOpts = $xeTable.computeColumnDragOpts - const { dragStartMethod } = columnDragOpts + const { trigger, dragStartMethod } = columnDragOpts const { column } = params const dragEl = evnt.currentTarget as HTMLElement - const thEl = dragEl.parentElement?.parentElement as HTMLElement + const thEl = trigger === 'cell' ? dragEl : dragEl.parentElement?.parentElement as HTMLElement reactData.isDragColMove = false clearColDropOrigin($xeTable) if (dragStartMethod && !dragStartMethod(params)) { diff --git a/packages/table/src/table.ts b/packages/table/src/table.ts index d10b047c54..42c152faa8 100644 --- a/packages/table/src/table.ts +++ b/packages/table/src/table.ts @@ -1072,7 +1072,7 @@ export default { if (this.editRules && !this._validate) { errLog('vxe.error.reqModule', ['Validator']) } - if ((this.checkboxOpts.range || this.keyboardConfig || this.mouseConfig) && !this.triggerCellMousedownEvent) { + if ((this.checkboxOpts.range || this.keyboardConfig || this.mouseConfig) && !this.handleCellMousedownEvent) { errLog('vxe.error.reqModule', ['Keyboard']) } if ((this.printConfig || this.importConfig || this.exportConfig) && !this._exportData) { @@ -1253,6 +1253,7 @@ export default { const virtualScrollBars = $xeTable.computeVirtualScrollBars const isArea = mouseConfig && mouseOpts.area const tableStyle = $xeTable.computeTableStyle + const columnDragOpts = $xeTable.computeColumnDragOpts return h('div', { ref: 'refElem', class: ['vxe-table', 'vxe-table--render-default', `tid_${tId}`, vSize ? `size--${vSize}` : '', `border--${tableBorder}`, { @@ -1268,6 +1269,7 @@ export default { 'column--highlight': columnOpts.isHover || highlightHoverColumn, 'checkbox--range': checkboxOpts.range, 'column--calc': isCalcColumn, + 'col--drag-cell': columnOpts.drag && columnDragOpts.trigger === 'cell', 'is--header': showHeader, 'is--footer': showFooter, 'is--group': isGroup, diff --git a/styles/components/table.scss b/styles/components/table.scss index 7d68c429e9..c02d5d5fc0 100644 --- a/styles/components/table.scss +++ b/styles/components/table.scss @@ -677,6 +677,24 @@ } } } + &.col--drag-cell { + .vxe-header--column { + user-select: none; + &:not(.is--drag-disabled) { + cursor: grab; + &:active { + cursor: grabbing; + } + &:hover { + color: var(--vxe-ui-font-primary-color); + } + } + &.is--drag-disabled { + color: var(--vxe-ui-input-disabled-color); + cursor: not-allowed; + } + } + } &.column--highlight { .vxe-header--column { &:not(.col--seq) { @@ -1368,6 +1386,24 @@ } } } + .vxe-body--column { + &.is--drag-cell { + user-select: none; + &:not(.is--drag-disabled) { + cursor: grab; + &:active { + cursor: grabbing; + } + &:hover { + color: var(--vxe-ui-font-primary-color); + } + } + &.is--drag-disabled { + color: var(--vxe-ui-input-disabled-color); + cursor: not-allowed; + } + } + } .vxe-body--row-list-move { transition-property: transform; transition-duration: 0.35s;