fix: 代码块结构改造完成

This commit is contained in:
parisma 2022-11-10 17:03:36 +08:00
parent c4293f17a6
commit c7a8552d9b
9 changed files with 122 additions and 294 deletions

View File

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

View File

@ -1,30 +1,26 @@
<template>
<div class="m-fields-code-select" :key="fieldKey">
<TMagicCard shadow="never">
<m-form-table
:config="tableConfig"
:model="model[name]"
:name="tableConfig.name"
:prop="prop"
:size="size"
@change="changeHandler"
>
</m-form-table>
</TMagicCard>
<div class="m-fields-code-select">
<m-form-table
:config="tableConfig"
:model="model[name]"
name="hookData"
:prop="prop"
:size="size"
@change="changeHandler"
>
</m-form-table>
</div>
</template>
<script lang="ts" setup name="MEditorCodeSelect">
import { computed, defineEmits, defineProps, inject, ref } from 'vue';
import { map, xor } from 'lodash-es';
import { computed, defineEmits, defineProps, inject, watch } from 'vue';
import { isEmpty, map } from 'lodash-es';
import { TMagicCard } from '@tmagic/design';
import { FormState, TableConfig } from '@tmagic/form';
import { FormItem, TableConfig } from '@tmagic/form';
import { HookType } from '@tmagic/schema';
import type { Services } from '../type';
import { CodeSelectOp } from '../type';
import { Services } from '../type';
const services = inject<Services>('services');
const form = inject<FormState>('mForm');
const emit = defineEmits(['change']);
const props = defineProps<{
@ -37,7 +33,7 @@ const props = defineProps<{
size: 'mini' | 'small' | 'medium';
}>();
const tableConfig = computed(() => {
const tableConfig = computed<FormItem>(() => {
const defaultConfig = {
dropSort: true,
items: [
@ -57,110 +53,41 @@ const tableConfig = computed(() => {
return [];
},
},
// {
// label: '',
// name: 'params',
// filter: v=>JSON.stringify(v)
// },
],
};
return {
name: 'data',
...defaultConfig,
...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(
() => props.model[props.name],
(value) => {
//
if (isEmpty(value)) {
//
props.model[props.name] = {
hookType: HookType.CODE,
hookData: [],
};
} else if (Array.isArray(value) && value.length > 0) {
// ['code1','code2']
const hookData = value.map((codeId) => ({
codeId,
}));
props.model[props.name] = {
hookType: HookType.CODE,
hookData,
};
}
},
{
immediate: true,
},
);
// 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 (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;
codeIds = value ? [value] : [];
}
await setCombineRelation(codeIds);
emit('change', value);
const changeHandler = async () => {
emit('change', props.model[props.name]);
};
//
const setCombineRelation = async (codeIds: string[]) => {
// id
const { id = '' } = services?.editorService.get('node') || {};
//
let opFlag = CodeSelectOp.CHANGE;
let diffValues = codeIds;
if (multiple.value) {
// initValuesinitValues
lastTagSnapshot.value = form?.initValues[props.name] || [];
opFlag = codeIds.length < lastTagSnapshot.value.length ? CodeSelectOp.DELETE : CodeSelectOp.ADD;
diffValues = xor(codeIds, lastTagSnapshot.value) as 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]);
// };
</script>

View File

@ -65,7 +65,7 @@ import { CodeBlockContent } from '@tmagic/schema';
import FunctionEditor from '../../../components/FunctionEditor.vue';
import Layout from '../../../components/Layout.vue';
import type { CodeDslList, ListState, Services } from '../../../type';
import type { CodeDslItem, ListState, Services } from '../../../type';
import { CodeEditorMode } from '../../../type';
import { serializeConfig } from '../../../utils/editor';
@ -87,8 +87,6 @@ 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;
@ -108,7 +106,7 @@ watchEffect(async () => {
currentTitle.value = state.codeList[0]?.name || '';
});
const selectHandler = (data: CodeDslList) => {
const selectHandler = (data: CodeDslItem) => {
services?.codeBlockService.setId(data.id);
currentTitle.value = data.name;
};

View File

@ -40,7 +40,7 @@
effect="dark"
content="查看绑定关系"
placement="bottom"
v-if="state.bindComps[data.id] && state.bindComps[data.id].length > 0"
v-if="data.combineInfo && data.combineInfo.length > 0"
>
<Icon :icon="Link" class="edit-icon" @click.stop="toggleCombineRelation(data)"></Icon>
</TMagicTooltip>
@ -53,7 +53,7 @@
<!-- 展示代码块下绑定的组件 -->
<div
class="code-comp-map-wrapper"
v-if="data.showRelation && state.bindComps[data.id] && state.bindComps[data.id].length > 0"
v-if="data.showRelation && data.combineInfo && data.combineInfo.length > 0"
>
<svg class="arrow-left" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-029747aa="">
<path
@ -61,23 +61,14 @@
d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z"
></path>
</svg>
<!-- todo 功能暂时隐藏 -->
<!-- <TMagicButton
v-for="(comp, index) in state.bindComps[data.id]"
:key="index"
class="code-comp"
size="small"
:plain="true"
>{{ comp.name }}<Icon :icon="Close" class="comp-delete-icon" @click.stop="unbind(comp.id, data.id)"></Icon
></TMagicButton> -->
<TMagicButton
v-for="(comp, index) in state.bindComps[data.id]"
v-for="(comp, index) in data.combineInfo"
:key="index"
class="code-comp"
size="small"
:plain="true"
@click.stop="selectComp(comp.id)"
>{{ comp.name }}</TMagicButton
@click.stop="selectComp(comp.compId)"
>{{ comp.compName }}</TMagicButton
>
</div>
</div>
@ -96,65 +87,65 @@
<script lang="ts" setup name="MEditorCodeBlockList">
import { computed, inject, reactive, ref, watch } from 'vue';
import { Close, Edit, Link, View } from '@element-plus/icons-vue';
import { forIn, isEmpty } from 'lodash-es';
import { cloneDeep, forIn, isEmpty } from 'lodash-es';
import { TMagicButton, TMagicInput, tMagicMessage, TMagicTooltip, TMagicTree } from '@tmagic/design';
import { CodeBlockContent, Id } from '@tmagic/schema';
import StageCore from '@tmagic/stage';
import Icon from '../../../components/Icon.vue';
import type { Services } from '../../../type';
import { CodeDeleteErrorType, CodeDslList, CodeEditorMode, ListRelationState } from '../../../type';
import type { CodeRelation, Services } from '../../../type';
import { CodeDeleteErrorType, CodeDslItem, CodeEditorMode, ListState } from '../../../type';
import codeBlockEditor from './CodeBlockEditor.vue';
const props = defineProps<{
customError?: (id: string, errorType: CodeDeleteErrorType) => any;
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
}>();
const services = inject<Services>('services');
//
const state = reactive<ListRelationState>({
const state = reactive<ListState>({
codeList: [],
bindComps: {},
});
const editable = computed(() => services?.codeBlockService.getEditStatus());
//
const isShowCodeBlockEditor = computed(() => services?.codeBlockService.getCodeEditorShowStatus() || false);
//
const codeCombineInfo = ref<CodeRelation | null>(null);
// ID
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),
const getBindCompsByCodeId = (codeId: Id) => {
if (!codeCombineInfo.value || !codeCombineInfo.value[codeId]) return [];
const bindCompsId = Object.keys(codeCombineInfo.value[codeId]);
return bindCompsId.map((compId) => ({
compId,
compName: getCompName(compId),
}));
state.bindComps[codeId] = compsInfo;
};
//
const initList = async () => {
const codeDsl = (await services?.codeBlockService.getCodeDsl()) || null;
if (!codeDsl) return;
const codeDsl = cloneDeep(await services?.codeBlockService.getCodeDsl()) || null;
codeCombineInfo.value = cloneDeep(services?.codeBlockService.getCombineInfo()) || null;
if (!codeDsl || !codeCombineInfo.value) return;
state.codeList = [];
forIn(codeDsl, (value: CodeBlockContent, codeId: string) => {
getBindCompsByCodeId(codeId);
forIn(codeDsl, (value: CodeBlockContent, codeId: Id) => {
state.codeList.push({
id: codeId,
name: value.name,
codeBlockContent: value,
showRelation: true,
combineInfo: getBindCompsByCodeId(codeId),
});
});
};
watch(
() => services?.codeBlockService.getCodeDsl(),
[() => services?.codeBlockService.getCodeDsl(), () => services?.codeBlockService.refreshCombineInfo()],
() => {
initList();
},
@ -164,21 +155,6 @@ watch(
},
);
//
watch(
() => services?.editorService.get('node'),
(curNode) => {
if (!curNode?.id) return;
forIn(state.bindComps, (bindCompInfo) => {
bindCompInfo.forEach((comp) => {
if (comp.id === curNode.id) {
comp.name = curNode.name;
}
});
});
},
);
//
const createCodeBlock = async () => {
const { codeBlockService } = services || {};
@ -197,14 +173,15 @@ const createCodeBlock = async () => {
};
//
const editCode = async (key: string) => {
const editCode = async (key: Id) => {
await services?.codeBlockService.setMode(CodeEditorMode.EDITOR);
services?.codeBlockService.setCodeEditorContent(true, key);
};
//
const deleteCode = (key: string) => {
const existBinds = !!(state.bindComps[key]?.length > 0);
const deleteCode = (key: Id) => {
const currentCode = state.codeList.find((codeItem: CodeDslItem) => codeItem.id === key);
const existBinds = !isEmpty(currentCode?.combineInfo);
const undeleteableList = services?.codeBlockService.getUndeletableList() || [];
if (!existBinds && !undeleteableList.includes(key)) {
//
@ -221,7 +198,7 @@ const deleteCode = (key: string) => {
const filterText = ref('');
const tree = ref();
const filterNode = (value: string, data: CodeDslList): boolean => {
const filterNode = (value: string, data: CodeDslItem): boolean => {
if (!value) {
return true;
}
@ -233,7 +210,7 @@ const filterTextChangeHandler = (val: string) => {
};
// /
const toggleCombineRelation = (data: CodeDslList) => {
const toggleCombineRelation = (data: CodeDslItem) => {
const { id } = data;
const currentCode = state.codeList.find((item) => item.id === id);
if (!currentCode) return;
@ -246,17 +223,6 @@ const getCompName = (compId: Id): string => {
return node?.name || String(compId);
};
// todo
//
// const unbind = async (compId: Id, codeId: string) => {
// const res = await services?.codeBlockService.unbind(compId, codeId, codeHooks);
// if (res) {
// ElMessage.success('');
// } else {
// ElMessage.error('');
// }
// };
//
const selectComp = (compId: Id) => {
const stage = services?.editorService.get<StageCore | null>('stage');

View File

@ -23,8 +23,8 @@ import { CodeBlockContent, CodeBlockDSL, HookType, Id, MApp, MNode } from '@tmag
import editorService from '../services/editor';
import type { CodeRelation, CodeState, HookData } from '../type';
import { CODE_DRAFT_STORAGE_KEY, CodeEditorMode, CodeSelectOp } from '../type';
import { error, info } from '../utils/logger';
import { CODE_DRAFT_STORAGE_KEY, CodeEditorMode } from '../type';
import { info } from '../utils/logger';
import BaseService from './BaseService';
@ -242,84 +242,23 @@ class CodeBlock extends BaseService {
}
/**
*
* @param {Id} id
* @param {string[]} diffCodeIds id数组
* @param {CodeSelectOp} opFlag
* @param {string} hook hook名称
*
* @returns {void}
*/
public async setCombineRelation(compId: Id, diffCodeIds: string[], opFlag: CodeSelectOp, hook: string) {
const combineInfo = this.getCombineInfo();
if (!combineInfo) return;
if (opFlag === CodeSelectOp.DELETE) {
try {
diffCodeIds.forEach((codeId) => {
const compsContent = combineInfo[codeId];
const index = compsContent?.[compId].findIndex((item) => item === hook);
if (typeof index !== 'undefined' && index !== -1) {
compsContent?.[compId].splice(index, 1);
}
});
} catch (e) {
error(e);
throw new Error('解绑代码块失败');
}
} else if (opFlag === CodeSelectOp.ADD) {
try {
diffCodeIds.forEach((codeId) => {
const compsContent = combineInfo[codeId];
const existHooks = compsContent?.[compId];
if (isEmpty(existHooks)) {
// comps属性不存在或者comps为空新增
combineInfo[codeId] = {
...(combineInfo[codeId] || {}),
[compId]: [hook],
};
} else {
// 往已有的关系中添加hook
existHooks?.push(hook);
}
});
} catch (e) {
error(e);
throw new Error('绑定代码块失败');
}
} else if (opFlag === CodeSelectOp.CHANGE) {
// 单选修改
forIn(combineInfo, (combineItem, codeId) => {
if (codeId === diffCodeIds[0]) {
// 增加
combineItem = {
...(combineItem || {}),
[compId]: [hook],
};
} else if (isEmpty(diffCodeIds) || codeId !== diffCodeIds[0]) {
// 清空或者移除之前的选项
const compHooks = combineItem?.[compId];
// continue
if (!compHooks) return true;
const index = compHooks.findIndex((hookName) => hookName === hook);
if (index !== -1) {
compHooks.splice(index, 1);
// break
return false;
}
}
});
}
console.log('---combineInfo--', combineInfo);
console.log('---this.state.relations--', this.state.relations);
public refreshCombineInfo(): CodeRelation | null {
const root = editorService.get<MApp | null>('root');
if (!root) return null;
const relations = {};
this.recurseMNode(root, relations);
this.state.relations = relations;
return this.state.relations;
}
/**
*
* @returns {CodeRelation | null}
* @returns {CodeRelation}
*/
public getCombineInfo(): CodeRelation | null {
const root = editorService.get<MApp | null>('root');
if (!root) return null;
this.recurseMNode(root);
public getCombineInfo(): CodeRelation {
return this.state.relations;
}
@ -429,17 +368,19 @@ class CodeBlock extends BaseService {
/**
* dsl中挂载了代码块的节点
* @param {MNode} node
* @param {MContainer} node
* @returns void
*/
private recurseMNode(node: MNode) {
private recurseMNode(node: MNode, relations: CodeRelation) {
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] = {};
if (value?.hookType === HookType.CODE && !isEmpty(value.hookData)) {
value.hookData.forEach((relationItem: HookData) => {
// continue
if (!relationItem.codeId) return;
if (!relations[relationItem.codeId]) {
relations[relationItem.codeId] = {};
}
const codeItem = this.state.relations[relationItem.codeId];
const codeItem = relations[relationItem.codeId];
if (isEmpty(codeItem[node.id])) {
codeItem[node.id] = [];
}
@ -449,7 +390,7 @@ class CodeBlock extends BaseService {
});
if (!isEmpty(node.items)) {
node.items.forEach((item: MNode) => {
this.recurseMNode(item);
this.recurseMNode(item, relations);
});
}
}

View File

@ -17,7 +17,7 @@
*/
import { reactive, toRaw } from 'vue';
import { cloneDeep, mergeWith, uniq } from 'lodash-es';
import { cloneDeep, isObject, mergeWith, uniq } from 'lodash-es';
import type { CodeBlockDSL, Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
import { NodeType } from '@tmagic/schema';
@ -459,6 +459,11 @@ class Editor extends BaseService {
let newConfig = await this.toggleFixedPosition(toRaw(config), node, this.get<MApp>('root'));
newConfig = mergeWith(cloneDeep(node), newConfig, (objValue, srcValue) => {
if (isObject(srcValue) && Array.isArray(objValue)) {
// 原来的配置是数组,新的配置是对象,则直接使用新的值
console.log('--srcValue-', srcValue);
return srcValue;
}
if (Array.isArray(srcValue)) {
return srcValue;
}
@ -521,7 +526,7 @@ class Editor extends BaseService {
this.pushHistoryState();
this.emit('update', newNodes);
codeBlockService.refreshCombineInfo();
return Array.isArray(config) ? newNodes : newNodes[0];
}

View File

@ -349,7 +349,7 @@ export enum CodeEditorMode {
EDITOR = 'editor',
}
export interface CodeDslList {
export interface CodeDslItem {
/** 代码块id */
id: Id;
/** 代码块名称 */
@ -358,19 +358,20 @@ export interface CodeDslList {
codeBlockContent?: CodeBlockContent;
/** 是否展示代码绑定关系 */
showRelation?: boolean;
/** 代码块对应绑定的组件信息 */
combineInfo?: CombineInfo[];
}
export interface CombineInfo {
/** 组件id */
compId: Id;
/** 组件名称 */
compName: string;
}
export interface ListState {
/** 代码块列表 */
codeList: CodeDslList[];
}
export interface ListRelationState extends ListState {
/** 与代码块绑定的组件信息 */
bindComps: {
/** 代码块id : 组件信息 */
[id: Id]: MNode[];
};
codeList: CodeDslItem[];
}
export enum CodeDeleteErrorType {
@ -380,14 +381,5 @@ export enum CodeDeleteErrorType {
BIND = 'bind',
}
export enum CodeSelectOp {
/** 增加 */
ADD = 'add',
/** 删除 */
DELETE = 'delete',
/** 单选修改 */
CHANGE = 'change',
}
// 代码块草稿localStorage key
export const CODE_DRAFT_STORAGE_KEY = 'magicCodeDraft';

View File

@ -226,14 +226,14 @@ export const fillConfig = (config: FormConfig = []) => [
{
name: 'created',
text: 'created',
type: 'code-select',
labelWidth: '100px',
type: 'code-select',
},
{
name: 'mounted',
text: 'mounted',
type: 'code-select',
labelWidth: '100px',
type: 'code-select',
},
],
},

View File

@ -59,7 +59,7 @@ export default {
events: [],
created: {
hookType: 'code',
data: [
hookData: [
{
codeId: 'code_5336',
params: {
@ -74,7 +74,7 @@ export default {
},
mounted: {
hookType: 'code',
data: [
hookData: [
{
codeId: 'code_5316',
},
@ -106,7 +106,7 @@ export default {
events: [],
created: {
hookType: 'code',
data: [
hookData: [
{
codeId: 'code_5316',
},