vxe-table-ro-test
Version:
一个基于 vue 的 PC 端表格组件,支持增删改查、虚拟树、列拖拽,懒加载、快捷菜单、数据校验、树形结构、打印、导入导出、自定义模板、渲染器、JSON 配置式...
305 lines (304 loc) • 16.9 kB
JavaScript
import { createCommentVNode, defineComponent, h, ref, inject, nextTick, watch, onMounted, onUnmounted } from 'vue';
import XEUtils from 'xe-utils';
import { VxeUI } from '../../ui';
import { convertHeaderColumnToRows, getColReMinWidth } from './util';
import { hasClass, getOffsetPos, addClass, removeClass } from '../../ui/src/dom';
const { renderer } = VxeUI;
const renderType = 'header';
export default defineComponent({
name: 'VxeTableHeader',
props: {
tableData: Array,
tableColumn: Array,
tableGroupColumn: Array,
fixedColumn: Array,
fixedType: {
type: String,
default: null
}
},
setup(props) {
const $xeTable = inject('$xeTable', {});
const { xID, props: tableProps, reactData: tableReactData, internalData: tableInternalData } = $xeTable;
const { refElem: tableRefElem, refTableBody, refLeftContainer, refRightContainer, refCellResizeBar } = $xeTable.getRefMaps();
const { computeColumnOpts } = $xeTable.getComputeMaps();
const headerColumn = ref([]);
const refElem = ref();
const refHeaderTable = ref();
const refHeaderColgroup = ref();
const refHeaderTHead = ref();
const refHeaderXSpace = ref();
const refHeaderBorderRepair = ref();
const uploadColumn = () => {
const { isGroup } = tableReactData;
headerColumn.value = isGroup ? convertHeaderColumnToRows(props.tableGroupColumn) : [];
};
const resizeMousedown = (evnt, params) => {
const { column } = params;
const { fixedType } = props;
const tableBody = refTableBody.value;
const leftContainerElem = refLeftContainer.value;
const rightContainerElem = refRightContainer.value;
const resizeBarElem = refCellResizeBar.value;
const { clientX: dragClientX } = evnt;
const wrapperElem = refElem.value;
const dragBtnElem = evnt.target;
const cell = params.cell = dragBtnElem.parentNode;
let dragLeft = 0;
const tableBodyElem = tableBody.$el;
const pos = getOffsetPos(dragBtnElem, wrapperElem);
const dragBtnWidth = dragBtnElem.clientWidth;
const dragBtnOffsetWidth = Math.floor(dragBtnWidth / 2);
const minInterval = getColReMinWidth(params) - dragBtnOffsetWidth; // 列之间的最小间距
let dragMinLeft = pos.left - cell.clientWidth + dragBtnWidth + minInterval;
let dragPosLeft = pos.left + dragBtnOffsetWidth;
const domMousemove = document.onmousemove;
const domMouseup = document.onmouseup;
const isLeftFixed = fixedType === 'left';
const isRightFixed = fixedType === 'right';
const tableEl = tableRefElem.value;
// 计算左右侧固定列偏移量
let fixedOffsetWidth = 0;
if (isLeftFixed || isRightFixed) {
const siblingProp = isLeftFixed ? 'nextElementSibling' : 'previousElementSibling';
let tempCellElem = cell[siblingProp];
while (tempCellElem) {
if (hasClass(tempCellElem, 'fixed--hidden')) {
break;
}
else if (!hasClass(tempCellElem, 'col--group')) {
fixedOffsetWidth += tempCellElem.offsetWidth;
}
tempCellElem = tempCellElem[siblingProp];
}
if (isRightFixed && rightContainerElem) {
dragPosLeft = rightContainerElem.offsetLeft + fixedOffsetWidth;
}
}
// 处理拖动事件
const updateEvent = function (evnt) {
evnt.stopPropagation();
evnt.preventDefault();
const offsetX = evnt.clientX - dragClientX;
let left = dragPosLeft + offsetX;
const scrollLeft = fixedType ? 0 : tableBodyElem.scrollLeft;
if (isLeftFixed) {
// 左固定列(不允许超过右侧固定列、不允许超过右边距)
left = Math.min(left, (rightContainerElem ? rightContainerElem.offsetLeft : tableBodyElem.clientWidth) - fixedOffsetWidth - minInterval);
}
else if (isRightFixed) {
// 右侧固定列(不允许超过左侧固定列、不允许超过左边距)
dragMinLeft = (leftContainerElem ? leftContainerElem.clientWidth : 0) + fixedOffsetWidth + minInterval;
left = Math.min(left, dragPosLeft + cell.clientWidth - minInterval);
}
else {
dragMinLeft = Math.max(tableBodyElem.scrollLeft, dragMinLeft);
// left = Math.min(left, tableBodyElem.clientWidth + tableBodyElem.scrollLeft - 40)
}
dragLeft = Math.max(left, dragMinLeft);
resizeBarElem.style.left = `${dragLeft - scrollLeft}px`;
};
tableReactData._isResize = true;
addClass(tableEl, 'drag--resize');
resizeBarElem.style.display = 'block';
document.onmousemove = updateEvent;
document.onmouseup = function (evnt) {
document.onmousemove = domMousemove;
document.onmouseup = domMouseup;
const resizeWidth = column.renderWidth + (isRightFixed ? dragPosLeft - dragLeft : dragLeft - dragPosLeft);
column.resizeWidth = resizeWidth;
resizeBarElem.style.display = 'none';
tableReactData._isResize = false;
tableInternalData._lastResizeTime = Date.now();
$xeTable.analyColumnWidth();
$xeTable.recalculate(true).then(() => {
$xeTable.saveCustomStore('update:visible');
$xeTable.updateCellAreas();
$xeTable.dispatchEvent('resizable-change', Object.assign(Object.assign({}, params), { resizeWidth }), evnt);
setTimeout(() => $xeTable.recalculate(true), 300);
});
removeClass(tableEl, 'drag--resize');
};
updateEvent(evnt);
if ($xeTable.closeMenu) {
$xeTable.closeMenu();
}
};
watch(() => props.tableColumn, uploadColumn);
onMounted(() => {
nextTick(() => {
const { fixedType } = props;
const { internalData } = $xeTable;
const { elemStore } = internalData;
const prefix = `${fixedType || 'main'}-header-`;
elemStore[`${prefix}wrapper`] = refElem;
elemStore[`${prefix}table`] = refHeaderTable;
elemStore[`${prefix}colgroup`] = refHeaderColgroup;
elemStore[`${prefix}list`] = refHeaderTHead;
elemStore[`${prefix}xSpace`] = refHeaderXSpace;
elemStore[`${prefix}repair`] = refHeaderBorderRepair;
uploadColumn();
});
});
onUnmounted(() => {
const { fixedType } = props;
const { internalData } = $xeTable;
const { elemStore } = internalData;
const prefix = `${fixedType || 'main'}-header-`;
elemStore[`${prefix}wrapper`] = null;
elemStore[`${prefix}table`] = null;
elemStore[`${prefix}colgroup`] = null;
elemStore[`${prefix}list`] = null;
elemStore[`${prefix}xSpace`] = null;
elemStore[`${prefix}repair`] = null;
});
const renderVN = () => {
const { fixedType, fixedColumn, tableColumn } = props;
const { resizable, border, columnKey, headerRowClassName, headerCellClassName, headerRowStyle, headerCellStyle, showHeaderOverflow: allColumnHeaderOverflow, headerAlign: allHeaderAlign, align: allAlign, mouseConfig } = tableProps;
const { isGroup, currentColumn, scrollXLoad, overflowX, scrollbarWidth } = tableReactData;
const { visibleColumn } = tableInternalData;
const columnOpts = computeColumnOpts.value;
let headerGroups = headerColumn.value;
let renderColumnList = tableColumn;
if (isGroup) {
renderColumnList = visibleColumn;
}
else {
// 如果是使用优化模式
if (fixedType) {
if (scrollXLoad || allColumnHeaderOverflow) {
renderColumnList = fixedColumn;
}
}
headerGroups = [renderColumnList];
}
return h('div', {
ref: refElem,
class: ['vxe-table--header-wrapper', fixedType ? `fixed-${fixedType}--wrapper` : 'body--wrapper'],
xid: xID
}, [
fixedType
? createCommentVNode()
: h('div', {
ref: refHeaderXSpace,
class: 'vxe-body--x-space'
}),
h('table', {
ref: refHeaderTable,
class: 'vxe-table--header',
xid: xID,
cellspacing: 0,
cellpadding: 0,
border: 0
}, [
/**
* 列宽
*/
h('colgroup', {
ref: refHeaderColgroup
}, renderColumnList.map((column, $columnIndex) => {
return h('col', {
name: column.id,
key: $columnIndex
});
}).concat(scrollbarWidth
? [
h('col', {
name: 'col_gutter'
})
]
: [])),
/**
* 头部
*/
h('thead', {
ref: refHeaderTHead
}, headerGroups.map((cols, $rowIndex) => {
return h('tr', {
class: ['vxe-header--row', headerRowClassName ? (XEUtils.isFunction(headerRowClassName) ? headerRowClassName({ $table: $xeTable, $rowIndex, fixed: fixedType, type: renderType }) : headerRowClassName) : ''],
style: headerRowStyle ? (XEUtils.isFunction(headerRowStyle) ? headerRowStyle({ $table: $xeTable, $rowIndex, fixed: fixedType, type: renderType }) : headerRowStyle) : null
}, cols.map((column, $columnIndex) => {
const { type, showHeaderOverflow, headerAlign, align, headerClassName, editRender, cellRender } = column;
const colid = column.id;
const renderOpts = editRender || cellRender;
const compConf = renderOpts ? renderer.get(renderOpts.name) : null;
const isColGroup = column.children && column.children.length;
const fixedHiddenColumn = fixedType ? (column.fixed !== fixedType && !isColGroup) : !!column.fixed && overflowX;
const headOverflow = XEUtils.eqNull(showHeaderOverflow) ? allColumnHeaderOverflow : showHeaderOverflow;
const headAlign = headerAlign || (compConf ? compConf.tableHeaderCellAlign : '') || allHeaderAlign || align || (compConf ? compConf.tableCellAlign : '') || allAlign;
let showEllipsis = headOverflow === 'ellipsis';
const showTitle = headOverflow === 'title';
const showTooltip = headOverflow === true || headOverflow === 'tooltip';
let hasEllipsis = showTitle || showTooltip || showEllipsis;
const hasFilter = column.filters && column.filters.some((item) => 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: renderType, isHidden: fixedHiddenColumn, hasFilter };
const thOns = {
onClick: (evnt) => $xeTable.triggerHeaderCellClickEvent(evnt, params),
onDblclick: (evnt) => $xeTable.triggerHeaderCellDblclickEvent(evnt, params)
};
// 横向虚拟滚动不支持动态行高
if (scrollXLoad && !hasEllipsis) {
showEllipsis = hasEllipsis = true;
}
// 按下事件处理
if (mouseConfig) {
thOns.onMousedown = (evnt) => $xeTable.triggerHeaderCellMousedownEvent(evnt, params);
}
return h('th', Object.assign(Object.assign({ class: ['vxe-header--column', colid, {
[`col--${headAlign}`]: headAlign,
[`col--${type}`]: type,
'col--last': $columnIndex === cols.length - 1,
'col--fixed': column.fixed,
'col--group': isColGroup,
'col--ellipsis': hasEllipsis,
'fixed--hidden': fixedHiddenColumn,
'is--sortable': column.sortable,
'col--filter': !!column.filters,
'is--filter-active': hasFilter,
'col--current': currentColumn === column
},
headerClassName ? (XEUtils.isFunction(headerClassName) ? headerClassName(params) : headerClassName) : '',
headerCellClassName ? (XEUtils.isFunction(headerCellClassName) ? headerCellClassName(params) : headerCellClassName) : ''
], colid, colspan: column.colSpan > 1 ? column.colSpan : null, rowspan: column.rowSpan > 1 ? column.rowSpan : null, style: headerCellStyle ? (XEUtils.isFunction(headerCellStyle) ? headerCellStyle(params) : headerCellStyle) : null }, thOns), { key: columnKey || columnOpts.useKey || isColGroup ? colid : $columnIndex }), [
h('div', {
class: ['vxe-cell', {
'c--title': showTitle,
'c--tooltip': showTooltip,
'c--ellipsis': showEllipsis
}]
}, column.renderHeader(params)),
/**
* 列宽拖动
*/
!fixedHiddenColumn && !isColGroup && (XEUtils.isBoolean(column.resizable) ? column.resizable : (columnOpts.resizable || resizable))
? h('div', {
class: ['vxe-resizable', {
'is--line': !border || border === 'none'
}],
onMousedown: (evnt) => resizeMousedown(evnt, params)
})
: null
]);
}).concat(scrollbarWidth
? [
h('th', {
class: 'vxe-header--gutter col--gutter'
})
]
: []));
}))
]),
/**
* 其他
*/
h('div', {
ref: refHeaderBorderRepair,
class: 'vxe-table--header-border-line'
})
]);
};
return renderVN;
}
});