feat: 修改code_block结构,组件代码关系绑定改为table,绑定关系更新未完成

This commit is contained in:
parisma 2022-11-09 15:22:46 +08:00
parent 60c572ec30
commit c4293f17a6
11 changed files with 268 additions and 172 deletions

View File

@ -21,6 +21,7 @@ import { EventEmitter } from 'events';
import { isEmpty } from 'lodash-es'; import { isEmpty } from 'lodash-es';
import type { EventItemConfig, MComponent, MContainer, MPage } from '@tmagic/schema'; import type { EventItemConfig, MComponent, MContainer, MPage } from '@tmagic/schema';
import { HookType } from '@tmagic/schema';
import type App from './App'; import type App from './App';
import type Page from './Page'; import type Page from './Page';
@ -85,10 +86,11 @@ class Node extends EventEmitter {
} }
private async runCodeBlock(hook: string) { private async runCodeBlock(hook: string) {
if (!Array.isArray(this.data[hook]) || !this.app.codeDsl || isEmpty(this.app?.codeDsl)) return; if (this.data[hook]?.hookType !== HookType.CODE || !this.app.codeDsl || isEmpty(this.app?.codeDsl)) return;
for (const codeId of this.data[hook]) { for (const item of this.data[hook].data) {
const { codeId, params = {} } = item;
if (this.app.codeDsl[codeId] && typeof this.app?.codeDsl[codeId]?.content === 'function') { 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);
} }
} }
} }

View File

@ -22,6 +22,7 @@ import { computed, inject, ref, watchEffect } from 'vue';
import type * as monaco from 'monaco-editor'; import type * as monaco from 'monaco-editor';
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design'; import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
import { Id } from '@tmagic/schema';
import { datetimeFormatter } from '@tmagic/utils'; import { datetimeFormatter } from '@tmagic/utils';
import MagicCodeEditor from '../layouts/CodeEditor.vue'; import MagicCodeEditor from '../layouts/CodeEditor.vue';
@ -30,7 +31,7 @@ import type { Services } from '../type';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
/** 代码id */ /** 代码id */
id: string; id: Id;
/** 代码内容 */ /** 代码内容 */
content: string; content: string;
/** 是否可编辑 */ /** 是否可编辑 */

View File

@ -23,6 +23,7 @@
import { inject, ref, watchEffect } from 'vue'; import { inject, ref, watchEffect } from 'vue';
import { TMagicCard, TMagicInput, tMagicMessage } from '@tmagic/design'; import { TMagicCard, TMagicInput, tMagicMessage } from '@tmagic/design';
import { Id } from '@tmagic/schema';
import type { Services } from '../type'; import type { Services } from '../type';
@ -30,7 +31,7 @@ import CodeDraftEditor from './CodeDraftEditor.vue';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
id: string; id: Id;
name: string; name: string;
content: string; content: string;
editable?: boolean; editable?: boolean;

View File

@ -1,96 +1,131 @@
<template> <template>
<div class="m-fields-code-select" :key="fieldKey"> <div class="m-fields-code-select" :key="fieldKey">
<TMagicCard shadow="never"> <TMagicCard shadow="never">
<template #header> <m-form-table
<m-fields-select :config="tableConfig"
:config="selectConfig" :model="model[name]"
:model="model" :name="tableConfig.name"
:prop="prop" :prop="prop"
:name="name" :size="size"
:size="size" @change="changeHandler"
@change="changeHandler" >
></m-fields-select> </m-form-table>
</template>
<div class="tool-bar">
<TMagicTooltip class="tool-item" effect="dark" content="查看代码块" placement="top">
<svg
@click="viewHandler"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
width="15px"
height="15px"
data-v-65a7fb6c=""
>
<path
fill="currentColor"
d="m23 12l-7.071 7.071l-1.414-1.414L20.172 12l-5.657-5.657l1.414-1.414L23 12zM3.828 12l5.657 5.657l-1.414 1.414L1 12l7.071-7.071l1.414 1.414L3.828 12z"
></path>
</svg>
</TMagicTooltip>
</div>
</TMagicCard> </TMagicCard>
</div> </div>
</template> </template>
<script lang="ts" setup name="MEditorCodeSelect"> <script lang="ts" setup name="MEditorCodeSelect">
import { computed, defineEmits, defineProps, inject, ref, watchEffect } from 'vue'; import { computed, defineEmits, defineProps, inject, ref } from 'vue';
import { map, xor } from 'lodash-es'; import { map, xor } from 'lodash-es';
import { TMagicCard, tMagicMessage, TMagicTooltip } from '@tmagic/design'; import { TMagicCard } from '@tmagic/design';
import { FormState, SelectConfig } from '@tmagic/form'; import { FormState, TableConfig } from '@tmagic/form';
import type { Services } from '../type'; import type { Services } from '../type';
import { CodeEditorMode, CodeSelectOp } from '../type'; import { CodeSelectOp } from '../type';
const services = inject<Services>('services'); const services = inject<Services>('services');
const form = inject<FormState>('mForm'); const form = inject<FormState>('mForm');
const emit = defineEmits(['change']); const emit = defineEmits(['change']);
const props = defineProps<{ const props = defineProps<{
config: { config: {
selectConfig?: SelectConfig; tableConfig?: TableConfig;
}; };
model: any; model: any;
prop: string; prop: string;
name: string; name: string;
size: string; size: 'mini' | 'small' | 'medium';
}>(); }>();
const selectConfig = computed(() => { const tableConfig = computed(() => {
const defaultConfig = { const defaultConfig = {
multiple: true, dropSort: true,
options: async () => { items: [
const codeDsl = await services?.codeBlockService.getCodeDsl(); {
if (codeDsl) { type: 'select',
return map(codeDsl, (value, key) => ({ label: '代码块',
text: `${value.name}${key}`, name: 'codeId',
label: `${value.name}${key}`, options: async () => {
value: key, const codeDsl = await services?.codeBlockService.getCodeDsl();
})); if (codeDsl) {
} return map(codeDsl, (value, key) => ({
return []; text: `${value.name}${key}`,
}, label: `${value.name}${key}`,
value: key,
}));
}
return [];
},
},
// {
// label: '',
// name: 'params',
// filter: v=>JSON.stringify(v)
// },
],
}; };
return { return {
name: 'data',
...defaultConfig, ...defaultConfig,
...props.config.selectConfig, ...props.config.tableConfig,
}; };
}); });
// const selectModel = computed(() => {
// console.log("props.model[props.name].data",props.model[props.name].data)
// return {
// [props.name]: props.model[props.name]?.data?.map((item: { codeId: Id }) => item.codeId) || []
// }
// });
// watch(
// () => selectModel.value,
// () => {
// const selectData
// if (isEmpty(selectModel)) return;
// const hookData = selectModel.value.map((selectedCodeId: Id) => ({
// codeId: selectedCodeId,
// }));
// console.log('hookData', hookData);
// if (isEmpty(hookData)) return;
// if (!props.model[props.name]?.data || isEmpty(props.model[props.name]?.data)) {
// // hook
// props.model[props.name] = {
// hookType: HookType.CODE,
// data: hookData,
// };
// } else {
// // hook data
// props.model[props.name].data = {
// ...props.model[props.name].data,
// ...hookData,
// };
// }
// console.log('-0-props.model[props.name]--', props.model[props.name]);
// },
// {
// deep: true,
// immediate: true,
// },
// );
const fieldKey = ref(''); const fieldKey = ref('');
const multiple = ref(true); const multiple = ref(true);
const lastTagSnapshot = ref<string[]>([]); const lastTagSnapshot = ref<string[]>([]);
watchEffect(async () => { // watchEffect(async () => {
if (!props.model[props.name]) return; // if (isEmpty(selectModel)) return;
const combineNames = await Promise.all( // const combineNames = await Promise.all(
props.model[props.name].map(async (id: string) => { // selectModel.value[props.name].map(async (id: string) => {
const { name = '' } = (await services?.codeBlockService.getCodeContentById(id)) || {}; // const { name = '' } = (await services?.codeBlockService.getCodeContentById(id)) || {};
return name; // return name;
}), // }),
); // );
fieldKey.value = combineNames.join('-'); // fieldKey.value = combineNames.join('-');
}); // });
const changeHandler = async (value: any) => { const changeHandler = async (value: any) => {
console.log('---value--', value);
let codeIds = value; let codeIds = value;
if (typeof value === 'string') { if (typeof value === 'string') {
multiple.value = false; multiple.value = false;
@ -118,14 +153,14 @@ const setCombineRelation = async (codeIds: string[]) => {
await services?.codeBlockService.setCombineRelation(id, diffValues, opFlag, props.prop); await services?.codeBlockService.setCombineRelation(id, diffValues, opFlag, props.prop);
}; };
const viewHandler = async () => { // const viewHandler = async () => {
if (props.model[props.name].length === 0) { // if (props.model[props.name].length === 0) {
tMagicMessage.error('请先绑定代码块'); // tMagicMessage.error('');
return; // return;
} // }
// // //
await services?.codeBlockService.setCombineIds(props.model[props.name]); // await services?.codeBlockService.setCombineIds(props.model[props.name]);
await services?.codeBlockService.setMode(CodeEditorMode.LIST); // await services?.codeBlockService.setMode(CodeEditorMode.LIST);
services?.codeBlockService.setCodeEditorContent(true, props.model[props.name][0]); // services?.codeBlockService.setCodeEditorContent(true, props.model[props.name][0]);
}; // };
</script> </script>

View File

@ -52,7 +52,7 @@
></FunctionEditor> ></FunctionEditor>
</div> </div>
</template> </template>
</layout> </Layout>
</TMagicDialog> </TMagicDialog>
</template> </template>
@ -61,10 +61,11 @@ import { computed, inject, reactive, ref, watchEffect } from 'vue';
import { cloneDeep, forIn, isEmpty } from 'lodash-es'; import { cloneDeep, forIn, isEmpty } from 'lodash-es';
import { TMagicDialog, TMagicTree } from '@tmagic/design'; import { TMagicDialog, TMagicTree } from '@tmagic/design';
import { CodeBlockContent } from '@tmagic/schema';
import FunctionEditor from '../../../components/FunctionEditor.vue'; import FunctionEditor from '../../../components/FunctionEditor.vue';
import Layout from '../../../components/Layout.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 { CodeEditorMode } from '../../../type';
import { serializeConfig } from '../../../utils/editor'; import { serializeConfig } from '../../../utils/editor';
@ -86,6 +87,8 @@ const editable = computed(() => services?.codeBlockService.getEditStatus());
// id // id
const selectedIds = computed(() => services?.codeBlockService.getCombineIds() || []); const selectedIds = computed(() => services?.codeBlockService.getCombineIds() || []);
services?.codeBlockService.getCombineInfo();
watchEffect(async () => { watchEffect(async () => {
codeConfig.value = cloneDeep(await services?.codeBlockService.getCodeContentById(id.value)) || null; codeConfig.value = cloneDeep(await services?.codeBlockService.getCodeContentById(id.value)) || null;
if (!codeConfig.value) return; if (!codeConfig.value) return;

View File

@ -99,11 +99,11 @@ import { Close, Edit, Link, View } from '@element-plus/icons-vue';
import { forIn, isEmpty } from 'lodash-es'; import { forIn, isEmpty } from 'lodash-es';
import { TMagicButton, TMagicInput, tMagicMessage, TMagicTooltip, TMagicTree } from '@tmagic/design'; 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 StageCore from '@tmagic/stage';
import Icon from '../../../components/Icon.vue'; 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 { CodeDeleteErrorType, CodeDslList, CodeEditorMode, ListRelationState } from '../../../type';
import codeBlockEditor from './CodeBlockEditor.vue'; import codeBlockEditor from './CodeBlockEditor.vue';
@ -126,15 +126,11 @@ const editable = computed(() => services?.codeBlockService.getEditStatus());
const isShowCodeBlockEditor = computed(() => services?.codeBlockService.getCodeEditorShowStatus() || false); const isShowCodeBlockEditor = computed(() => services?.codeBlockService.getCodeEditorShowStatus() || false);
// ID // ID
const getBindCompsByCodeId = (codeId: string, codeBlockContent: CodeBlockContent) => { const getBindCompsByCodeId = (codeId: string) => {
if (isEmpty(codeBlockContent) || isEmpty(codeBlockContent.comps)) { const codeCombineInfo = services?.codeBlockService.getCombineInfo();
state.bindComps[codeId] = []; if (!codeCombineInfo) return null;
return; const bindCompsId = Object.keys(codeCombineInfo[codeId]);
} const compsInfo = bindCompsId.map((compId) => ({
const compsField = codeBlockContent.comps as CodeRelation;
const bindCompIds = Object.keys(compsField);
const bindCompsFiltered = bindCompIds.filter((compId) => !isEmpty(compsField[compId]));
const compsInfo = bindCompsFiltered.map((compId) => ({
id: compId, id: compId,
name: getCompName(compId), name: getCompName(compId),
})); }));
@ -147,7 +143,7 @@ const initList = async () => {
if (!codeDsl) return; if (!codeDsl) return;
state.codeList = []; state.codeList = [];
forIn(codeDsl, (value: CodeBlockContent, codeId: string) => { forIn(codeDsl, (value: CodeBlockContent, codeId: string) => {
getBindCompsByCodeId(codeId, value); getBindCompsByCodeId(codeId);
state.codeList.push({ state.codeList.push({
id: codeId, id: codeId,
name: value.name, name: value.name,

View File

@ -19,10 +19,10 @@
import { reactive } from 'vue'; import { reactive } from 'vue';
import { cloneDeep, forIn, isEmpty, keys, omit, pick } from 'lodash-es'; 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 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 { CODE_DRAFT_STORAGE_KEY, CodeEditorMode, CodeSelectOp } from '../type';
import { error, info } from '../utils/logger'; import { error, info } from '../utils/logger';
@ -37,6 +37,7 @@ class CodeBlock extends BaseService {
mode: CodeEditorMode.EDITOR, mode: CodeEditorMode.EDITOR,
combineIds: [], combineIds: [],
undeletableList: [], undeletableList: [],
relations: {},
}); });
constructor() { constructor() {
@ -82,10 +83,10 @@ class CodeBlock extends BaseService {
/** /**
* id获取代码块内容 * id获取代码块内容
* @param {string} id id * @param {Id} id id
* @returns {CodeBlockContent | null} * @returns {CodeBlockContent | null}
*/ */
public async getCodeContentById(id: string): Promise<CodeBlockContent | null> { public async getCodeContentById(id: Id): Promise<CodeBlockContent | null> {
if (!id) return null; if (!id) return null;
const totalCodeDsl = await this.getCodeDsl(); const totalCodeDsl = await this.getCodeDsl();
if (!totalCodeDsl) return null; if (!totalCodeDsl) return null;
@ -94,11 +95,11 @@ class CodeBlock extends BaseService {
/** /**
* ID和代码内容到源dsl * ID和代码内容到源dsl
* @param {string} id id * @param {Id} id id
* @param {CodeBlockContent} codeConfig * @param {CodeBlockContent} codeConfig
* @returns {void} * @returns {void}
*/ */
public async setCodeDslById(id: string, codeConfig: CodeBlockContent): Promise<void> { public async setCodeDslById(id: Id, codeConfig: CodeBlockContent): Promise<void> {
let codeDsl = await this.getCodeDsl(); let codeDsl = await this.getCodeDsl();
if (!codeDsl) { if (!codeDsl) {
// dsl中无代码块字段 // dsl中无代码块字段
@ -154,10 +155,10 @@ class CodeBlock extends BaseService {
/** /**
* *
* @param {boolean} status * @param {boolean} status
* @param {string} id id * @param {Id} id id
* @returns {void} * @returns {void}
*/ */
public setCodeEditorContent(status: boolean, id: string): void { public setCodeEditorContent(status: boolean, id: Id): void {
if (!id) return; if (!id) return;
this.setId(id); this.setId(id);
this.state.isShowCodeEditor = status; this.state.isShowCodeEditor = status;
@ -190,19 +191,19 @@ class CodeBlock extends BaseService {
/** /**
* ID * ID
* @param {string} id id * @param {Id} id id
* @returns {void} * @returns {void}
*/ */
public setId(id: string) { public setId(id: Id) {
if (!id) return; if (!id) return;
this.state.id = id; this.state.id = id;
} }
/** /**
* ID * ID
* @returns {string} id id * @returns {Id} id id
*/ */
public getId(): string { public getId(): Id {
return this.state.id; return this.state.id;
} }
@ -249,12 +250,12 @@ class CodeBlock extends BaseService {
* @returns {void} * @returns {void}
*/ */
public async setCombineRelation(compId: Id, diffCodeIds: string[], opFlag: CodeSelectOp, hook: string) { public async setCombineRelation(compId: Id, diffCodeIds: string[], opFlag: CodeSelectOp, hook: string) {
const codeDsl = cloneDeep(await this.getCodeDsl()); const combineInfo = this.getCombineInfo();
if (!codeDsl) return; if (!combineInfo) return;
if (opFlag === CodeSelectOp.DELETE) { if (opFlag === CodeSelectOp.DELETE) {
try { try {
diffCodeIds.forEach((codeId) => { diffCodeIds.forEach((codeId) => {
const compsContent = codeDsl[codeId].comps; const compsContent = combineInfo[codeId];
const index = compsContent?.[compId].findIndex((item) => item === hook); const index = compsContent?.[compId].findIndex((item) => item === hook);
if (typeof index !== 'undefined' && index !== -1) { if (typeof index !== 'undefined' && index !== -1) {
compsContent?.[compId].splice(index, 1); compsContent?.[compId].splice(index, 1);
@ -267,12 +268,12 @@ class CodeBlock extends BaseService {
} else if (opFlag === CodeSelectOp.ADD) { } else if (opFlag === CodeSelectOp.ADD) {
try { try {
diffCodeIds.forEach((codeId) => { diffCodeIds.forEach((codeId) => {
const compsContent = codeDsl[codeId].comps; const compsContent = combineInfo[codeId];
const existHooks = compsContent?.[compId]; const existHooks = compsContent?.[compId];
if (isEmpty(existHooks)) { if (isEmpty(existHooks)) {
// comps属性不存在或者comps为空新增 // comps属性不存在或者comps为空新增
codeDsl[codeId].comps = { combineInfo[codeId] = {
...(codeDsl[codeId].comps || {}), ...(combineInfo[codeId] || {}),
[compId]: [hook], [compId]: [hook],
}; };
} else { } else {
@ -286,16 +287,16 @@ class CodeBlock extends BaseService {
} }
} else if (opFlag === CodeSelectOp.CHANGE) { } else if (opFlag === CodeSelectOp.CHANGE) {
// 单选修改 // 单选修改
forIn(codeDsl, (codeBlockContent, codeId) => { forIn(combineInfo, (combineItem, codeId) => {
if (codeId === diffCodeIds[0]) { if (codeId === diffCodeIds[0]) {
// 增加 // 增加
codeBlockContent.comps = { combineItem = {
...(codeBlockContent?.comps || {}), ...(combineItem || {}),
[compId]: [hook], [compId]: [hook],
}; };
} else if (isEmpty(diffCodeIds) || codeId !== diffCodeIds[0]) { } else if (isEmpty(diffCodeIds) || codeId !== diffCodeIds[0]) {
// 清空或者移除之前的选项 // 清空或者移除之前的选项
const compHooks = codeBlockContent?.comps?.[compId]; const compHooks = combineItem?.[compId];
// continue // continue
if (!compHooks) return true; if (!compHooks) return true;
const index = compHooks.findIndex((hookName) => hookName === hook); 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<MApp | null>('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; return this.state.undeletableList;
} }
/** /**
* 线 * 线
* @param {string[]} codeIds id数组 * @param {Id[]} codeIds id数组
* @returns {void} * @returns {void}
*/ */
public async setUndeleteableList(codeIds: string[]): Promise<void> { public async setUndeleteableList(codeIds: Id[]): Promise<void> {
this.state.undeletableList = codeIds; 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); 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}`); 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}`); globalThis.localStorage.removeItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`);
} }
/** /**
* dsl数据源中删除指定id的代码块 * dsl数据源中删除指定id的代码块
* @param {string[]} codeIds id数组 * @param {Id[]} codeIds id数组
* @returns {CodeBlockDSL} code dsl * @returns {CodeBlockDSL} code dsl
*/ */
public async deleteCodeDslByIds(codeIds: string[]): Promise<CodeBlockDSL> { public async deleteCodeDslByIds(codeIds: Id[]): Promise<CodeBlockDSL> {
const currentDsl = await this.getCodeDsl(); const currentDsl = await this.getCodeDsl();
const newDsl = omit(currentDsl, codeIds); const newDsl = omit(currentDsl, codeIds);
await this.setCodeDsl(newDsl); await this.setCodeDsl(newDsl);
@ -362,9 +375,9 @@ class CodeBlock extends BaseService {
/** /**
* id * id
* @returns {string} id * @returns {Id} id
*/ */
public async getUniqueId(): Promise<string> { public async getUniqueId(): Promise<Id> {
const newId = `code_${Math.random().toString(10).substring(2).substring(0, 4)}`; const newId = `code_${Math.random().toString(10).substring(2).substring(0, 4)}`;
// 判断是否重复 // 判断是否重复
const dsl = await this.getCodeDsl(); const dsl = await this.getCodeDsl();
@ -381,7 +394,7 @@ class CodeBlock extends BaseService {
public async deleteCompsInRelation(node: MNode) { public async deleteCompsInRelation(node: MNode) {
const codeDsl = cloneDeep(await this.getCodeDsl()); const codeDsl = cloneDeep(await this.getCodeDsl());
if (!codeDsl) return; if (!codeDsl) return;
this.recurseNodes(node, codeDsl); this.refreshRelationDeep(node, codeDsl);
this.setCodeDsl(codeDsl); this.setCodeDsl(codeDsl);
} }
@ -395,8 +408,13 @@ class CodeBlock extends BaseService {
this.state.undeletableList = []; 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; if (!node.id) return;
forIn(codeDsl, (codeBlockContent) => { forIn(codeDsl, (codeBlockContent) => {
const compsContent = codeBlockContent.comps || {}; const compsContent = codeBlockContent.comps || {};
@ -404,7 +422,34 @@ class CodeBlock extends BaseService {
}); });
if (!isEmpty(node.items)) { if (!isEmpty(node.items)) {
node.items.forEach((item: MNode) => { 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);
}); });
} }
} }

View File

@ -77,19 +77,6 @@
.m-fields-code-select { .m-fields-code-select {
width: 100%; 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 { .code-editor-dialog {
.el-dialog__body { .el-dialog__body {

View File

@ -19,7 +19,7 @@
import type { Component } from 'vue'; import type { Component } from 'vue';
import type { FormConfig } from '@tmagic/form'; 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 StageCore from '@tmagic/stage';
import type { ContainerHighlightType, MoveableOptions } from '@tmagic/stage'; import type { ContainerHighlightType, MoveableOptions } from '@tmagic/stage';
@ -309,28 +309,13 @@ export interface ScrollViewerEvent {
scrollWidth: number; scrollWidth: number;
} }
export interface CodeBlockDSL {
[id: string]: CodeBlockContent;
}
export interface CodeBlockContent {
/** 代码块名称 */
name: string;
/** 代码块内容 */
content: string;
/** 代码块与组件的绑定关系 */
comps?: CodeRelation;
/** 扩展字段 */
[propName: string]: any;
}
export type CodeState = { export type CodeState = {
/** 是否展示代码块编辑区 */ /** 是否展示代码块编辑区 */
isShowCodeEditor: boolean; isShowCodeEditor: boolean;
/** 代码块DSL数据源 */ /** 代码块DSL数据源 */
codeDsl: CodeBlockDSL | null; codeDsl: CodeBlockDSL | null;
/** 当前选中的代码块id */ /** 当前选中的代码块id */
id: string; id: Id;
/** 代码块是否可编辑 */ /** 代码块是否可编辑 */
editable: boolean; editable: boolean;
/** 代码编辑面板的展示模式 */ /** 代码编辑面板的展示模式 */
@ -338,12 +323,23 @@ export type CodeState = {
/** list模式下左侧展示的代码列表 */ /** list模式下左侧展示的代码列表 */
combineIds: string[]; combineIds: string[];
/** 为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除) */ /** 为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除) */
undeletableList: string[]; undeletableList: Id[];
/** 代码块和组件的绑定关系 */
relations: CodeRelation;
};
export type HookData = {
/** 代码块id */
codeId: Id;
/** 参数 */
params?: object;
}; };
export type CodeRelation = { export type CodeRelation = {
/** 组件id:['created'] */ [codeId: Id]: {
[compId: string | number]: string[]; /** 组件id:['created'] */
[compId: Id]: string[];
};
}; };
export enum CodeEditorMode { export enum CodeEditorMode {
@ -355,7 +351,7 @@ export enum CodeEditorMode {
export interface CodeDslList { export interface CodeDslList {
/** 代码块id */ /** 代码块id */
id: string; id: Id;
/** 代码块名称 */ /** 代码块名称 */
name: string; name: string;
/** 代码块函数内容 */ /** 代码块函数内容 */
@ -370,10 +366,10 @@ export interface ListState {
} }
export interface ListRelationState extends ListState { export interface ListRelationState extends ListState {
/** 与代码块绑定的组件id信息 */ /** 与代码块绑定的组件信息 */
bindComps: { bindComps: {
/** 代码块id : 组件信息 */ /** 代码块id : 组件信息 */
[id: string]: MNode[]; [id: Id]: MNode[];
}; };
} }

View File

@ -73,7 +73,7 @@ export interface MApp extends MComponent {
} }
export interface CodeBlockDSL { export interface CodeBlockDSL {
[id: string]: CodeBlockContent; [id: Id]: CodeBlockContent;
} }
export interface CodeBlockContent { export interface CodeBlockContent {
@ -81,10 +81,18 @@ export interface CodeBlockContent {
name: string; name: string;
/** 代码块内容 */ /** 代码块内容 */
content: any; content: any;
/** 扩展字段 */
[propName: string]: any;
} }
export interface PastePosition { export interface PastePosition {
left?: number; left?: number;
top?: number; top?: number;
} }
export type MNode = MComponent | MContainer | MPage | MApp; export type MNode = MComponent | MContainer | MPage | MApp;
export enum HookType {
/** 代码块钩子标识 */
CODE = 'code',
}

View File

@ -24,19 +24,13 @@ export default {
code_5336: { code_5336: {
name: 'getData', name: 'getData',
// eslint-disable-next-line no-eval // eslint-disable-next-line no-eval
content: eval(`(vm) => {\n console.log("this is getData function")\n}`), content: eval(`(vm, params) => {\n console.log("this is getData function",vm,params)\n}`),
comps: { params: ['name', 'age'],
page_299: ['mounted', 'created'],
},
}, },
code_5316: { code_5316: {
name: 'getList', name: 'getList',
// eslint-disable-next-line no-eval // eslint-disable-next-line no-eval
content: eval(`(vm) => {\n console.log("this is getList function")\n}`), content: eval(`(vm) => {\n console.log("this is getList function")\n}`),
comps: {
text_9027: ['created'],
page_299: ['created'],
},
}, },
}, },
items: [ items: [
@ -63,8 +57,29 @@ export default {
fontWeight: '', fontWeight: '',
}, },
events: [], events: [],
created: ['code_5316', 'code_5336'], created: {
mounted: ['code_5336'], hookType: 'code',
data: [
{
codeId: 'code_5336',
params: {
name: 'lisa',
age: 12,
},
},
{
codeId: 'code_5316',
},
],
},
mounted: {
hookType: 'code',
data: [
{
codeId: 'code_5316',
},
],
},
items: [ items: [
{ {
type: 'text', type: 'text',
@ -89,7 +104,14 @@ export default {
text: 'Tmagic editor 营销活动编辑器', text: 'Tmagic editor 营销活动编辑器',
multiple: true, multiple: true,
events: [], events: [],
created: ['code_5316'], created: {
hookType: 'code',
data: [
{
codeId: 'code_5316',
},
],
},
}, },
{ {
type: 'qrcode', type: 'qrcode',