@tmagic/editor 1.2.10 → 1.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,216 @@
1
+ <template>
2
+ <div class="m-fields-event-select">
3
+ <m-form-container
4
+ v-if="isOldVersion"
5
+ ref="eventForm"
6
+ :size="props.size"
7
+ :model="model"
8
+ :config="tableConfig"
9
+ @change="onChangeHandler"
10
+ ></m-form-container>
11
+
12
+ <div v-else class="fullWidth">
13
+ <TMagicButton class="create-button" type="primary" size="small" @click="addEvent()">添加事件</TMagicButton>
14
+ <m-form-panel
15
+ v-for="(cardItem, index) in model[name]"
16
+ :key="index"
17
+ :config="actionsConfig"
18
+ :model="cardItem"
19
+ @change="onChangeHandler"
20
+ >
21
+ <template #header>
22
+ <m-form-container
23
+ class="fullWidth"
24
+ :config="eventNameConfig"
25
+ :model="cardItem"
26
+ @change="onChangeHandler"
27
+ ></m-form-container>
28
+ <TMagicButton style="color: #f56c6c" text :icon="Delete" @click="removeEvent(index)"></TMagicButton>
29
+ </template>
30
+ </m-form-panel>
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <script lang="ts" setup name="MEditorEventSelect">
36
+ import { computed, defineProps, inject } from 'vue';
37
+ import { Delete } from '@element-plus/icons-vue';
38
+ import { has } from 'lodash-es';
39
+
40
+ import { TMagicButton } from '@tmagic/design';
41
+ import { FormState } from '@tmagic/form';
42
+ import { ActionType } from '@tmagic/schema';
43
+
44
+ import { EventSelectConfig, Services } from '../type';
45
+ const services = inject<Services>('services');
46
+
47
+ const props = defineProps<{
48
+ config: EventSelectConfig;
49
+ model: any;
50
+ prop: string;
51
+ name: string;
52
+ size: 'small' | 'default' | 'large';
53
+ }>();
54
+ const emit = defineEmits(['change']);
55
+
56
+ // 事件名称下拉框表单配置
57
+ const eventNameConfig = computed(() => {
58
+ const defaultEventNameConfig = {
59
+ name: 'name',
60
+ text: '事件',
61
+ type: 'select',
62
+ labelWidth: '40px',
63
+ options: (mForm: FormState, { formValue }: any) =>
64
+ services?.eventsService.getEvent(formValue.type).map((option: any) => ({
65
+ text: option.label,
66
+ value: option.value,
67
+ })),
68
+ };
69
+ return { ...defaultEventNameConfig, ...props.config.eventNameConfig };
70
+ });
71
+
72
+ // 联动类型
73
+ const actionTypeConfig = computed(() => {
74
+ const defaultActionTypeConfig = {
75
+ name: 'actionType',
76
+ text: '联动类型',
77
+ labelWidth: '70px',
78
+ type: 'select',
79
+ defaultValue: ActionType.COMP,
80
+ options: () => [
81
+ {
82
+ text: '组件',
83
+ label: '组件',
84
+ value: ActionType.COMP,
85
+ },
86
+ {
87
+ text: '代码',
88
+ label: '代码',
89
+ value: ActionType.CODE,
90
+ },
91
+ ],
92
+ };
93
+ return { ...defaultActionTypeConfig, ...props.config.actionTypeConfig };
94
+ });
95
+
96
+ // 联动组件配置
97
+ const targetCompConfig = computed(() => {
98
+ const defaultTargetCompConfig = {
99
+ name: 'to',
100
+ text: '联动组件',
101
+ labelWidth: '70px',
102
+ type: 'ui-select',
103
+ display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
104
+ };
105
+ return { ...defaultTargetCompConfig, ...props.config.targetCompConfig };
106
+ });
107
+
108
+ // 联动组件动作配置
109
+ const compActionConfig = computed(() => {
110
+ const defaultCompActionConfig = {
111
+ name: 'method',
112
+ text: '动作',
113
+ labelWidth: '70px',
114
+ type: 'select',
115
+ display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
116
+ options: (mForm: FormState, { model }: any) => {
117
+ const node = services?.editorService.getNodeById(model.to);
118
+ if (!node?.type) return [];
119
+
120
+ return services?.eventsService.getMethod(node.type).map((option: any) => ({
121
+ text: option.label,
122
+ value: option.value,
123
+ }));
124
+ },
125
+ };
126
+ return { ...defaultCompActionConfig, ...props.config.compActionConfig };
127
+ });
128
+
129
+ // 代码联动配置
130
+ const codeActionConfig = computed(() => {
131
+ const defaultCodeActionConfig = {
132
+ type: 'code-select-col',
133
+ display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.CODE,
134
+ };
135
+ return { ...defaultCodeActionConfig, ...props.config.codeActionConfig };
136
+ });
137
+
138
+ // 兼容旧的数据格式
139
+ const tableConfig = computed(() => ({
140
+ type: 'table',
141
+ name: 'events',
142
+ items: [
143
+ {
144
+ name: 'name',
145
+ label: '事件名',
146
+ type: eventNameConfig.value.type,
147
+ options: (mForm: FormState, { formValue }: any) =>
148
+ services?.eventsService.getEvent(formValue.type).map((option: any) => ({
149
+ text: option.label,
150
+ value: option.value,
151
+ })),
152
+ },
153
+ {
154
+ name: 'to',
155
+ label: '联动组件',
156
+ type: 'ui-select',
157
+ },
158
+ {
159
+ name: 'method',
160
+ label: '动作',
161
+ type: compActionConfig.value.type,
162
+ options: (mForm: FormState, { model }: any) => {
163
+ const node = services?.editorService.getNodeById(model.to);
164
+ if (!node?.type) return [];
165
+
166
+ return services?.eventsService.getMethod(node.type).map((option: any) => ({
167
+ text: option.label,
168
+ value: option.value,
169
+ }));
170
+ },
171
+ },
172
+ ],
173
+ }));
174
+
175
+ // 组件动作组表单配置
176
+ const actionsConfig = computed(() => ({
177
+ items: [
178
+ {
179
+ type: 'group-list',
180
+ name: 'actions',
181
+ enableToggleMode: false,
182
+ items: [actionTypeConfig.value, targetCompConfig.value, compActionConfig.value, codeActionConfig.value],
183
+ },
184
+ ],
185
+ }));
186
+
187
+ // 是否为旧的数据格式
188
+ const isOldVersion = computed(() => {
189
+ if (props.model[props.name].length === 0) return false;
190
+ return !has(props.model[props.name][0], 'actions');
191
+ });
192
+
193
+ // 添加事件
194
+ const addEvent = () => {
195
+ const defaultEvent = {
196
+ name: '',
197
+ actions: [],
198
+ };
199
+ if (!props.model[props.name]) {
200
+ props.model[props.name] = [];
201
+ }
202
+ props.model[props.name].push(defaultEvent);
203
+ onChangeHandler();
204
+ };
205
+
206
+ // 删除事件
207
+ const removeEvent = (index: number) => {
208
+ if (!props.name) return;
209
+ props.model[props.name].splice(index, 1);
210
+ onChangeHandler();
211
+ };
212
+
213
+ const onChangeHandler = () => {
214
+ emit('change', props.model);
215
+ };
216
+ </script>
package/src/index.ts CHANGED
@@ -20,6 +20,8 @@ import { App } from 'vue';
20
20
  import Code from './fields/Code.vue';
21
21
  import CodeLink from './fields/CodeLink.vue';
22
22
  import CodeSelect from './fields/CodeSelect.vue';
23
+ import CodeSelectCol from './fields/CodeSelectCol.vue';
24
+ import EventSelect from './fields/EventSelect.vue';
23
25
  import uiSelect from './fields/UISelect.vue';
24
26
  import CodeEditor from './layouts/CodeEditor.vue';
25
27
  import { setConfig } from './utils/config';
@@ -40,9 +42,12 @@ export { default as storageService } from './services/storage';
40
42
  export { default as eventsService } from './services/events';
41
43
  export { default as uiService } from './services/ui';
42
44
  export { default as codeBlockService } from './services/codeBlock';
45
+ export { default as depService } from './services/dep';
43
46
  export { default as ComponentListPanel } from './layouts/sidebar/ComponentListPanel.vue';
44
47
  export { default as LayerPanel } from './layouts/sidebar/LayerPanel.vue';
45
48
  export { default as CodeSelect } from './fields/CodeSelect.vue';
49
+ export { default as CodeSelectCol } from './fields/CodeSelectCol.vue';
50
+ export { default as EventSelect } from './fields/EventSelect.vue';
46
51
  export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
47
52
  export { default as PropsPanel } from './layouts/PropsPanel.vue';
48
53
  export { default as ToolButton } from './components/ToolButton.vue';
@@ -67,5 +72,7 @@ export default {
67
72
  app.component('m-fields-vs-code', Code);
68
73
  app.component('magic-code-editor', CodeEditor);
69
74
  app.component('m-fields-code-select', CodeSelect);
75
+ app.component('m-fields-code-select-col', CodeSelectCol);
76
+ app.component('m-fields-event-select', EventSelect);
70
77
  },
71
78
  };
@@ -0,0 +1,198 @@
1
+ import type { ExtractPropTypes } from 'vue';
2
+ import { onUnmounted, toRaw, watch } from 'vue';
3
+
4
+ import type { EventOption } from '@tmagic/core';
5
+ import type { CodeBlockContent, Id, MApp, MNode, MPage } from '@tmagic/schema';
6
+
7
+ import { createCodeBlockTarget } from './utils/dep';
8
+ import editorProps from './editorProps';
9
+ import type { Services } from './type';
10
+
11
+ export declare type LooseRequired<T> = {
12
+ [P in string & keyof T]: T[P];
13
+ };
14
+
15
+ export const initServiceState = (
16
+ props: Readonly<LooseRequired<Readonly<ExtractPropTypes<typeof editorProps>>>>,
17
+ {
18
+ editorService,
19
+ historyService,
20
+ componentListService,
21
+ propsService,
22
+ eventsService,
23
+ uiService,
24
+ codeBlockService,
25
+ }: Services,
26
+ ) => {
27
+ // 初始值变化,重新设置节点信息
28
+ watch(
29
+ () => props.modelValue,
30
+ (modelValue) => {
31
+ editorService.set('root', modelValue);
32
+ },
33
+ {
34
+ immediate: true,
35
+ },
36
+ );
37
+
38
+ watch(
39
+ () => props.componentGroupList,
40
+ (componentGroupList) => componentListService.setList(componentGroupList),
41
+ {
42
+ immediate: true,
43
+ },
44
+ );
45
+
46
+ watch(
47
+ () => props.propsConfigs,
48
+ (configs) => propsService.setPropsConfigs(configs),
49
+ {
50
+ immediate: true,
51
+ },
52
+ );
53
+
54
+ watch(
55
+ () => props.propsValues,
56
+ (values) => propsService.setPropsValues(values),
57
+ {
58
+ immediate: true,
59
+ },
60
+ );
61
+
62
+ watch(
63
+ () => props.eventMethodList,
64
+ (eventMethodList) => {
65
+ const eventsList: Record<string, EventOption[]> = {};
66
+ const methodsList: Record<string, EventOption[]> = {};
67
+
68
+ Object.keys(eventMethodList).forEach((type: string) => {
69
+ eventsList[type] = eventMethodList[type].events;
70
+ methodsList[type] = eventMethodList[type].methods;
71
+ });
72
+
73
+ eventsService.setEvents(eventsList);
74
+ eventsService.setMethods(methodsList);
75
+ },
76
+ {
77
+ immediate: true,
78
+ },
79
+ );
80
+
81
+ watch(
82
+ () => props.defaultSelected,
83
+ (defaultSelected) => defaultSelected && editorService.select(defaultSelected),
84
+ {
85
+ immediate: true,
86
+ },
87
+ );
88
+
89
+ watch(
90
+ () => props.stageRect,
91
+ (stageRect) => stageRect && uiService.set('stageRect', stageRect),
92
+ {
93
+ immediate: true,
94
+ },
95
+ );
96
+
97
+ onUnmounted(() => {
98
+ editorService.resetState();
99
+ historyService.resetState();
100
+ propsService.resetState();
101
+ uiService.resetState();
102
+ componentListService.resetState();
103
+ codeBlockService.resetState();
104
+ });
105
+ };
106
+
107
+ export const initServiceEvents = (
108
+ props: Readonly<LooseRequired<Readonly<ExtractPropTypes<typeof editorProps>>>>,
109
+ emit: (event: 'props-panel-mounted' | 'update:modelValue', ...args: any[]) => void,
110
+ { editorService, codeBlockService, depService }: Services,
111
+ ) => {
112
+ const rootChangeHandler = async (value: MApp, preValue?: MApp | null) => {
113
+ const nodeId = editorService.get('node')?.id || props.defaultSelected;
114
+ let node;
115
+ if (nodeId) {
116
+ node = editorService.getNodeById(nodeId);
117
+ }
118
+
119
+ if (node && node !== value) {
120
+ await editorService.select(node.id);
121
+ } else if (value?.items?.length) {
122
+ await editorService.select(value.items[0]);
123
+ } else if (value?.id) {
124
+ editorService.set('nodes', [value]);
125
+ editorService.set('parent', null);
126
+ editorService.set('page', null);
127
+ }
128
+
129
+ if (toRaw(value) !== toRaw(preValue)) {
130
+ emit('update:modelValue', value);
131
+ }
132
+
133
+ value.codeBlocks = value.codeBlocks || {};
134
+
135
+ codeBlockService.setCodeDsl(value.codeBlocks);
136
+
137
+ depService.removeTargets('code-block');
138
+
139
+ Object.entries(value.codeBlocks).forEach(([id, code]) => {
140
+ depService.addTarget(createCodeBlockTarget(id, code));
141
+ });
142
+
143
+ if (value && Array.isArray(value.items)) {
144
+ depService.collect(value.items, true);
145
+ } else {
146
+ depService.clear();
147
+ }
148
+ };
149
+
150
+ const nodeAddHandler = (nodes: MNode[]) => {
151
+ depService.collect(nodes);
152
+ };
153
+
154
+ const nodeUpdateHandler = (nodes: MNode[]) => {
155
+ depService.collect(nodes);
156
+ };
157
+
158
+ const nodeRemoveHandler = (nodes: MNode[]) => {
159
+ depService.clear(nodes);
160
+ };
161
+
162
+ const historyChangeHandler = (page: MPage) => {
163
+ depService.collect([page], true);
164
+ };
165
+
166
+ editorService.on('history-change', historyChangeHandler);
167
+ editorService.on('root-change', rootChangeHandler);
168
+ editorService.on('add', nodeAddHandler);
169
+ editorService.on('remove', nodeRemoveHandler);
170
+ editorService.on('update', nodeUpdateHandler);
171
+
172
+ const codeBlockAddOrUpdateHandler = (id: Id, codeBlock: CodeBlockContent) => {
173
+ if (depService.hasTarget(id)) {
174
+ depService.getTarget(id)!.name = codeBlock.name;
175
+ return;
176
+ }
177
+
178
+ depService.addTarget(createCodeBlockTarget(id, codeBlock));
179
+ };
180
+
181
+ const codeBlockRemoveHandler = (id: Id) => {
182
+ depService.removeTarget(id);
183
+ };
184
+
185
+ codeBlockService.on('addOrUpdate', codeBlockAddOrUpdateHandler);
186
+ codeBlockService.on('remove', codeBlockRemoveHandler);
187
+
188
+ onUnmounted(() => {
189
+ editorService.off('history-change', historyChangeHandler);
190
+ editorService.off('root-change', rootChangeHandler);
191
+ editorService.off('add', nodeAddHandler);
192
+ editorService.off('remove', nodeRemoveHandler);
193
+ editorService.off('update', nodeUpdateHandler);
194
+
195
+ codeBlockService.off('addOrUpdate', codeBlockAddOrUpdateHandler);
196
+ codeBlockService.off('remove', codeBlockRemoveHandler);
197
+ });
198
+ };