mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-06-11 14:45:10 +08:00
feat: 修改code_block结构,组件代码关系绑定改为table,绑定关系更新未完成
This commit is contained in:
parent
60c572ec30
commit
c4293f17a6
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
/** 是否可编辑 */
|
||||
|
@ -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;
|
||||
|
@ -1,96 +1,131 @@
|
||||
<template>
|
||||
<div class="m-fields-code-select" :key="fieldKey">
|
||||
<TMagicCard shadow="never">
|
||||
<template #header>
|
||||
<m-fields-select
|
||||
:config="selectConfig"
|
||||
:model="model"
|
||||
:prop="prop"
|
||||
:name="name"
|
||||
:size="size"
|
||||
@change="changeHandler"
|
||||
></m-fields-select>
|
||||
</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>
|
||||
<m-form-table
|
||||
:config="tableConfig"
|
||||
:model="model[name]"
|
||||
:name="tableConfig.name"
|
||||
:prop="prop"
|
||||
:size="size"
|
||||
@change="changeHandler"
|
||||
>
|
||||
</m-form-table>
|
||||
</TMagicCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<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 { TMagicCard, tMagicMessage, TMagicTooltip } from '@tmagic/design';
|
||||
import { FormState, SelectConfig } from '@tmagic/form';
|
||||
import { TMagicCard } from '@tmagic/design';
|
||||
import { FormState, TableConfig } from '@tmagic/form';
|
||||
|
||||
import type { Services } from '../type';
|
||||
import { CodeEditorMode, CodeSelectOp } from '../type';
|
||||
import { CodeSelectOp } from '../type';
|
||||
const services = inject<Services>('services');
|
||||
const form = inject<FormState>('mForm');
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
const props = defineProps<{
|
||||
config: {
|
||||
selectConfig?: SelectConfig;
|
||||
tableConfig?: TableConfig;
|
||||
};
|
||||
model: any;
|
||||
prop: string;
|
||||
name: string;
|
||||
size: string;
|
||||
size: 'mini' | 'small' | 'medium';
|
||||
}>();
|
||||
|
||||
const selectConfig = computed(() => {
|
||||
const tableConfig = computed(() => {
|
||||
const defaultConfig = {
|
||||
multiple: true,
|
||||
options: async () => {
|
||||
const codeDsl = await services?.codeBlockService.getCodeDsl();
|
||||
if (codeDsl) {
|
||||
return map(codeDsl, (value, key) => ({
|
||||
text: `${value.name}(${key})`,
|
||||
label: `${value.name}(${key})`,
|
||||
value: key,
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
},
|
||||
dropSort: true,
|
||||
items: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '代码块',
|
||||
name: 'codeId',
|
||||
options: async () => {
|
||||
const codeDsl = await services?.codeBlockService.getCodeDsl();
|
||||
if (codeDsl) {
|
||||
return map(codeDsl, (value, key) => ({
|
||||
text: `${value.name}(${key})`,
|
||||
label: `${value.name}(${key})`,
|
||||
value: key,
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: '参数',
|
||||
// name: 'params',
|
||||
// filter: v=>JSON.stringify(v)
|
||||
// },
|
||||
],
|
||||
};
|
||||
return {
|
||||
name: 'data',
|
||||
...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 multiple = ref(true);
|
||||
const lastTagSnapshot = ref<string[]>([]);
|
||||
|
||||
watchEffect(async () => {
|
||||
if (!props.model[props.name]) return;
|
||||
const combineNames = await Promise.all(
|
||||
props.model[props.name].map(async (id: string) => {
|
||||
const { name = '' } = (await services?.codeBlockService.getCodeContentById(id)) || {};
|
||||
return name;
|
||||
}),
|
||||
);
|
||||
fieldKey.value = combineNames.join('-');
|
||||
});
|
||||
// watchEffect(async () => {
|
||||
// if (isEmpty(selectModel)) return;
|
||||
// const combineNames = await Promise.all(
|
||||
// selectModel.value[props.name].map(async (id: string) => {
|
||||
// const { name = '' } = (await services?.codeBlockService.getCodeContentById(id)) || {};
|
||||
// return name;
|
||||
// }),
|
||||
// );
|
||||
// fieldKey.value = combineNames.join('-');
|
||||
// });
|
||||
|
||||
const changeHandler = async (value: any) => {
|
||||
console.log('---value--', value);
|
||||
let codeIds = value;
|
||||
if (typeof value === 'string') {
|
||||
multiple.value = false;
|
||||
@ -118,14 +153,14 @@ const setCombineRelation = async (codeIds: string[]) => {
|
||||
await services?.codeBlockService.setCombineRelation(id, diffValues, opFlag, props.prop);
|
||||
};
|
||||
|
||||
const viewHandler = async () => {
|
||||
if (props.model[props.name].length === 0) {
|
||||
tMagicMessage.error('请先绑定代码块');
|
||||
return;
|
||||
}
|
||||
// 记录当前已被绑定的代码块,为查看弹窗的展示内容
|
||||
await services?.codeBlockService.setCombineIds(props.model[props.name]);
|
||||
await services?.codeBlockService.setMode(CodeEditorMode.LIST);
|
||||
services?.codeBlockService.setCodeEditorContent(true, props.model[props.name][0]);
|
||||
};
|
||||
// const viewHandler = async () => {
|
||||
// if (props.model[props.name].length === 0) {
|
||||
// tMagicMessage.error('请先绑定代码块');
|
||||
// return;
|
||||
// }
|
||||
// // 记录当前已被绑定的代码块,为查看弹窗的展示内容
|
||||
// await services?.codeBlockService.setCombineIds(props.model[props.name]);
|
||||
// await services?.codeBlockService.setMode(CodeEditorMode.LIST);
|
||||
// services?.codeBlockService.setCodeEditorContent(true, props.model[props.name][0]);
|
||||
// };
|
||||
</script>
|
||||
|
@ -52,7 +52,7 @@
|
||||
></FunctionEditor>
|
||||
</div>
|
||||
</template>
|
||||
</layout>
|
||||
</Layout>
|
||||
</TMagicDialog>
|
||||
</template>
|
||||
|
||||
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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<CodeBlockContent | null> {
|
||||
public async getCodeContentById(id: Id): Promise<CodeBlockContent | null> {
|
||||
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<void> {
|
||||
public async setCodeDslById(id: Id, codeConfig: CodeBlockContent): Promise<void> {
|
||||
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<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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置不可删除列表:为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除)
|
||||
* @param {string[]} codeIds 代码块id数组
|
||||
* @param {Id[]} codeIds 代码块id数组
|
||||
* @returns {void}
|
||||
*/
|
||||
public async setUndeleteableList(codeIds: string[]): Promise<void> {
|
||||
public async setUndeleteableList(codeIds: Id[]): Promise<void> {
|
||||
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<CodeBlockDSL> {
|
||||
public async deleteCodeDslByIds(codeIds: Id[]): Promise<CodeBlockDSL> {
|
||||
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<string> {
|
||||
public async getUniqueId(): Promise<Id> {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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[];
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
}
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user