From ff076f2ee38ad387587498c694c813f9124a718c Mon Sep 17 00:00:00 2001 From: roymondchen Date: Wed, 4 Dec 2024 17:30:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E6=96=B0=E5=A2=9E=E8=B0=83?= =?UTF-8?q?=E8=AF=95api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/App.ts | 5 +- packages/core/src/DevtoolApi.ts | 118 ++++++++++++++++++ packages/core/src/Env.ts | 1 + packages/core/src/Node.ts | 70 +++++------ packages/core/src/index.ts | 3 + packages/data-source/src/DataSourceManager.ts | 2 +- packages/utils/src/index.ts | 2 +- runtime/vue3/page/App.vue | 30 ++++- 8 files changed, 189 insertions(+), 42 deletions(-) create mode 100644 packages/core/src/DevtoolApi.ts diff --git a/packages/core/src/App.ts b/packages/core/src/App.ts index 4be1eb104..60f3ade7f 100644 --- a/packages/core/src/App.ts +++ b/packages/core/src/App.ts @@ -48,6 +48,7 @@ export interface AppOptionsConfig { } class App extends EventEmitter { + [x: string]: any; public env: Env = new Env(); public dsl?: MApp; public codeDsl?: CodeBlockDSL; @@ -230,7 +231,7 @@ class App extends EventEmitter { * @param eventConfig 代码动作的配置 * @returns void */ - public async runCode(codeId: Id, params: Record, args: any[], flowState: FlowState) { + public async runCode(codeId: Id, params: Record, args: any[], flowState?: FlowState) { if (!codeId || isEmpty(this.codeDsl)) return; const content = this.codeDsl?.[codeId]?.content; if (typeof content === 'function') { @@ -243,7 +244,7 @@ class App extends EventEmitter { methodName: string, params: Record, args: any[], - flowState: FlowState, + flowState?: FlowState, ) { if (!dsId || !methodName) return; diff --git a/packages/core/src/DevtoolApi.ts b/packages/core/src/DevtoolApi.ts new file mode 100644 index 000000000..9abed2b8e --- /dev/null +++ b/packages/core/src/DevtoolApi.ts @@ -0,0 +1,118 @@ +import { cloneDeep } from 'lodash-es'; + +import { compiledNodeField } from '@tmagic/data-source'; +import type { DisplayCondItem, EventConfig, Id } from '@tmagic/schema'; +import { NODE_CONDS_KEY } from '@tmagic/schema'; +import { isValueIncludeDataSource, setValueByKeyPath } from '@tmagic/utils'; + +import TMagicApp from './App'; + +export default class DevToolApi { + app: TMagicApp; + + constructor({ app }: { app: TMagicApp }) { + this.app = app; + } + + public openPop(popId: Id) { + if (typeof this.app.openPop === 'function') { + return this.app.openPop(popId); + } + } + + public setDataSourceData(dsId: string, data: any, path?: string) { + const ds = this.app.dataSourceManager?.get(dsId); + + if (!ds) { + return; + } + + ds.setData(data, path); + } + + public delDataSourceData() { + return; + } + + public requestDataSource(dsId: string) { + const ds = this.app.dataSourceManager?.get(dsId); + + if (!ds) { + return; + } + + if (typeof ds.refresh === 'function') { + return ds.refresh(); + } + + if (typeof ds.request === 'function') { + return ds.request(); + } + + ds.isInit = false; + this.app.dataSourceManager?.init(ds); + } + + public getDisplayCondRealValue(_nodeId: Id, condItem: DisplayCondItem) { + return this.app.dataSourceManager?.compliedConds({ [NODE_CONDS_KEY]: [{ cond: [condItem] }] }); + } + + public async callHook(nodeId: Id, hookName: string, hookData: { params: Record }[]) { + const node = this.app.getNode(nodeId); + if (!node) { + return; + } + + for (const item of hookData) { + await node.runHookCode(hookName, item.params); + } + } + + public trigger(nodeId: Id, events: EventConfig) { + const node = this.app.getNode(nodeId); + if (!node) { + return; + } + + this.app.emit(events.name, node); + } + + public updateDsl(_nodeId: Id, _data: any, _path: string) { + return; + } + + public isValueIncludeDataSource(value: any) { + return isValueIncludeDataSource(value); + } + + public compileDataSourceValue(value: any) { + return compiledNodeField(value, this.app.dataSourceManager?.data || {}); + } + + public updateCode(codeId: string, value: any, path: string) { + if (!this.app.dsl) { + return; + } + + const { codeBlocks } = this.app.dsl; + if (!codeBlocks) { + return; + } + + const code = codeBlocks[codeId]; + + if (!code) { + return; + } + + const newCode = cloneDeep(code); + if (path === 'content' && typeof value === 'string' && (value.includes('function') || value.includes('=>'))) { + // eslint-disable-next-line no-eval + value = eval(value); + } + + setValueByKeyPath(path, value, newCode); + + codeBlocks[codeId] = newCode; + } +} diff --git a/packages/core/src/Env.ts b/packages/core/src/Env.ts index ee00a8255..2b2dea085 100644 --- a/packages/core/src/Env.ts +++ b/packages/core/src/Env.ts @@ -17,6 +17,7 @@ */ class Env { + [x: string]: any; isIos = false; isIphone = false; isIpad = false; diff --git a/packages/core/src/Node.ts b/packages/core/src/Node.ts index 3b066795e..ee1f1b706 100644 --- a/packages/core/src/Node.ts +++ b/packages/core/src/Node.ts @@ -76,39 +76,7 @@ class Node extends EventEmitter { this.eventQueue.push(event); } - public destroy() { - this.removeAllListeners(); - } - - private listenLifeSafe() { - this.once('created', async (instance: any) => { - this.once('destroy', () => { - this.instance = null; - if (typeof this.data.destroy === 'function') { - this.data.destroy(this); - } - - this.listenLifeSafe(); - }); - - this.instance = instance; - await this.runHookCode('created'); - }); - - this.once('mounted', async (instance: any) => { - this.instance = instance; - - for (let eventConfig = this.eventQueue.shift(); eventConfig; eventConfig = this.eventQueue.shift()) { - if (typeof instance[eventConfig.method] === 'function') { - await instance[eventConfig.method](eventConfig.fromCpt, ...eventConfig.args); - } - } - - await this.runHookCode('mounted'); - }); - } - - private async runHookCode(hook: string) { + public async runHookCode(hook: string, params?: Record) { if (typeof this.data[hook] === 'function') { // 兼容旧的数据格式 await this.data[hook](this); @@ -131,12 +99,12 @@ class Node extends EventEmitter { if (hookData?.hookType !== HookType.CODE) return; for (const item of hookData.hookData) { - const { codeType = HookCodeType.CODE, codeId, params = {} } = item; + const { codeType = HookCodeType.CODE, codeId, params: itemParams = {} } = item; let functionContent: ((...args: any[]) => any) | string | undefined; const functionParams: { app: TMagicApp; params: Record; dataSource?: DataSource } = { app: this.app, - params, + params: params || itemParams, }; if (codeType === HookCodeType.CODE && typeof codeId === 'string' && this.app.codeDsl?.[codeId]) { @@ -152,6 +120,38 @@ class Node extends EventEmitter { } } } + + public destroy() { + this.removeAllListeners(); + } + + private listenLifeSafe() { + this.once('created', async (instance: any) => { + this.once('destroy', () => { + this.instance = null; + if (typeof this.data.destroy === 'function') { + this.data.destroy(this); + } + + this.listenLifeSafe(); + }); + + this.instance = instance; + await this.runHookCode('created'); + }); + + this.once('mounted', async (instance: any) => { + this.instance = instance; + + for (let eventConfig = this.eventQueue.shift(); eventConfig; eventConfig = this.eventQueue.shift()) { + if (typeof instance[eventConfig.method] === 'function') { + await instance[eventConfig.method](eventConfig.fromCpt, ...eventConfig.args); + } + } + + await this.runHookCode('mounted'); + }); + } } export default Node; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index fe504f9cb..383259f82 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -18,6 +18,8 @@ import App from './App'; +export { cloneDeep } from 'lodash-es'; + export * from '@tmagic/data-source'; export * from '@tmagic/dep'; export * from '@tmagic/schema'; @@ -32,5 +34,6 @@ export { default as Page } from './Page'; export { default as Node } from './Node'; export { default as IteratorContainer } from './IteratorContainer'; export { default as FlowState } from './FlowState'; +export { default as DevtoolApi } from './DevtoolApi'; export default App; diff --git a/packages/data-source/src/DataSourceManager.ts b/packages/data-source/src/DataSourceManager.ts index 97def39e5..334936e61 100644 --- a/packages/data-source/src/DataSourceManager.ts +++ b/packages/data-source/src/DataSourceManager.ts @@ -47,7 +47,7 @@ class DataSourceManager extends EventEmitter { public app: TMagicApp; - public dataSourceMap = new Map(); + public dataSourceMap = new Map>(); public data: DataSourceManagerData = {}; public useMock?: boolean = false; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 890c08e52..447bcc03d 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -109,7 +109,7 @@ export const getNodePath = (id: Id, data: MNode[] = []): MNode[] => { return path; }; -export const getNodeInfo = (id: Id, root: MApp | null) => { +export const getNodeInfo = (id: Id, root: Pick | null) => { const info: EditorNodeInfo = { node: null, parent: null, diff --git a/runtime/vue3/page/App.vue b/runtime/vue3/page/App.vue index ec3be90e3..fe73fbd0e 100644 --- a/runtime/vue3/page/App.vue +++ b/runtime/vue3/page/App.vue @@ -3,11 +3,11 @@