更新配置项,更新Receiver入参,修复渲染错误
This commit is contained in:
1235
pnpm-lock.yaml
generated
1235
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
vue-{{ vueVer }}
|
vue-{{ vueVer }}
|
||||||
<Invoker url="/child.html#?id={id}" style="border:1px solid red;height: 400px;overflow-y: scroll;" :items="items" />
|
<Invoker style="border:1px solid red;height: 400px;overflow-y: scroll;" :items="items" />
|
||||||
<component :is="dom" />
|
<component :is="dom" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import * as Vue3 from "vue";
|
import * as Vue3 from "vue";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { h, isVue2, isVue3, type VNode, type VNodeChildren, type VNodeData } from "vue-demi";
|
import { ComponentInternalInstance, h, isVue2, isVue3, type VNode, type VNodeChildren, type VNodeData } from "vue-demi";
|
||||||
import { InvokerItem } from "./InvokerItem";
|
import { InvokerItem } from "./InvokerItem";
|
||||||
import { ComponentInternalInstance } from "vue-demi/lib/v2/index.js";
|
|
||||||
|
|
||||||
function splitAttrs(obj: object) : { attrs: Record<string, any>, on: Record<string, any> } {
|
function splitAttrs(obj: object) : { attrs: Record<string, any>, on: Record<string, any> } {
|
||||||
const attrs: Record<string, any> = {};
|
const attrs: Record<string, any> = {};
|
||||||
@@ -21,8 +20,8 @@ function splitAttrs(obj: object) : { attrs: Record<string, any>, on: Record<stri
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将扁平化的组件数据对象分离为Vue2形式的data对象
|
* 将扁平化的组件数据对象分离为Vue2形式的data对象
|
||||||
* @param rawData
|
* @param rawData
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function splitVue2Data(rawData?: Record<string, any> | null) {
|
function splitVue2Data(rawData?: Record<string, any> | null) {
|
||||||
console.warn("Handling Vue2 data: ", rawData);
|
console.warn("Handling Vue2 data: ", rawData);
|
||||||
@@ -54,11 +53,11 @@ function splitVue2Data(rawData?: Record<string, any> | null) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 渲染具名插槽
|
* 渲染具名插槽
|
||||||
* @param slotFn
|
* @param slotFn
|
||||||
* @param args
|
* @param args
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function renderSlot(slotFn: Function, args: any, ctx?: ComponentInternalInstance) {
|
function renderSlot(slotFn: Function, args: any, renderFunction?: Function) {
|
||||||
let child: string | Array<InvokerItem> | InvokerItem = slotFn(args);
|
let child: string | Array<InvokerItem> | InvokerItem = slotFn(args);
|
||||||
// console.warn("Rendering Slot: ", child);
|
// console.warn("Rendering Slot: ", child);
|
||||||
// 字符串情况
|
// 字符串情况
|
||||||
@@ -67,17 +66,18 @@ function renderSlot(slotFn: Function, args: any, ctx?: ComponentInternalInstance
|
|||||||
}
|
}
|
||||||
// Array<InvokerItem>情况
|
// Array<InvokerItem>情况
|
||||||
else if (Array.isArray(child)) {
|
else if (Array.isArray(child)) {
|
||||||
return child.map((c: InvokerItem) => hh(c.tag, c.data, c.children, ctx));
|
return child.map((c: InvokerItem) => hh(c.tag, c.data, c.children, renderFunction));
|
||||||
}
|
}
|
||||||
// InvokerItem情况
|
// InvokerItem情况
|
||||||
else {
|
else {
|
||||||
return hh(child.tag, child.data, child.children, ctx);
|
return hh(child.tag, child.data, child.children, renderFunction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hh(tag: string, data?: Record<string, any> | null, children?: string | Array<InvokerItem> | Record<string, Function>, ctx?: ComponentInternalInstance) {
|
export function hh(tag: string | object, data?: Record<string, any> | null, children?: string | Array<InvokerItem> | Record<string, Function>, renderFunction?: Function) {
|
||||||
// 适配Vue2渲染函数结构
|
// 适配Vue2渲染函数结构
|
||||||
// console.debug("Rendering", tag, data, children, `Vue Version: ${isVue2 ? 'Vue2' : 'Vue3'}`);
|
// console.debug("Rendering", tag, data, children, `Vue Version: ${isVue2 ? 'Vue2' : 'Vue3'}`);
|
||||||
|
const render = renderFunction || h;
|
||||||
|
|
||||||
// 处理tag
|
// 处理tag
|
||||||
let processedTag: string | object = tag;
|
let processedTag: string | object = tag;
|
||||||
@@ -103,7 +103,8 @@ export function hh(tag: string, data?: Record<string, any> | null, children?: st
|
|||||||
}
|
}
|
||||||
// 子节点列表则递归处理
|
// 子节点列表则递归处理
|
||||||
else if (Array.isArray(children)) {
|
else if (Array.isArray(children)) {
|
||||||
processedChildren = children.map(child => hh(child.tag, child.data, child.children, ctx));
|
processedChildren = children.map(child => hh(child.tag, child.data, child.children, renderFunction));
|
||||||
|
console.warn("Processed Children: ", processedChildren);
|
||||||
}
|
}
|
||||||
// 处理具名插槽对象
|
// 处理具名插槽对象
|
||||||
else if (children && typeof children == 'object') {
|
else if (children && typeof children == 'object') {
|
||||||
@@ -111,7 +112,7 @@ export function hh(tag: string, data?: Record<string, any> | null, children?: st
|
|||||||
processedChildren = Object.fromEntries(
|
processedChildren = Object.fromEntries(
|
||||||
Object.entries(children).map(([name, slotFn]) => [
|
Object.entries(children).map(([name, slotFn]) => [
|
||||||
name,
|
name,
|
||||||
(args) => renderSlot(slotFn, args, ctx)
|
(args) => renderSlot(slotFn, args, renderFunction)
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -121,13 +122,15 @@ export function hh(tag: string, data?: Record<string, any> | null, children?: st
|
|||||||
Object.entries(children).map(([name, slotFn]) => [
|
Object.entries(children).map(([name, slotFn]) => [
|
||||||
name,
|
name,
|
||||||
(args) => {
|
(args) => {
|
||||||
return renderSlot(slotFn, args);
|
return renderSlot(slotFn, args, renderFunction);
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug("Processed", processedTag, processedData, processedChildren);
|
console.debug("Processed", {
|
||||||
return h(processedTag, processedData, processedChildren);
|
processedTag, processedData, processedChildren
|
||||||
}
|
});
|
||||||
|
return render(processedTag, processedData, processedChildren);
|
||||||
|
}
|
||||||
|
@@ -1,12 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
onBeforeUpdate,
|
onMounted,
|
||||||
onUnmounted,
|
onUnmounted,
|
||||||
|
ref,
|
||||||
watch,
|
watch,
|
||||||
type PropType
|
type PropType
|
||||||
} from "vue-demi";
|
} from "vue-demi";
|
||||||
import { ModContext } from "../types/ModContext";
|
import { ModContext } from "../types/ModContext";
|
||||||
import { InvokerItem } from "./InvokerItem";
|
import { InvokerItem } from "./InvokerItem";
|
||||||
|
import { modPageConfig } from "../modPageConfig";
|
||||||
|
|
||||||
let idCount = 0;
|
let idCount = 0;
|
||||||
export type InvokerModName = string;
|
export type InvokerModName = string;
|
||||||
@@ -37,9 +39,13 @@ export default defineComponent({
|
|||||||
const id = ++idCount;
|
const id = ++idCount;
|
||||||
/** vue实例上下文,此处获取的是Receiver的vue组件实例 */
|
/** vue实例上下文,此处获取的是Receiver的vue组件实例 */
|
||||||
let receiver: ModContext | null = null;
|
let receiver: ModContext | null = null;
|
||||||
|
const invokerKey = modPageConfig.Invoker.invokerKey;
|
||||||
|
const url = ref<string | undefined>('');
|
||||||
|
|
||||||
window["MES_MOD_INVOKERS"] ||= {};
|
console.log(`[Mod-Invoker] setting up invoker, id: ${id}, name: ${props.name}, invokerKey: ${invokerKey}`);
|
||||||
window["MES_MOD_INVOKERS"][id] = {
|
|
||||||
|
window[invokerKey] ||= {};
|
||||||
|
window[invokerKey][id] = {
|
||||||
getRenderContext,
|
getRenderContext,
|
||||||
initFinish,
|
initFinish,
|
||||||
receiver,
|
receiver,
|
||||||
@@ -64,7 +70,7 @@ export default defineComponent({
|
|||||||
/** 当Receiver初始化完毕后触发 */
|
/** 当Receiver初始化完毕后触发 */
|
||||||
function initFinish(context: ModContext) {
|
function initFinish(context: ModContext) {
|
||||||
receiver = context;
|
receiver = context;
|
||||||
console.log('[Mod-Invoker]initFinish')
|
console.log('[Mod-Invoker] initFinish')
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRefs() {
|
function getRefs() {
|
||||||
@@ -92,11 +98,11 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
onBeforeUpdate(() => {
|
onMounted(async () => {
|
||||||
|
url.value = await modPageConfig.Invoker.GetModUrl?.(props.name ?? '', id.toString(), invokerKey);
|
||||||
|
})
|
||||||
|
|
||||||
});
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
console.log("[Mod-Invoker] invoker-destroyed");
|
|
||||||
emit('destroyed');
|
emit('destroyed');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -107,7 +113,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<iframe
|
<iframe
|
||||||
src={props.url.replace('{id}',id.toString())}
|
src={url.value}
|
||||||
scrolling={props.scroll ? "yes" : "no"}
|
scrolling={props.scroll ? "yes" : "no"}
|
||||||
style={`border: none; width: 100%; height: ${props.height}; `}
|
style={`border: none; width: 100%; height: ${props.height}; `}
|
||||||
></iframe>
|
></iframe>
|
||||||
|
@@ -1,94 +1,7 @@
|
|||||||
import { defineComponent, nextTick, onMounted, ref, getCurrentInstance, isVue3 } from 'vue-demi';
|
import { defineComponent, nextTick, onMounted, ref, getCurrentInstance, isVue3, h } from 'vue-demi';
|
||||||
import { hh } from './DemiHelper';
|
import { hh } from './DemiHelper';
|
||||||
import { InvokerContext } from '../types/InvokerContext';
|
import { InvokerContext } from '../types/InvokerContext';
|
||||||
|
|
||||||
console.log('receiver-loaded');
|
|
||||||
|
|
||||||
|
|
||||||
function renderContext(item: object | Array<any>, toSlot: boolean = false) {
|
|
||||||
/**
|
|
||||||
* 对象为字符串的情况
|
|
||||||
* # { tag: 'div', attrs: undefined, children: 'hhh' } => h('span', undefined, 'hhh')
|
|
||||||
*/
|
|
||||||
if (typeof item === 'string')
|
|
||||||
return item;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象为函数的情况
|
|
||||||
* # { tag: 'div', attrs: undefined, children: () => 'hhh' } => h('span', undefined, 'hhh')
|
|
||||||
*/
|
|
||||||
if (typeof (item) === "function") {
|
|
||||||
|
|
||||||
// return item;
|
|
||||||
return item();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象为数组的情况
|
|
||||||
* # { tag: 'div', attrs: undefined, [ { tag: 'span', attrs: undefined, 'hhh' } ] } => h('div', undefined, [ h('span', undefined, 'hhh') ])
|
|
||||||
*/
|
|
||||||
if (Array.isArray(item)) {
|
|
||||||
|
|
||||||
const list = item.map(i => {
|
|
||||||
if (isVue3) {
|
|
||||||
// const comp = resolveComponent(i.tag);
|
|
||||||
return hh(i.tag, i.attrs, i.children);
|
|
||||||
// return hDemi(comp, i.attrs, renderContext(i.children, typeof (comp) !== 'string') as any); // vue3
|
|
||||||
}
|
|
||||||
|
|
||||||
// const { attrs, listeners, ref } = splitAttrs(i.attrs);
|
|
||||||
const slots = renderContext(i.children);
|
|
||||||
if (typeof (slots) === "object") {
|
|
||||||
return hh(i.tag, {
|
|
||||||
attrs: i.attrs
|
|
||||||
// attrs,
|
|
||||||
// on: listeners,
|
|
||||||
// scopedSlots: slots,
|
|
||||||
// ref
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return hh(i.tag, {
|
|
||||||
attrs: i.attrs,
|
|
||||||
}, slots);
|
|
||||||
|
|
||||||
});
|
|
||||||
if (toSlot) {
|
|
||||||
return () => list;
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 复合对象的情况
|
|
||||||
* #
|
|
||||||
*/
|
|
||||||
const children: Record<string, any> = {};
|
|
||||||
for (const key in item) {
|
|
||||||
|
|
||||||
children[key] = function (scope) {
|
|
||||||
const child = item[key](scope);
|
|
||||||
if (isVue3) {
|
|
||||||
return hh(child.tag, child.attrs, renderContext(child.children, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(child)) {
|
|
||||||
const slots = renderContext(child);
|
|
||||||
return slots;
|
|
||||||
} else {
|
|
||||||
// const { attrs, listeners, ref } = splitAttrs(child.attrs);
|
|
||||||
const slots = renderContext(child.children);
|
|
||||||
return hh(child.tag, {
|
|
||||||
attrs: child.attrs
|
|
||||||
// attrs,
|
|
||||||
// on: listeners,
|
|
||||||
// ref
|
|
||||||
}, slots);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Receiver',
|
name: 'Receiver',
|
||||||
props: {
|
props: {
|
||||||
@@ -98,13 +11,14 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
parentKey: {
|
parentKey: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => 'modInvoker'
|
default: () => 'MES_MOD_INVOKERS'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const instance = getCurrentInstance();
|
const instance = getCurrentInstance();
|
||||||
let invokerContext: InvokerContext = null!;
|
let invokerContext: InvokerContext = null!;
|
||||||
const renderVersion = ref(0);
|
const renderVersion = ref(0);
|
||||||
|
console.log(`[Mod-Receiver] receiver setup, invokerId: ${props.parentId}, invokerKey: ${props.parentKey}`);
|
||||||
if (window.parent != window) {
|
if (window.parent != window) {
|
||||||
const invoker = window.parent[props.parentKey][props.parentId];
|
const invoker = window.parent[props.parentKey][props.parentId];
|
||||||
invokerContext = invoker;
|
invokerContext = invoker;
|
||||||
@@ -112,7 +26,7 @@ export default defineComponent({
|
|||||||
else {
|
else {
|
||||||
throw new Error("[Receiver] 组件必须在iframe中使用");
|
throw new Error("[Receiver] 组件必须在iframe中使用");
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderItems = ref(invokerContext.getRenderContext() ?? []);
|
const renderItems = ref(invokerContext.getRenderContext() ?? []);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -124,11 +38,12 @@ export default defineComponent({
|
|||||||
return renderVersion.value;
|
return renderVersion.value;
|
||||||
},
|
},
|
||||||
Update: function (): void {
|
Update: function (): void {
|
||||||
console.log('[Receiver] Force Update', renderVersion.value);
|
console.log('[Mod-Receiver] force update', renderVersion.value);
|
||||||
renderVersion.value += 1;
|
renderVersion.value += 1;
|
||||||
renderItems.value = invokerContext.getRenderContext() ?? [];
|
renderItems.value = invokerContext.getRenderContext() ?? [];
|
||||||
instance?.proxy?.$forceUpdate();
|
instance?.proxy?.$forceUpdate();
|
||||||
},
|
},
|
||||||
|
// @ts-ignore
|
||||||
get refs() {
|
get refs() {
|
||||||
return instance?.proxy?.$refs;
|
return instance?.proxy?.$refs;
|
||||||
}
|
}
|
||||||
@@ -136,21 +51,22 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return () => {
|
return (h) => {
|
||||||
try {
|
try {
|
||||||
console.log('receiver-update', renderVersion.value);
|
|
||||||
if (renderVersion.value < 0) return;
|
if (renderVersion.value < 0) return;
|
||||||
if (invokerContext.getRenderContext) {
|
if (invokerContext.getRenderContext) {
|
||||||
const itemList = invokerContext.getRenderContext() ?? [];
|
const itemList = invokerContext.getRenderContext() ?? [];
|
||||||
|
|
||||||
// const list = renderContext(itemList);
|
// const list = renderContext(itemList);
|
||||||
// console.log('item-list', list);
|
console.log("[Mod-Receiver] rendering: ", itemList);
|
||||||
return hh('div', undefined, itemList);
|
// @ts-ignore
|
||||||
|
return hh('div', undefined, itemList, h);
|
||||||
}
|
}
|
||||||
return hh('div');
|
console.log("[Mod-Receiver] 'getRenderContext' method not found");
|
||||||
|
return hh('div', undefined, undefined, h);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.error("[Receiver] 渲染节点过程中出现错误", e);
|
console.error("[Mod-Receiver] 渲染节点过程中出现错误", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
14
src/main.ts
14
src/main.ts
@@ -1,5 +1,19 @@
|
|||||||
import { createApp, Vue2 } from 'vue-demi'
|
import { createApp, Vue2 } from 'vue-demi'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import { ConfigureModPage } from './modPageConfig'
|
||||||
|
|
||||||
|
ConfigureModPage({
|
||||||
|
Invoker: {
|
||||||
|
GetModUrl: (modName, invokerId, invokerKey) => {
|
||||||
|
const query: Record<string, string> = {
|
||||||
|
id: invokerId,
|
||||||
|
key: invokerKey
|
||||||
|
};
|
||||||
|
const searchParam = new URLSearchParams(query);
|
||||||
|
return `/child.html#/?${searchParam.toString()}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
@@ -3,17 +3,29 @@ import { InvokerModName } from "./components/Invoker";
|
|||||||
|
|
||||||
let _modPageConfig = {
|
let _modPageConfig = {
|
||||||
Invoker: {
|
Invoker: {
|
||||||
|
invokerKey: "MES_MOD_INVOKERS",
|
||||||
/** Invoker中计算ModUrl的方法 */
|
/** Invoker中计算ModUrl的方法 */
|
||||||
GetModUrl: undefined as ((modName: InvokerModName) => string | Promise<string>) | undefined,
|
GetModUrl: undefined as ((modName: InvokerModName, invokerId: string, invokerKey: string) => string | Promise<string>) | undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ConfigureModPage(config: typeof _modPageConfig) {
|
export function ConfigureModPage(config: DeepPartial<typeof _modPageConfig>) {
|
||||||
Object.assign(_modPageConfig, {
|
Object.assign(_modPageConfig, {
|
||||||
|
..._modPageConfig,
|
||||||
|
...config,
|
||||||
Invoker: {
|
Invoker: {
|
||||||
...config.Invoker,
|
..._modPageConfig.Invoker,
|
||||||
|
...config?.Invoker
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const modPageConfig: DeepReadonly<typeof _modPageConfig> = _modPageConfig;
|
export const modPageConfig: DeepReadonly<typeof _modPageConfig> = _modPageConfig;
|
||||||
|
|
||||||
|
type DeepPartial<T> = T extends Function // 排除Function,因为Function也是object
|
||||||
|
? T
|
||||||
|
: T extends object
|
||||||
|
? {
|
||||||
|
[P in keyof T]?: DeepPartial<T[P]>;
|
||||||
|
}
|
||||||
|
: T;
|
Reference in New Issue
Block a user