2023-04-03 19:02:44 +08:00

187 lines
3.6 KiB
TypeScript

import { h, resolveComponent, toRaw, VNode } from "vue";
import { useGlobal } from "../hooks";
import { isFunction, isString, isObject } from "./index";
import { parseExtensionComponent } from "./parse";
import temp from "./temp";
// 配置
interface Options {
// 标识
prop?: string;
// 数据值
scope?: any;
// 当前行
item?: any;
// 插槽
slots?: 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 || [];
const global = useGlobal();
// 渲染后组件
let comp: VNode | null = null;
// 插槽模式渲染
if (vnode.name.includes("slot-")) {
const rn = slots[vnode.name];
if (rn) {
return rn({ scope, ...options._data });
} else {
return <cl-error-message title={`${vnode.name} is not found`} />;
}
}
// 实例模式下,先注册到全局,再分解组件渲染
if (vnode.vm && !regs.get(vnode.name)) {
temp.vue.component(vnode.name, { ...vnode.vm });
regs.set(vnode.name, { ...vnode.vm });
}
// 处理 props
if (isFunction(vnode.props)) {
vnode.props = vnode.props({ scope, ...options._data });
}
// 组件参数
const props = {
...vnode.props,
..._data,
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 isFunctionSlot =
!global.render.functionSlots.exclude?.includes(vnode.name) &&
(vnode.functionSlot === undefined ? true : vnode.functionSlot);
// 渲染组件
comp = h(
toRaw(resolveComponent(vnode.name)),
props,
isFunctionSlot ? () => children : children
);
}
// 挂载到 refs 中
if (isFunction(vnode.ref)) {
setTimeout(() => {
vnode.ref(comp?.component?.exposed);
}, 0);
}
return comp;
}
// 渲染节点
export function renderNode(vnode: any, options: Options) {
const { item, scope, _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 = "请填写";
break;
case "el-select":
placeholder = "请选择";
break;
default:
break;
}
if (placeholder) {
if (!item.component.props.placeholder) {
item.component.props.placeholder = placeholder + item.label;
}
}
}
}
// 组件实例
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) {
const { children } = parseExtensionComponent(vnode);
return parseNode(vnode, { ...options, children });
} else {
if (options.custom) {
return options.custom(vnode);
}
return <cl-error-message title={`组件渲染失败,组件 name 不能为空`} />;
}
}
}