mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2025-12-13 06:02:49 +00:00
186 lines
3.6 KiB
TypeScript
186 lines
3.6 KiB
TypeScript
import { h, resolveComponent, toRaw, 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 中
|
||
if (isFunction(vnode.ref)) {
|
||
setTimeout(() => {
|
||
vnode.ref(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-select":
|
||
placeholder = config.dict.label.placeholderSelect;
|
||
break;
|
||
|
||
default:
|
||
placeholder = config.dict.label.placeholder;
|
||
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) {
|
||
return parseNode(vnode, { ...options, children, ...parseExtensionComponent(vnode) });
|
||
} else {
|
||
if (options.custom) {
|
||
return options.custom(vnode);
|
||
}
|
||
|
||
return <cl-error-message title={`Error,name is required`} />;
|
||
}
|
||
}
|
||
}
|