@blueking/vxe-table
Version:
一个基于 vue 的 PC 端表格组件,支持增删改查、虚拟树、列拖拽,懒加载、快捷菜单、数据校验、树形结构、打印、导入导出、自定义模板、渲染器、JSON 配置式...
434 lines (433 loc) • 15.9 kB
JavaScript
"use strict";
var _xeUtils = _interopRequireDefault(require("xe-utils"));
var _ui = require("../../../ui");
var _util = require("../../src/util");
var _dom = require("../../../ui/src/dom");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const {
hooks
} = _ui.VxeUI;
function getTargetOffset(target, container) {
let offsetTop = 0;
let offsetLeft = 0;
const triggerCheckboxLabel = !_dom.browse.firefox && (0, _dom.hasClass)(target, 'vxe-checkbox--label');
if (triggerCheckboxLabel) {
const checkboxLabelStyle = getComputedStyle(target);
offsetTop -= _xeUtils.default.toNumber(checkboxLabelStyle.paddingTop);
offsetLeft -= _xeUtils.default.toNumber(checkboxLabelStyle.paddingLeft);
}
while (target && target !== container) {
offsetTop += target.offsetTop;
offsetLeft += target.offsetLeft;
target = target.offsetParent;
if (triggerCheckboxLabel) {
const checkboxStyle = getComputedStyle(target);
offsetTop -= _xeUtils.default.toNumber(checkboxStyle.paddingTop);
offsetLeft -= _xeUtils.default.toNumber(checkboxStyle.paddingLeft);
}
}
return {
offsetTop,
offsetLeft
};
}
hooks.add('tableKeyboardModule', {
setupTable($xeTable) {
const {
props,
reactData,
internalData
} = $xeTable;
const {
refElem
} = $xeTable.getRefMaps();
const {
computeEditOpts,
computeCheckboxOpts,
computeMouseOpts,
computeTreeOpts
} = $xeTable.getComputeMaps();
function getCheckboxRangeRows(evnt, params, targetTrElem, trRect, offsetClientTop, moveRange) {
let countHeight = 0;
let rangeRows = [];
let moveSize = 0;
const isDown = moveRange > 0;
const {
scrollYLoad,
rowHeight
} = reactData;
const {
afterFullData
} = internalData;
if (scrollYLoad) {
if (isDown) {
moveSize = offsetClientTop + moveRange;
} else {
moveSize = trRect.height - offsetClientTop + Math.abs(moveRange);
}
const _rowIndex = $xeTable.getVTRowIndex(params.row);
if (isDown) {
rangeRows = afterFullData.slice(_rowIndex, _rowIndex + Math.ceil(moveSize / rowHeight));
} else {
rangeRows = afterFullData.slice(_rowIndex - Math.floor(moveSize / rowHeight), _rowIndex + 1);
}
} else {
if (isDown) {
moveSize = evnt.clientY - trRect.y;
} else {
moveSize = trRect.y - evnt.clientY + trRect.height;
}
const siblingProp = isDown ? 'next' : 'previous';
while (targetTrElem && countHeight < moveSize) {
const rowNodeRest = $xeTable.getRowNode(targetTrElem);
if (rowNodeRest) {
rangeRows.push(rowNodeRest.item);
countHeight += targetTrElem.offsetHeight;
targetTrElem = targetTrElem[`${siblingProp}ElementSibling`];
}
}
}
return rangeRows;
}
const handleCheckboxRangeEvent = (evnt, params) => {
const {
column,
cell
} = params;
if (column.type === 'checkbox') {
const el = refElem.value;
const {
elemStore
} = internalData;
const disX = evnt.clientX;
const disY = evnt.clientY;
const bodyWrapperElem = (0, _util.getRefElem)(elemStore[`${column.fixed || 'main'}-body-wrapper`] || elemStore['main-body-wrapper']);
if (!bodyWrapperElem) {
return;
}
const checkboxRangeElem = bodyWrapperElem.querySelector('.vxe-table--checkbox-range');
const domMousemove = document.onmousemove;
const domMouseup = document.onmouseup;
const trElem = cell.parentElement;
const selectRecords = $xeTable.getCheckboxRecords();
let lastRangeRows = [];
const marginSize = 1;
const offsetRest = getTargetOffset(evnt.target, bodyWrapperElem);
const startTop = offsetRest.offsetTop + evnt.offsetY;
const startLeft = offsetRest.offsetLeft + evnt.offsetX;
const startScrollTop = bodyWrapperElem.scrollTop;
const rowHeight = trElem.offsetHeight;
const trRect = trElem.getBoundingClientRect();
const offsetClientTop = disY - trRect.y;
let mouseScrollTimeout = null;
let isMouseScrollDown = false;
let mouseScrollSpaceSize = 1;
const triggerEvent = (type, evnt) => {
$xeTable.dispatchEvent(`checkbox-range-${type}`, {
records: $xeTable.getCheckboxRecords(),
reserves: $xeTable.getCheckboxReserveRecords()
}, evnt);
};
const handleChecked = evnt => {
const {
clientX,
clientY
} = evnt;
const offsetLeft = clientX - disX;
const offsetTop = clientY - disY + (bodyWrapperElem.scrollTop - startScrollTop);
let rangeHeight = Math.abs(offsetTop);
let rangeWidth = Math.abs(offsetLeft);
let rangeTop = startTop;
let rangeLeft = startLeft;
if (offsetTop < marginSize) {
// 向上
rangeTop += offsetTop;
if (rangeTop < marginSize) {
rangeTop = marginSize;
rangeHeight = startTop;
}
} else {
// 向下
rangeHeight = Math.min(rangeHeight, bodyWrapperElem.scrollHeight - startTop - marginSize);
}
if (offsetLeft < marginSize) {
// 向左
rangeLeft += offsetLeft;
if (rangeWidth > startLeft) {
rangeLeft = marginSize;
rangeWidth = startLeft;
}
} else {
// 向右
rangeWidth = Math.min(rangeWidth, bodyWrapperElem.clientWidth - startLeft - marginSize);
}
checkboxRangeElem.style.height = `${rangeHeight}px`;
checkboxRangeElem.style.width = `${rangeWidth}px`;
checkboxRangeElem.style.left = `${rangeLeft}px`;
checkboxRangeElem.style.top = `${rangeTop}px`;
checkboxRangeElem.style.display = 'block';
const rangeRows = getCheckboxRangeRows(evnt, params, trElem, trRect, offsetClientTop, offsetTop < marginSize ? -rangeHeight : rangeHeight);
// 至少滑动 10px 才能有效匹配
if (rangeHeight > 10 && rangeRows.length !== lastRangeRows.length) {
lastRangeRows = rangeRows;
if (evnt.ctrlKey) {
rangeRows.forEach(row => {
$xeTable.handleBatchSelectRows([row], selectRecords.indexOf(row) === -1);
});
} else {
$xeTable.setAllCheckboxRow(false);
$xeTable.handleCheckedCheckboxRow(rangeRows, true, false);
}
triggerEvent('change', evnt);
}
};
// 停止鼠标滚动
const stopMouseScroll = () => {
clearTimeout(mouseScrollTimeout);
mouseScrollTimeout = null;
};
// 开始鼠标滚动
const startMouseScroll = evnt => {
stopMouseScroll();
mouseScrollTimeout = setTimeout(() => {
if (mouseScrollTimeout) {
const {
scrollLeft,
scrollTop,
clientHeight,
scrollHeight
} = bodyWrapperElem;
const topSize = Math.ceil(mouseScrollSpaceSize * 50 / rowHeight);
if (isMouseScrollDown) {
if (scrollTop + clientHeight < scrollHeight) {
$xeTable.scrollTo(scrollLeft, scrollTop + topSize);
startMouseScroll(evnt);
handleChecked(evnt);
} else {
stopMouseScroll();
}
} else {
if (scrollTop) {
$xeTable.scrollTo(scrollLeft, scrollTop - topSize);
startMouseScroll(evnt);
handleChecked(evnt);
} else {
stopMouseScroll();
}
}
}
}, 50);
};
(0, _dom.addClass)(el, 'drag--range');
document.onmousemove = evnt => {
evnt.preventDefault();
evnt.stopPropagation();
const {
clientY
} = evnt;
const {
boundingTop
} = (0, _dom.getAbsolutePos)(bodyWrapperElem);
// 如果超过可视区,触发滚动
if (clientY < boundingTop) {
isMouseScrollDown = false;
mouseScrollSpaceSize = boundingTop - clientY;
if (!mouseScrollTimeout) {
startMouseScroll(evnt);
}
} else if (clientY > boundingTop + bodyWrapperElem.clientHeight) {
isMouseScrollDown = true;
mouseScrollSpaceSize = clientY - boundingTop - bodyWrapperElem.clientHeight;
if (!mouseScrollTimeout) {
startMouseScroll(evnt);
}
} else if (mouseScrollTimeout) {
stopMouseScroll();
}
handleChecked(evnt);
};
document.onmouseup = evnt => {
stopMouseScroll();
(0, _dom.removeClass)(el, 'drag--range');
checkboxRangeElem.removeAttribute('style');
document.onmousemove = domMousemove;
document.onmouseup = domMouseup;
triggerEvent('end', evnt);
};
triggerEvent('start', evnt);
}
};
const handleCellMousedownEvent = (evnt, params) => {
const {
editConfig,
checkboxConfig,
mouseConfig
} = props;
const checkboxOpts = computeCheckboxOpts.value;
const mouseOpts = computeMouseOpts.value;
const editOpts = computeEditOpts.value;
if (mouseConfig && mouseOpts.area && $xeTable.handleMousedownCellAreaEvent) {
return $xeTable.handleMousedownCellAreaEvent(evnt, params);
} else {
if (checkboxConfig && checkboxOpts.range) {
handleCheckboxRangeEvent(evnt, params);
}
if (mouseConfig && mouseOpts.selected) {
if (!editConfig || editOpts.mode === 'cell') {
$xeTable.handleSelected(params, evnt);
}
}
}
};
const keyboardMethods = {
// 处理 Tab 键移动
moveTabSelected(args, isLeft, evnt) {
const {
editConfig
} = props;
const {
afterFullData,
visibleColumn
} = internalData;
const editOpts = computeEditOpts.value;
let targetRow;
let targetRowIndex;
let targetColumnIndex;
const params = Object.assign({}, args);
const _rowIndex = $xeTable.getVTRowIndex(params.row);
const _columnIndex = $xeTable.getVTColumnIndex(params.column);
evnt.preventDefault();
if (isLeft) {
// 向左
if (_columnIndex <= 0) {
// 如果已经是第一列,则移动到上一行
if (_rowIndex > 0) {
targetRowIndex = _rowIndex - 1;
targetRow = afterFullData[targetRowIndex];
targetColumnIndex = visibleColumn.length - 1;
}
} else {
targetColumnIndex = _columnIndex - 1;
}
} else {
if (_columnIndex >= visibleColumn.length - 1) {
// 如果已经是第一列,则移动到上一行
if (_rowIndex < afterFullData.length - 1) {
targetRowIndex = _rowIndex + 1;
targetRow = afterFullData[targetRowIndex];
targetColumnIndex = 0;
}
} else {
targetColumnIndex = _columnIndex + 1;
}
}
const targetColumn = visibleColumn[targetColumnIndex];
if (targetColumn) {
if (targetRow) {
params.rowIndex = targetRowIndex;
params.row = targetRow;
} else {
params.rowIndex = _rowIndex;
}
params.columnIndex = targetColumnIndex;
params.column = targetColumn;
params.cell = $xeTable.getCellElement(params.row, params.column);
if (editConfig) {
if (editOpts.trigger === 'click' || editOpts.trigger === 'dblclick') {
if (editOpts.mode === 'row') {
$xeTable.handleEdit(params, evnt);
} else {
$xeTable.scrollToRow(params.row, params.column).then(() => $xeTable.handleSelected(params, evnt));
}
}
} else {
$xeTable.scrollToRow(params.row, params.column).then(() => $xeTable.handleSelected(params, evnt));
}
}
},
// 处理当前行方向键移动
moveCurrentRow(isUpArrow, isDwArrow, evnt) {
const {
treeConfig
} = props;
const {
currentRow
} = reactData;
const {
afterFullData
} = internalData;
const treeOpts = computeTreeOpts.value;
const childrenField = treeOpts.children || treeOpts.childrenField;
let targetRow;
evnt.preventDefault();
if (currentRow) {
if (treeConfig) {
const {
index,
items
} = _xeUtils.default.findTree(afterFullData, item => item === currentRow, {
children: childrenField
});
if (isUpArrow && index > 0) {
targetRow = items[index - 1];
} else if (isDwArrow && index < items.length - 1) {
targetRow = items[index + 1];
}
} else {
const _rowIndex = $xeTable.getVTRowIndex(currentRow);
if (isUpArrow && _rowIndex > 0) {
targetRow = afterFullData[_rowIndex - 1];
} else if (isDwArrow && _rowIndex < afterFullData.length - 1) {
targetRow = afterFullData[_rowIndex + 1];
}
}
} else {
targetRow = afterFullData[0];
}
if (targetRow) {
const params = {
$table: $xeTable,
row: targetRow,
rowIndex: $xeTable.getRowIndex(targetRow),
$rowIndex: $xeTable.getVMRowIndex(targetRow)
};
$xeTable.scrollToRow(targetRow).then(() => $xeTable.triggerCurrentRowEvent(evnt, params));
}
},
// 处理可编辑方向键移动
moveSelected(args, isLeftArrow, isUpArrow, isRightArrow, isDwArrow, evnt) {
const {
afterFullData,
visibleColumn
} = internalData;
const params = Object.assign({}, args);
const _rowIndex = $xeTable.getVTRowIndex(params.row);
const _columnIndex = $xeTable.getVTColumnIndex(params.column);
evnt.preventDefault();
if (isUpArrow && _rowIndex > 0) {
// 移动到上一行
params.rowIndex = _rowIndex - 1;
params.row = afterFullData[params.rowIndex];
} else if (isDwArrow && _rowIndex < afterFullData.length - 1) {
// 移动到下一行
params.rowIndex = _rowIndex + 1;
params.row = afterFullData[params.rowIndex];
} else if (isLeftArrow && _columnIndex) {
// 移动到左侧单元格
params.columnIndex = _columnIndex - 1;
params.column = visibleColumn[params.columnIndex];
} else if (isRightArrow && _columnIndex < visibleColumn.length - 1) {
// 移动到右侧单元格
params.columnIndex = _columnIndex + 1;
params.column = visibleColumn[params.columnIndex];
}
$xeTable.scrollToRow(params.row, params.column).then(() => {
params.cell = $xeTable.getCellElement(params.row, params.column);
$xeTable.handleSelected(params, evnt);
});
},
handleCellMousedownEvent
};
return keyboardMethods;
}
});