diff --git a/packages/core/src/Node.ts b/packages/core/src/Node.ts
index ad61527c..9814a868 100644
--- a/packages/core/src/Node.ts
+++ b/packages/core/src/Node.ts
@@ -21,6 +21,7 @@ import { EventEmitter } from 'events';
import { isEmpty } from 'lodash-es';
import type { EventItemConfig, MComponent, MContainer, MPage } from '@tmagic/schema';
+import { HookType } from '@tmagic/schema';
import type App from './App';
import type Page from './Page';
@@ -85,10 +86,11 @@ class Node extends EventEmitter {
}
private async runCodeBlock(hook: string) {
- if (!Array.isArray(this.data[hook]) || !this.app.codeDsl || isEmpty(this.app?.codeDsl)) return;
- for (const codeId of this.data[hook]) {
+ if (this.data[hook]?.hookType !== HookType.CODE || !this.app.codeDsl || isEmpty(this.app?.codeDsl)) return;
+ for (const item of this.data[hook].data) {
+ const { codeId, params = {} } = item;
if (this.app.codeDsl[codeId] && typeof this.app?.codeDsl[codeId]?.content === 'function') {
- await this.app.codeDsl[codeId].content(this);
+ await this.app.codeDsl[codeId].content(this, params);
}
}
}
diff --git a/packages/editor/src/components/CodeDraftEditor.vue b/packages/editor/src/components/CodeDraftEditor.vue
index 14d67e38..5b4c22aa 100644
--- a/packages/editor/src/components/CodeDraftEditor.vue
+++ b/packages/editor/src/components/CodeDraftEditor.vue
@@ -22,6 +22,7 @@ import { computed, inject, ref, watchEffect } from 'vue';
import type * as monaco from 'monaco-editor';
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
+import { Id } from '@tmagic/schema';
import { datetimeFormatter } from '@tmagic/utils';
import MagicCodeEditor from '../layouts/CodeEditor.vue';
@@ -30,7 +31,7 @@ import type { Services } from '../type';
const props = withDefaults(
defineProps<{
/** 代码id */
- id: string;
+ id: Id;
/** 代码内容 */
content: string;
/** 是否可编辑 */
diff --git a/packages/editor/src/components/FunctionEditor.vue b/packages/editor/src/components/FunctionEditor.vue
index 9856c3d5..e32c83ff 100644
--- a/packages/editor/src/components/FunctionEditor.vue
+++ b/packages/editor/src/components/FunctionEditor.vue
@@ -23,6 +23,7 @@
import { inject, ref, watchEffect } from 'vue';
import { TMagicCard, TMagicInput, tMagicMessage } from '@tmagic/design';
+import { Id } from '@tmagic/schema';
import type { Services } from '../type';
@@ -30,7 +31,7 @@ import CodeDraftEditor from './CodeDraftEditor.vue';
const props = withDefaults(
defineProps<{
- id: string;
+ id: Id;
name: string;
content: string;
editable?: boolean;
diff --git a/packages/editor/src/fields/CodeSelect.vue b/packages/editor/src/fields/CodeSelect.vue
index 87bd197e..50765498 100644
--- a/packages/editor/src/fields/CodeSelect.vue
+++ b/packages/editor/src/fields/CodeSelect.vue
@@ -1,96 +1,131 @@
diff --git a/packages/editor/src/layouts/sidebar/code-block/CodeBlockEditor.vue b/packages/editor/src/layouts/sidebar/code-block/CodeBlockEditor.vue
index 7705f718..de905ccc 100644
--- a/packages/editor/src/layouts/sidebar/code-block/CodeBlockEditor.vue
+++ b/packages/editor/src/layouts/sidebar/code-block/CodeBlockEditor.vue
@@ -52,7 +52,7 @@
>
-
+
@@ -61,10 +61,11 @@ import { computed, inject, reactive, ref, watchEffect } from 'vue';
import { cloneDeep, forIn, isEmpty } from 'lodash-es';
import { TMagicDialog, TMagicTree } from '@tmagic/design';
+import { CodeBlockContent } from '@tmagic/schema';
import FunctionEditor from '../../../components/FunctionEditor.vue';
import Layout from '../../../components/Layout.vue';
-import type { CodeBlockContent, CodeDslList, ListState, Services } from '../../../type';
+import type { CodeDslList, ListState, Services } from '../../../type';
import { CodeEditorMode } from '../../../type';
import { serializeConfig } from '../../../utils/editor';
@@ -86,6 +87,8 @@ const editable = computed(() => services?.codeBlockService.getEditStatus());
// 当前选中组件绑定的代码块id数组
const selectedIds = computed(() => services?.codeBlockService.getCombineIds() || []);
+services?.codeBlockService.getCombineInfo();
+
watchEffect(async () => {
codeConfig.value = cloneDeep(await services?.codeBlockService.getCodeContentById(id.value)) || null;
if (!codeConfig.value) return;
diff --git a/packages/editor/src/layouts/sidebar/code-block/CodeBlockList.vue b/packages/editor/src/layouts/sidebar/code-block/CodeBlockList.vue
index 9a49fbe1..d6887d42 100644
--- a/packages/editor/src/layouts/sidebar/code-block/CodeBlockList.vue
+++ b/packages/editor/src/layouts/sidebar/code-block/CodeBlockList.vue
@@ -99,11 +99,11 @@ import { Close, Edit, Link, View } from '@element-plus/icons-vue';
import { forIn, isEmpty } from 'lodash-es';
import { TMagicButton, TMagicInput, tMagicMessage, TMagicTooltip, TMagicTree } from '@tmagic/design';
-import { Id } from '@tmagic/schema';
+import { CodeBlockContent, Id } from '@tmagic/schema';
import StageCore from '@tmagic/stage';
import Icon from '../../../components/Icon.vue';
-import type { CodeBlockContent, CodeRelation, Services } from '../../../type';
+import type { Services } from '../../../type';
import { CodeDeleteErrorType, CodeDslList, CodeEditorMode, ListRelationState } from '../../../type';
import codeBlockEditor from './CodeBlockEditor.vue';
@@ -126,15 +126,11 @@ const editable = computed(() => services?.codeBlockService.getEditStatus());
const isShowCodeBlockEditor = computed(() => services?.codeBlockService.getCodeEditorShowStatus() || false);
// 根据代码块ID获取其绑定的组件信息
-const getBindCompsByCodeId = (codeId: string, codeBlockContent: CodeBlockContent) => {
- if (isEmpty(codeBlockContent) || isEmpty(codeBlockContent.comps)) {
- state.bindComps[codeId] = [];
- return;
- }
- const compsField = codeBlockContent.comps as CodeRelation;
- const bindCompIds = Object.keys(compsField);
- const bindCompsFiltered = bindCompIds.filter((compId) => !isEmpty(compsField[compId]));
- const compsInfo = bindCompsFiltered.map((compId) => ({
+const getBindCompsByCodeId = (codeId: string) => {
+ const codeCombineInfo = services?.codeBlockService.getCombineInfo();
+ if (!codeCombineInfo) return null;
+ const bindCompsId = Object.keys(codeCombineInfo[codeId]);
+ const compsInfo = bindCompsId.map((compId) => ({
id: compId,
name: getCompName(compId),
}));
@@ -147,7 +143,7 @@ const initList = async () => {
if (!codeDsl) return;
state.codeList = [];
forIn(codeDsl, (value: CodeBlockContent, codeId: string) => {
- getBindCompsByCodeId(codeId, value);
+ getBindCompsByCodeId(codeId);
state.codeList.push({
id: codeId,
name: value.name,
diff --git a/packages/editor/src/services/codeBlock.ts b/packages/editor/src/services/codeBlock.ts
index 764bb202..af842e90 100644
--- a/packages/editor/src/services/codeBlock.ts
+++ b/packages/editor/src/services/codeBlock.ts
@@ -19,10 +19,10 @@
import { reactive } from 'vue';
import { cloneDeep, forIn, isEmpty, keys, omit, pick } from 'lodash-es';
-import { Id, MNode } from '@tmagic/schema';
+import { CodeBlockContent, CodeBlockDSL, HookType, Id, MApp, MNode } from '@tmagic/schema';
import editorService from '../services/editor';
-import type { CodeBlockContent, CodeBlockDSL, CodeState } from '../type';
+import type { CodeRelation, CodeState, HookData } from '../type';
import { CODE_DRAFT_STORAGE_KEY, CodeEditorMode, CodeSelectOp } from '../type';
import { error, info } from '../utils/logger';
@@ -37,6 +37,7 @@ class CodeBlock extends BaseService {
mode: CodeEditorMode.EDITOR,
combineIds: [],
undeletableList: [],
+ relations: {},
});
constructor() {
@@ -82,10 +83,10 @@ class CodeBlock extends BaseService {
/**
* 根据代码块id获取代码块内容
- * @param {string} id 代码块id
+ * @param {Id} id 代码块id
* @returns {CodeBlockContent | null}
*/
- public async getCodeContentById(id: string): Promise {
+ public async getCodeContentById(id: Id): Promise {
if (!id) return null;
const totalCodeDsl = await this.getCodeDsl();
if (!totalCodeDsl) return null;
@@ -94,11 +95,11 @@ class CodeBlock extends BaseService {
/**
* 设置代码块ID和代码内容到源dsl
- * @param {string} id 代码块id
+ * @param {Id} id 代码块id
* @param {CodeBlockContent} codeConfig 代码块内容配置信息
* @returns {void}
*/
- public async setCodeDslById(id: string, codeConfig: CodeBlockContent): Promise {
+ public async setCodeDslById(id: Id, codeConfig: CodeBlockContent): Promise {
let codeDsl = await this.getCodeDsl();
if (!codeDsl) {
// dsl中无代码块字段
@@ -154,10 +155,10 @@ class CodeBlock extends BaseService {
/**
* 设置代码编辑面板展示状态及展示内容
* @param {boolean} status 是否展示代码编辑面板
- * @param {string} id 代码块id
+ * @param {Id} id 代码块id
* @returns {void}
*/
- public setCodeEditorContent(status: boolean, id: string): void {
+ public setCodeEditorContent(status: boolean, id: Id): void {
if (!id) return;
this.setId(id);
this.state.isShowCodeEditor = status;
@@ -190,19 +191,19 @@ class CodeBlock extends BaseService {
/**
* 设置当前选中的代码块ID
- * @param {string} id 代码块id
+ * @param {Id} id 代码块id
* @returns {void}
*/
- public setId(id: string) {
+ public setId(id: Id) {
if (!id) return;
this.state.id = id;
}
/**
* 获取当前选中的代码块ID
- * @returns {string} id 代码块id
+ * @returns {Id} id 代码块id
*/
- public getId(): string {
+ public getId(): Id {
return this.state.id;
}
@@ -249,12 +250,12 @@ class CodeBlock extends BaseService {
* @returns {void}
*/
public async setCombineRelation(compId: Id, diffCodeIds: string[], opFlag: CodeSelectOp, hook: string) {
- const codeDsl = cloneDeep(await this.getCodeDsl());
- if (!codeDsl) return;
+ const combineInfo = this.getCombineInfo();
+ if (!combineInfo) return;
if (opFlag === CodeSelectOp.DELETE) {
try {
diffCodeIds.forEach((codeId) => {
- const compsContent = codeDsl[codeId].comps;
+ const compsContent = combineInfo[codeId];
const index = compsContent?.[compId].findIndex((item) => item === hook);
if (typeof index !== 'undefined' && index !== -1) {
compsContent?.[compId].splice(index, 1);
@@ -267,12 +268,12 @@ class CodeBlock extends BaseService {
} else if (opFlag === CodeSelectOp.ADD) {
try {
diffCodeIds.forEach((codeId) => {
- const compsContent = codeDsl[codeId].comps;
+ const compsContent = combineInfo[codeId];
const existHooks = compsContent?.[compId];
if (isEmpty(existHooks)) {
// comps属性不存在,或者comps为空:新增
- codeDsl[codeId].comps = {
- ...(codeDsl[codeId].comps || {}),
+ combineInfo[codeId] = {
+ ...(combineInfo[codeId] || {}),
[compId]: [hook],
};
} else {
@@ -286,16 +287,16 @@ class CodeBlock extends BaseService {
}
} else if (opFlag === CodeSelectOp.CHANGE) {
// 单选修改
- forIn(codeDsl, (codeBlockContent, codeId) => {
+ forIn(combineInfo, (combineItem, codeId) => {
if (codeId === diffCodeIds[0]) {
// 增加
- codeBlockContent.comps = {
- ...(codeBlockContent?.comps || {}),
+ combineItem = {
+ ...(combineItem || {}),
[compId]: [hook],
};
} else if (isEmpty(diffCodeIds) || codeId !== diffCodeIds[0]) {
// 清空或者移除之前的选项
- const compHooks = codeBlockContent?.comps?.[compId];
+ const compHooks = combineItem?.[compId];
// continue
if (!compHooks) return true;
const index = compHooks.findIndex((hookName) => hookName === hook);
@@ -307,53 +308,65 @@ class CodeBlock extends BaseService {
}
});
}
- this.setCodeDsl(codeDsl);
+ console.log('---combineInfo--', combineInfo);
+ console.log('---this.state.relations--', this.state.relations);
+ }
+
+ /**
+ * 获取绑定关系
+ * @returns {CodeRelation | null}
+ */
+ public getCombineInfo(): CodeRelation | null {
+ const root = editorService.get('root');
+ if (!root) return null;
+ this.recurseMNode(root);
+ return this.state.relations;
}
/**
* 获取不可删除列表
- * @returns {string[]}
+ * @returns {Id[]}
*/
- public getUndeletableList(): string[] {
+ public getUndeletableList(): Id[] {
return this.state.undeletableList;
}
/**
* 设置不可删除列表:为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除)
- * @param {string[]} codeIds 代码块id数组
+ * @param {Id[]} codeIds 代码块id数组
* @returns {void}
*/
- public async setUndeleteableList(codeIds: string[]): Promise {
+ public async setUndeleteableList(codeIds: Id[]): Promise {
this.state.undeletableList = codeIds;
}
/**
* 设置代码草稿
*/
- public setCodeDraft(codeId: string, content: string): void {
+ public setCodeDraft(codeId: Id, content: string): void {
globalThis.localStorage.setItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`, content);
}
/**
* 获取代码草稿
*/
- public getCodeDraft(codeId: string): string | null {
+ public getCodeDraft(codeId: Id): string | null {
return globalThis.localStorage.getItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`);
}
/**
* 删除代码草稿
*/
- public removeCodeDraft(codeId: string): void {
+ public removeCodeDraft(codeId: Id): void {
globalThis.localStorage.removeItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`);
}
/**
* 在dsl数据源中删除指定id的代码块
- * @param {string[]} codeIds 需要删除的代码块id数组
+ * @param {Id[]} codeIds 需要删除的代码块id数组
* @returns {CodeBlockDSL} 删除后的code dsl
*/
- public async deleteCodeDslByIds(codeIds: string[]): Promise {
+ public async deleteCodeDslByIds(codeIds: Id[]): Promise {
const currentDsl = await this.getCodeDsl();
const newDsl = omit(currentDsl, codeIds);
await this.setCodeDsl(newDsl);
@@ -362,9 +375,9 @@ class CodeBlock extends BaseService {
/**
* 生成代码块唯一id
- * @returns {string} 代码块唯一id
+ * @returns {Id} 代码块唯一id
*/
- public async getUniqueId(): Promise {
+ public async getUniqueId(): Promise {
const newId = `code_${Math.random().toString(10).substring(2).substring(0, 4)}`;
// 判断是否重复
const dsl = await this.getCodeDsl();
@@ -381,7 +394,7 @@ class CodeBlock extends BaseService {
public async deleteCompsInRelation(node: MNode) {
const codeDsl = cloneDeep(await this.getCodeDsl());
if (!codeDsl) return;
- this.recurseNodes(node, codeDsl);
+ this.refreshRelationDeep(node, codeDsl);
this.setCodeDsl(codeDsl);
}
@@ -395,8 +408,13 @@ class CodeBlock extends BaseService {
this.state.undeletableList = [];
}
- // 删除组件时 如果是容器 需要遍历删除其包含节点的绑定信息
- private recurseNodes(node: MNode, codeDsl: CodeBlockDSL) {
+ /**
+ * 删除组件时 如果是容器 需要遍历删除其包含节点的绑定信息
+ * @param {MNode} node 节点信息
+ * @param {CodeBlockDSL} codeDsl 代码块
+ * @returns void
+ */
+ private refreshRelationDeep(node: MNode, codeDsl: CodeBlockDSL) {
if (!node.id) return;
forIn(codeDsl, (codeBlockContent) => {
const compsContent = codeBlockContent.comps || {};
@@ -404,7 +422,34 @@ class CodeBlock extends BaseService {
});
if (!isEmpty(node.items)) {
node.items.forEach((item: MNode) => {
- this.recurseNodes(item, codeDsl);
+ this.refreshRelationDeep(item, codeDsl);
+ });
+ }
+ }
+
+ /**
+ * 递归遍历dsl中挂载了代码块的节点,并更新绑定关系数据
+ * @param {MNode} node 节点信息
+ * @returns void
+ */
+ private recurseMNode(node: MNode) {
+ forIn(node, (value, key) => {
+ if (value?.hookType === HookType.CODE && !isEmpty(value?.data)) {
+ value.data.forEach((relationItem: HookData) => {
+ if (!this.state.relations[relationItem.codeId]) {
+ this.state.relations[relationItem.codeId] = {};
+ }
+ const codeItem = this.state.relations[relationItem.codeId];
+ if (isEmpty(codeItem[node.id])) {
+ codeItem[node.id] = [];
+ }
+ codeItem[node.id].push(key);
+ });
+ }
+ });
+ if (!isEmpty(node.items)) {
+ node.items.forEach((item: MNode) => {
+ this.recurseMNode(item);
});
}
}
diff --git a/packages/editor/src/theme/code-block.scss b/packages/editor/src/theme/code-block.scss
index e6c1dd25..07a3057d 100644
--- a/packages/editor/src/theme/code-block.scss
+++ b/packages/editor/src/theme/code-block.scss
@@ -77,19 +77,6 @@
.m-fields-code-select {
width: 100%;
- .el-card__body {
- padding: 5px;
- }
- .tool-bar {
- display: flex;
- align-items: center;
- justify-content: end;
- height: 20px;
- .tool-item {
- display: flex;
- align-items: center;
- }
- }
}
.code-editor-dialog {
.el-dialog__body {
diff --git a/packages/editor/src/type.ts b/packages/editor/src/type.ts
index 8f361260..8e830ab1 100644
--- a/packages/editor/src/type.ts
+++ b/packages/editor/src/type.ts
@@ -19,7 +19,7 @@
import type { Component } from 'vue';
import type { FormConfig } from '@tmagic/form';
-import type { Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
+import type { CodeBlockContent, CodeBlockDSL, Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
import type StageCore from '@tmagic/stage';
import type { ContainerHighlightType, MoveableOptions } from '@tmagic/stage';
@@ -309,28 +309,13 @@ export interface ScrollViewerEvent {
scrollWidth: number;
}
-export interface CodeBlockDSL {
- [id: string]: CodeBlockContent;
-}
-
-export interface CodeBlockContent {
- /** 代码块名称 */
- name: string;
- /** 代码块内容 */
- content: string;
- /** 代码块与组件的绑定关系 */
- comps?: CodeRelation;
- /** 扩展字段 */
- [propName: string]: any;
-}
-
export type CodeState = {
/** 是否展示代码块编辑区 */
isShowCodeEditor: boolean;
/** 代码块DSL数据源 */
codeDsl: CodeBlockDSL | null;
/** 当前选中的代码块id */
- id: string;
+ id: Id;
/** 代码块是否可编辑 */
editable: boolean;
/** 代码编辑面板的展示模式 */
@@ -338,12 +323,23 @@ export type CodeState = {
/** list模式下左侧展示的代码列表 */
combineIds: string[];
/** 为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除) */
- undeletableList: string[];
+ undeletableList: Id[];
+ /** 代码块和组件的绑定关系 */
+ relations: CodeRelation;
+};
+
+export type HookData = {
+ /** 代码块id */
+ codeId: Id;
+ /** 参数 */
+ params?: object;
};
export type CodeRelation = {
- /** 组件id:['created'] */
- [compId: string | number]: string[];
+ [codeId: Id]: {
+ /** 组件id:['created'] */
+ [compId: Id]: string[];
+ };
};
export enum CodeEditorMode {
@@ -355,7 +351,7 @@ export enum CodeEditorMode {
export interface CodeDslList {
/** 代码块id */
- id: string;
+ id: Id;
/** 代码块名称 */
name: string;
/** 代码块函数内容 */
@@ -370,10 +366,10 @@ export interface ListState {
}
export interface ListRelationState extends ListState {
- /** 与代码块绑定的组件id信息 */
+ /** 与代码块绑定的组件信息 */
bindComps: {
/** 代码块id : 组件信息 */
- [id: string]: MNode[];
+ [id: Id]: MNode[];
};
}
diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts
index 3e7d59f3..0ec68bda 100644
--- a/packages/schema/src/index.ts
+++ b/packages/schema/src/index.ts
@@ -73,7 +73,7 @@ export interface MApp extends MComponent {
}
export interface CodeBlockDSL {
- [id: string]: CodeBlockContent;
+ [id: Id]: CodeBlockContent;
}
export interface CodeBlockContent {
@@ -81,10 +81,18 @@ export interface CodeBlockContent {
name: string;
/** 代码块内容 */
content: any;
+ /** 扩展字段 */
+ [propName: string]: any;
}
+
export interface PastePosition {
left?: number;
top?: number;
}
export type MNode = MComponent | MContainer | MPage | MApp;
+
+export enum HookType {
+ /** 代码块钩子标识 */
+ CODE = 'code',
+}
diff --git a/playground/src/configs/dsl.ts b/playground/src/configs/dsl.ts
index 6c33694b..91dad7a1 100644
--- a/playground/src/configs/dsl.ts
+++ b/playground/src/configs/dsl.ts
@@ -24,19 +24,13 @@ export default {
code_5336: {
name: 'getData',
// eslint-disable-next-line no-eval
- content: eval(`(vm) => {\n console.log("this is getData function")\n}`),
- comps: {
- page_299: ['mounted', 'created'],
- },
+ content: eval(`(vm, params) => {\n console.log("this is getData function",vm,params)\n}`),
+ params: ['name', 'age'],
},
code_5316: {
name: 'getList',
// eslint-disable-next-line no-eval
content: eval(`(vm) => {\n console.log("this is getList function")\n}`),
- comps: {
- text_9027: ['created'],
- page_299: ['created'],
- },
},
},
items: [
@@ -63,8 +57,29 @@ export default {
fontWeight: '',
},
events: [],
- created: ['code_5316', 'code_5336'],
- mounted: ['code_5336'],
+ created: {
+ hookType: 'code',
+ data: [
+ {
+ codeId: 'code_5336',
+ params: {
+ name: 'lisa',
+ age: 12,
+ },
+ },
+ {
+ codeId: 'code_5316',
+ },
+ ],
+ },
+ mounted: {
+ hookType: 'code',
+ data: [
+ {
+ codeId: 'code_5316',
+ },
+ ],
+ },
items: [
{
type: 'text',
@@ -89,7 +104,14 @@ export default {
text: 'Tmagic editor 营销活动编辑器',
multiple: true,
events: [],
- created: ['code_5316'],
+ created: {
+ hookType: 'code',
+ data: [
+ {
+ codeId: 'code_5316',
+ },
+ ],
+ },
},
{
type: 'qrcode',