@visactor/vue-vtable
Version:
The vue version of VTable
120 lines (117 loc) • 5.49 kB
JavaScript
import * as VTable from '@visactor/vtable';
import { convertPropsToCamelCase, toCamelCase } from './stringUtils.js';
import { isObject, isNil, isFunction } from '@visactor/vutils';
import { isVNode, cloneVNode } from 'vue';
// 检查属性是否为事件
function isEventProp(key, props) {
return key.startsWith('on') && isFunction(props[key]);
}
// 创建自定义布局
function createCustomLayout(children, isHeader, args) {
// 同一 cell 内多个 vue Group 的计数器,每次 createCustomLayout 调用时归零
let vueIndex = 0;
// 组件映射
const componentMap = {
Group: VTable.CustomLayout.Group,
Image: VTable.CustomLayout.Image,
Text: VTable.CustomLayout.Text,
Tag: VTable.CustomLayout.Tag,
Radio: VTable.CustomLayout.Radio,
CheckBox: VTable.CustomLayout.CheckBox
};
// 创建组件的函数
function createComponent(child) {
var _a, _b;
if (!child) {
return null;
}
const { type, children: childChildren } = child;
const props = convertPropsToCamelCase(child.props);
const componentName = (type === null || type === void 0 ? void 0 : type.symbol) || (type === null || type === void 0 ? void 0 : type.name);
const ComponentClass = componentMap[componentName];
if (!ComponentClass) {
return null;
}
// 创建组件实例
const component = new ComponentClass(Object.assign({}, props));
// 绑定组件事件
bindComponentEvents(component, props);
// 递归创建子组件
const subChildren = resolveChildren(childChildren);
if (isObject(props === null || props === void 0 ? void 0 : props.vue)) {
// vue 自定义节点:无需继续循环子节点
const { element } = props.vue;
let targetVNode = element !== null && element !== void 0 ? element : subChildren.find(node => (node === null || node === void 0 ? void 0 : node.type) !== Symbol.for('v-cmt'));
if (isVNode(targetVNode)) {
// node 标记 key 增加唯一项标记,避免重复渲染
targetVNode = !targetVNode.key
? cloneVNode(targetVNode, { key: `row_${args.row}_col_${args.col}` })
: targetVNode;
}
else {
targetVNode = null;
}
// 注入稳定 ID:基于 cell 坐标 + 递增索引,确保场景图重建后缓存 key 不变
const stableId = isNil(props.vue.id) ? `cell_${args.col}_${args.row}_${vueIndex++}` : undefined;
Object.assign(child.props.vue, Object.assign({ element: targetVNode,
// 不接入外部指定
container: isHeader ? (_a = args === null || args === void 0 ? void 0 : args.table) === null || _a === void 0 ? void 0 : _a.headerDomContainer : (_b = args === null || args === void 0 ? void 0 : args.table) === null || _b === void 0 ? void 0 : _b.bodyDomContainer }, (stableId ? { id: stableId } : {})));
return component;
}
subChildren.forEach((subChild) => {
const subComponent = createComponent(subChild);
if (subComponent) {
component.add(subComponent);
}
else if (subChild.type === Symbol.for('v-fgt')) {
subChild.children.forEach((nestedChild) => {
const nestedComponent = createComponent(nestedChild);
if (nestedComponent) {
component.add(nestedComponent);
}
});
}
});
return component;
}
// 处理子节点
function resolveChildren(childChildren) {
var _a;
return ((_a = childChildren === null || childChildren === void 0 ? void 0 : childChildren.default) === null || _a === void 0 ? void 0 : _a.call(childChildren)) || childChildren || [];
}
// 绑定组件事件
function bindComponentEvents(component, props) {
Object.keys(props).forEach(key => {
if (isEventProp(key, props)) {
let eventName;
if (key.startsWith('on')) {
eventName = key.slice(2).toLowerCase(); // 去掉'on'前缀并转换为小写
}
else {
eventName = toCamelCase(key.slice(2)).toLowerCase(); // 转换为camelCase
}
component.addEventListener(eventName, props[key]);
}
});
}
// 返回root组件和refs
return { rootComponent: createComponent(children) };
}
function createCustomLayoutHandler(children, isHeader) {
return (args) => {
const { table, row, col, rect } = args;
const record = table.getCellOriginRecord(col, row);
const { height, width } = rect !== null && rect !== void 0 ? rect : table.getCellRect(col, row);
const customLayoutKey = isHeader ? 'headerCustomLayout' : 'customLayout';
if (!children[customLayoutKey]) {
return null;
}
const rootContainer = children[customLayoutKey]({ table, row, col, rect, record, height, width })[0];
const { rootComponent } = createCustomLayout(rootContainer, isHeader, args);
return {
rootContainer: rootComponent,
renderDefault: false
};
};
}
export { createCustomLayout, createCustomLayoutHandler };