import { h, resolveComponent, toRaw, type VNode } from "vue";
|
import { isObject } from "./index";
|
import { parseExtensionComponent } from "./parse";
|
import global from "./global";
|
import { useConfig } from "../hooks";
|
import { isFunction, isString } from "lodash-es";
|
|
// 配置
|
interface Options {
|
// 标识
|
prop?: string;
|
// 数据值
|
scope?: any;
|
// 当前行
|
item?: any;
|
// 插槽
|
slots?: any;
|
// 子集
|
children?: any[] & any;
|
// 自定义
|
custom?: (vnode: any) => any;
|
// 渲染方式
|
render?: "slot" | null;
|
// 其他
|
[key: string]: any;
|
}
|
|
// 临时注册组件列表
|
const regs: Map<string, any> = new Map();
|
|
// 解析节点
|
export function parseNode(vnode: any, options: Options): VNode {
|
const { scope, prop, slots, children, _data } = options || {};
|
|
// 渲染后组件
|
let comp: VNode | null = null;
|
|
// 插槽模式渲染
|
if (vnode.name?.includes("slot-")) {
|
const rn = slots[vnode.name];
|
|
if (rn) {
|
return rn({ scope, prop, ..._data });
|
} else {
|
return <cl-error-message title={`${vnode.name} is not found`} />;
|
}
|
}
|
|
// 实例模式下,先注册到全局,再分解组件渲染
|
if (vnode.vm && !regs.get(vnode.name)) {
|
global.vue.component(vnode.name, { ...vnode.vm });
|
regs.set(vnode.name, { ...vnode.vm });
|
}
|
|
// 处理 props
|
if (isFunction(vnode.props)) {
|
vnode.props = vnode.props({ scope, prop, ..._data });
|
}
|
|
// 组件参数
|
const props = {
|
...vnode.props,
|
..._data,
|
prop,
|
scope
|
};
|
|
// 是否禁用
|
props.disabled = _data?.isDisabled || props.disabled;
|
|
// 添加双向绑定
|
if (props && scope) {
|
if (prop) {
|
props.modelValue = scope[prop];
|
props["onUpdate:modelValue"] = function (val: any) {
|
scope[prop] = val;
|
};
|
}
|
}
|
|
// 组件实例渲染
|
if (vnode.vm) {
|
comp = h(regs.get(vnode.name), props);
|
} else {
|
const slots = {
|
...vnode.slots
|
};
|
|
if (children) {
|
slots.default = () => children;
|
}
|
|
// 渲染组件
|
comp = h(toRaw(resolveComponent(vnode.name)), props, slots);
|
}
|
|
// 挂载到 refs 中
|
const refBind = vnode.ref || options.ref;
|
if (isFunction(refBind)) {
|
setTimeout(() => {
|
refBind(comp?.component?.exposed);
|
}, 0);
|
}
|
|
return comp;
|
}
|
|
// 渲染节点
|
export function renderNode(vnode: any, options: Options) {
|
const config = useConfig();
|
const { item, scope, children, _data, render } = options || {};
|
|
if (!vnode) {
|
return null;
|
}
|
|
if (vnode.__v_isVNode) {
|
return vnode;
|
}
|
|
// 默认参数配置
|
if (item) {
|
if (item.component) {
|
if (!item.component.props) {
|
item.component.props = {};
|
}
|
|
// 占位符
|
let placeholder = "";
|
|
switch (item.component?.name) {
|
case "el-input":
|
placeholder = config.dict.label.placeholder;
|
break;
|
}
|
|
if (placeholder) {
|
if (!item.component.props.placeholder) {
|
item.component.props.placeholder = placeholder;
|
}
|
}
|
}
|
}
|
|
// 组件实例
|
if (vnode.vm) {
|
if (!vnode.name) {
|
vnode.name = vnode.vm?.name || vnode.vm?.__hmrId;
|
}
|
|
return parseNode(vnode, options);
|
}
|
|
// 组件名渲染
|
if (isString(vnode)) {
|
if (render == "slot") {
|
if (!vnode.includes("slot-")) {
|
return vnode;
|
}
|
}
|
|
return parseNode({ name: vnode }, options);
|
}
|
|
// 方法回调
|
if (isFunction(vnode)) {
|
return vnode({ scope, h, ..._data });
|
}
|
|
// jsx 模式
|
if (isObject(vnode)) {
|
if (vnode.name) {
|
return parseNode(vnode, { ...options, children, ...parseExtensionComponent(vnode) });
|
} else {
|
if (options.custom) {
|
return options.custom(vnode);
|
}
|
|
return <cl-error-message title="Error,component name is required" />;
|
}
|
}
|
}
|