@tmagic/editor 1.2.0-beta.11 → 1.2.0-beta.13

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.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.0-beta.11",
2
+ "version": "1.2.0-beta.13",
3
3
  "name": "@tmagic/editor",
4
4
  "sideEffects": [
5
5
  "dist/*",
@@ -45,12 +45,12 @@
45
45
  "dependencies": {
46
46
  "@babel/core": "^7.18.0",
47
47
  "@element-plus/icons-vue": "^2.0.9",
48
- "@tmagic/core": "1.2.0-beta.11",
49
- "@tmagic/design": "1.2.0-beta.11",
50
- "@tmagic/form": "1.2.0-beta.11",
51
- "@tmagic/schema": "1.2.0-beta.11",
52
- "@tmagic/stage": "1.2.0-beta.11",
53
- "@tmagic/utils": "1.2.0-beta.11",
48
+ "@tmagic/core": "1.2.0-beta.13",
49
+ "@tmagic/design": "1.2.0-beta.13",
50
+ "@tmagic/form": "1.2.0-beta.13",
51
+ "@tmagic/schema": "1.2.0-beta.13",
52
+ "@tmagic/stage": "1.2.0-beta.13",
53
+ "@tmagic/utils": "1.2.0-beta.13",
54
54
  "buffer": "^6.0.3",
55
55
  "color": "^3.1.3",
56
56
  "events": "^3.3.0",
@@ -62,8 +62,8 @@
62
62
  "vue": "^3.2.37"
63
63
  },
64
64
  "peerDependencies": {
65
- "@tmagic/design": "1.2.0-beta.11",
66
- "@tmagic/form": "1.2.0-beta.11",
65
+ "@tmagic/design": "1.2.0-beta.13",
66
+ "@tmagic/form": "1.2.0-beta.13",
67
67
  "monaco-editor": "^0.34.0",
68
68
  "vue": "^3.2.37"
69
69
  },
package/src/Editor.vue CHANGED
@@ -319,6 +319,7 @@ export default defineComponent({
319
319
 
320
320
  provide('services', services);
321
321
 
322
+ provide('codeOptions', props.codeOptions);
322
323
  provide(
323
324
  'stageOptions',
324
325
  reactive({
@@ -0,0 +1,130 @@
1
+ <template>
2
+ <div class="m-editor-wrapper">
3
+ <magic-code-editor
4
+ ref="codeEditor"
5
+ class="m-editor-container"
6
+ :init-values="`${codeContent}`"
7
+ @save="saveCodeDraft"
8
+ :language="language"
9
+ :options="codeOptions"
10
+ ></magic-code-editor>
11
+ <div class="m-editor-content-bottom" v-if="editable">
12
+ <TMagicButton type="primary" class="button" @click="saveCode">保存</TMagicButton>
13
+ <TMagicButton type="primary" class="button" @click="close">关闭</TMagicButton>
14
+ </div>
15
+ <div class="m-editor-content-bottom" v-else>
16
+ <TMagicButton type="primary" class="button" @click="close">关闭</TMagicButton>
17
+ </div>
18
+ </div>
19
+ </template>
20
+ <script lang="ts" setup name="MEditorCodeDraftEditor">
21
+ import { computed, inject, ref, watchEffect } from 'vue';
22
+ import type * as monaco from 'monaco-editor';
23
+
24
+ import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
25
+ import { Id } from '@tmagic/schema';
26
+ import { datetimeFormatter } from '@tmagic/utils';
27
+
28
+ import MagicCodeEditor from '../layouts/CodeEditor.vue';
29
+ import type { Services } from '../type';
30
+
31
+ const props = withDefaults(
32
+ defineProps<{
33
+ /** 代码id */
34
+ id: Id;
35
+ /** 代码内容 */
36
+ content: string;
37
+ /** 是否可编辑 */
38
+ editable?: boolean;
39
+ /** 是否自动保存草稿 */
40
+ autoSaveDraft?: boolean;
41
+ /** 编辑器参数 */
42
+ codeOptions?: Object;
43
+ /** 编辑器语言 */
44
+ language?: string;
45
+ }>(),
46
+ {
47
+ editable: true,
48
+ autoSaveDraft: true,
49
+ },
50
+ );
51
+ const emit = defineEmits(['save', 'close', 'saveAndClose']);
52
+
53
+ const services = inject<Services>('services');
54
+
55
+ const codeContent = ref<string>('');
56
+ const editorContent = ref<string>('');
57
+ const codeEditor = ref<InstanceType<typeof MagicCodeEditor>>();
58
+ // 原始代码内容
59
+ const originCodeContent = ref<string>('');
60
+
61
+ const codeOptions = computed(() => ({
62
+ ...props.codeOptions,
63
+ readOnly: !props.editable,
64
+ }));
65
+
66
+ watchEffect(() => {
67
+ codeContent.value = props.content;
68
+ if (!originCodeContent.value) {
69
+ // 暂存原始的代码内容
70
+ originCodeContent.value = codeContent.value;
71
+ }
72
+ // 有草稿时展示上次保存的草稿内容
73
+ const codeDraft = services?.codeBlockService.getCodeDraft(props.id);
74
+ if (codeDraft) {
75
+ codeContent.value = codeDraft;
76
+ }
77
+ });
78
+
79
+ // 保存草稿
80
+ const saveCodeDraft = async (codeValue: string) => {
81
+ if (!props.autoSaveDraft) return;
82
+ if (originCodeContent.value === codeValue) {
83
+ // 没修改或改回原样 有草稿的话删除草稿
84
+ services?.codeBlockService.removeCodeDraft(props.id);
85
+ return;
86
+ }
87
+ services?.codeBlockService.setCodeDraft(props.id, codeValue);
88
+ tMagicMessage.success(`代码草稿保存成功 ${datetimeFormatter(new Date())}`);
89
+ };
90
+
91
+ // 保存代码
92
+ const saveCode = (): void => {
93
+ if (!codeEditor.value || !props.editable) return;
94
+ // 代码内容
95
+ editorContent.value = (codeEditor.value.getEditor() as monaco.editor.IStandaloneCodeEditor)?.getValue();
96
+ emit('save', editorContent.value);
97
+ };
98
+
99
+ // 保存并关闭
100
+ const saveAndClose = (): void => {
101
+ if (!codeEditor.value || !props.editable) return;
102
+ // 代码内容
103
+ editorContent.value = (codeEditor.value.getEditor() as monaco.editor.IStandaloneCodeEditor)?.getValue();
104
+ emit('saveAndClose', editorContent.value);
105
+ };
106
+
107
+ // 关闭弹窗
108
+ const close = async () => {
109
+ const codeDraft = services?.codeBlockService.getCodeDraft(props.id);
110
+ if (codeDraft) {
111
+ tMagicMessageBox
112
+ .confirm('您有代码修改未保存,是否保存后再关闭?', '提示', {
113
+ confirmButtonText: '确认',
114
+ cancelButtonText: '取消',
115
+ type: 'warning',
116
+ })
117
+ .then(async () => {
118
+ // 保存之后再关闭
119
+ saveAndClose();
120
+ })
121
+ .catch(() => {
122
+ // 删除草稿 直接关闭
123
+ services?.codeBlockService.removeCodeDraft(props.id);
124
+ emit('close');
125
+ });
126
+ } else {
127
+ emit('close');
128
+ }
129
+ };
130
+ </script>
@@ -0,0 +1,144 @@
1
+ <template>
2
+ <TMagicCard shadow="never">
3
+ <template #header>
4
+ <div class="code-name-wrapper">
5
+ <div class="code-name-label">代码块名称</div>
6
+ <TMagicInput class="code-name-input" v-model="codeName" :disabled="!editable" />
7
+ </div>
8
+ <div class="code-name-wrapper">
9
+ <div class="code-name-label">参数定义</div>
10
+ <m-form-table
11
+ style="width: 320px"
12
+ :config="tableConfig"
13
+ :model="tableModel"
14
+ :enableToggleMode="false"
15
+ name="params"
16
+ prop="params"
17
+ size="small"
18
+ >
19
+ </m-form-table>
20
+ </div>
21
+ </template>
22
+ <CodeDraftEditor
23
+ :id="id"
24
+ :content="codeContent"
25
+ :editable="editable"
26
+ :autoSaveDraft="autoSaveDraft"
27
+ :codeOptions="codeOptions"
28
+ language="javascript"
29
+ @save="saveCode"
30
+ @saveAndClose="saveAndClose"
31
+ @close="close"
32
+ ></CodeDraftEditor>
33
+ </TMagicCard>
34
+ </template>
35
+ <script lang="ts" setup name="MEditorFunctionEditor">
36
+ import { inject, provide, ref, watchEffect } from 'vue';
37
+
38
+ import { TMagicCard, TMagicInput, tMagicMessage } from '@tmagic/design';
39
+ import { CodeParam, Id } from '@tmagic/schema';
40
+
41
+ import type { Services } from '../type';
42
+
43
+ import CodeDraftEditor from './CodeDraftEditor.vue';
44
+
45
+ const tableConfig = {
46
+ border: true,
47
+ enableFullscreen: false,
48
+ name: 'params',
49
+ items: [
50
+ {
51
+ type: 'text',
52
+ label: '参数名',
53
+ name: 'name',
54
+ },
55
+ ],
56
+ };
57
+
58
+ const props = withDefaults(
59
+ defineProps<{
60
+ id: Id;
61
+ name: string;
62
+ content: string;
63
+ editable?: boolean;
64
+ autoSaveDraft?: boolean;
65
+ codeOptions?: object;
66
+ }>(),
67
+ {
68
+ editable: true,
69
+ autoSaveDraft: true,
70
+ },
71
+ );
72
+
73
+ const emit = defineEmits(['change', 'field-input']);
74
+
75
+ const services = inject<Services>('services');
76
+
77
+ const codeName = ref<string>('');
78
+ const codeContent = ref<string>('');
79
+ const evalRes = ref(true);
80
+
81
+ provide('mForm', {
82
+ $emit: emit,
83
+ setField: () => {},
84
+ });
85
+
86
+ const tableModel = ref<{ params: CodeParam[] }>();
87
+ watchEffect(() => {
88
+ codeName.value = props.name;
89
+ codeContent.value = props.content;
90
+ });
91
+
92
+ const initTableModel = () => {
93
+ const codeDsl = services?.codeBlockService.getCodeDslSync();
94
+ if (!codeDsl) return;
95
+ tableModel.value = {
96
+ params: codeDsl[props.id]?.params || [],
97
+ };
98
+ };
99
+
100
+ initTableModel();
101
+
102
+ // 保存前钩子
103
+ const beforeSave = (codeValue: string): boolean => {
104
+ try {
105
+ // eval检测js代码是否存在语法错误
106
+ // eslint-disable-next-line no-eval
107
+ eval(codeValue);
108
+ return true;
109
+ } catch (e: any) {
110
+ tMagicMessage.error(e.stack);
111
+ return false;
112
+ }
113
+ };
114
+
115
+ // 保存代码
116
+ const saveCode = async (codeValue: string): Promise<void> => {
117
+ if (!props.editable) return;
118
+ evalRes.value = beforeSave(codeValue);
119
+ if (evalRes.value) {
120
+ // 存入dsl
121
+ await services?.codeBlockService.setCodeDslById(props.id, {
122
+ name: codeName.value,
123
+ content: codeValue,
124
+ params: tableModel.value?.params || [],
125
+ });
126
+ tMagicMessage.success('代码保存成功');
127
+ // 删除草稿
128
+ services?.codeBlockService.removeCodeDraft(props.id);
129
+ }
130
+ };
131
+
132
+ // 保存并关闭
133
+ const saveAndClose = async (codeValue: string) => {
134
+ await saveCode(codeValue);
135
+ if (evalRes.value) {
136
+ close();
137
+ }
138
+ };
139
+
140
+ // 关闭弹窗
141
+ const close = () => {
142
+ services?.codeBlockService.setCodeEditorShowStatus(false);
143
+ };
144
+ </script>
@@ -1,131 +1,110 @@
1
1
  <template>
2
- <div class="m-fields-code-select" :key="fieldKey">
3
- <TMagicCard shadow="never">
4
- <template #header>
5
- <m-fields-select
6
- :config="selectConfig"
7
- :model="model"
8
- :prop="prop"
9
- :name="name"
10
- :size="size"
11
- @change="changeHandler"
12
- ></m-fields-select>
13
- </template>
14
- <div class="tool-bar">
15
- <TMagicTooltip class="tool-item" effect="dark" content="查看代码块" placement="top">
16
- <svg
17
- @click="viewHandler"
18
- preserveAspectRatio="xMidYMid meet"
19
- viewBox="0 0 24 24"
20
- width="15px"
21
- height="15px"
22
- data-v-65a7fb6c=""
23
- >
24
- <path
25
- fill="currentColor"
26
- 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"
27
- ></path>
28
- </svg>
29
- </TMagicTooltip>
30
- </div>
31
- </TMagicCard>
2
+ <div class="m-fields-code-select">
3
+ <m-form-table
4
+ :config="tableConfig"
5
+ :model="model[name]"
6
+ name="hookData"
7
+ :enableToggleMode="false"
8
+ :prop="prop"
9
+ :size="size"
10
+ @change="changeHandler"
11
+ >
12
+ </m-form-table>
32
13
  </div>
33
14
  </template>
34
15
 
35
16
  <script lang="ts" setup name="MEditorCodeSelect">
36
- import { computed, defineEmits, defineProps, inject, ref, watchEffect } from 'vue';
37
- import { map, xor } from 'lodash-es';
17
+ import { computed, defineEmits, defineProps, inject, watch } from 'vue';
18
+ import { isEmpty, map } from 'lodash-es';
38
19
 
39
- import { TMagicCard, tMagicMessage, TMagicTooltip } from '@tmagic/design';
40
- import { FormState, SelectConfig } from '@tmagic/form';
20
+ import { FormItem, TableConfig } from '@tmagic/form';
21
+ import { HookType, Id } from '@tmagic/schema';
41
22
 
42
- import type { Services } from '../type';
43
- import { CodeEditorMode, CodeSelectOp } from '../type';
23
+ import { CodeParamStatement, HookData, Services } from '../type';
44
24
  const services = inject<Services>('services');
45
- const form = inject<FormState>('mForm');
46
25
  const emit = defineEmits(['change']);
47
26
 
48
27
  const props = defineProps<{
49
28
  config: {
50
- selectConfig?: SelectConfig;
29
+ tableConfig?: TableConfig;
51
30
  };
52
31
  model: any;
53
32
  prop: string;
54
33
  name: string;
55
- size: string;
34
+ size: 'mini' | 'small' | 'medium';
56
35
  }>();
36
+ const codeDsl = computed(() => services?.codeBlockService.getCodeDslSync());
57
37
 
58
- const selectConfig = computed(() => {
38
+ const tableConfig = computed<FormItem>(() => {
59
39
  const defaultConfig = {
60
- multiple: true,
61
- options: async () => {
62
- const codeDsl = await services?.codeBlockService.getCodeDsl();
63
- if (codeDsl) {
64
- return map(codeDsl, (value, key) => ({
65
- text: `${value.name}(${key})`,
66
- label: `${value.name}(${key})`,
67
- value: key,
68
- }));
69
- }
70
- return [];
71
- },
40
+ dropSort: true,
41
+ enableFullscreen: false,
42
+ border: true,
43
+ items: [
44
+ {
45
+ type: 'select',
46
+ label: '代码块',
47
+ name: 'codeId',
48
+ width: '200px',
49
+ options: () => {
50
+ if (codeDsl.value) {
51
+ return map(codeDsl.value, (value, key) => ({
52
+ text: `${value.name}(${key})`,
53
+ label: `${value.name}(${key})`,
54
+ value: key,
55
+ }));
56
+ }
57
+ return [];
58
+ },
59
+ onChange: (formState: any, codeId: Id, { model }: any) => {
60
+ // 参数的items是根据函数生成的,当codeId变化后修正model的值,避免写入其他codeId的params
61
+ model.params = {};
62
+ },
63
+ },
64
+ {
65
+ name: 'params',
66
+ label: '参数',
67
+ defaultValue: {},
68
+ itemsFunction: (row: HookData) => getParamsConfig(row.codeId),
69
+ },
70
+ ],
72
71
  };
73
72
  return {
74
73
  ...defaultConfig,
75
- ...props.config.selectConfig,
74
+ ...props.config.tableConfig,
76
75
  };
77
76
  });
78
- const fieldKey = ref('');
79
- const multiple = ref(true);
80
- const lastTagSnapshot = ref<string[]>([]);
81
77
 
82
- watchEffect(async () => {
83
- if (!props.model[props.name]) return;
84
- const combineNames = await Promise.all(
85
- props.model[props.name].map(async (id: string) => {
86
- const { name = '' } = (await services?.codeBlockService.getCodeContentById(id)) || {};
87
- return name;
88
- }),
89
- );
90
- fieldKey.value = combineNames.join('-');
91
- });
92
-
93
- const changeHandler = async (value: any) => {
94
- let codeIds = value;
95
- if (typeof value === 'string') {
96
- multiple.value = false;
97
- codeIds = value ? [value] : [];
98
- }
99
- await setCombineRelation(codeIds);
100
- emit('change', value);
101
- };
102
-
103
- // 同步绑定关系
104
- const setCombineRelation = async (codeIds: string[]) => {
105
- // 组件id
106
- const { id = '' } = services?.editorService.get('node') || {};
78
+ watch(
79
+ () => props.model[props.name],
80
+ (value) => {
81
+ // 兼容旧的数据结构
82
+ if (isEmpty(value)) {
83
+ // 空值或者空数组
84
+ props.model[props.name] = {
85
+ hookType: HookType.CODE,
86
+ hookData: [],
87
+ };
88
+ }
89
+ },
90
+ {
91
+ immediate: true,
92
+ },
93
+ );
107
94
 
108
- // 兼容单选
109
- let opFlag = CodeSelectOp.CHANGE;
110
- let diffValues = codeIds;
111
- if (multiple.value) {
112
- // initValues为表单初始值,当表单内容发生变化时,initValues也会更新,可以理解为上一次表单内容的快照
113
- lastTagSnapshot.value = form?.initValues[props.name] || [];
114
- opFlag = codeIds.length < lastTagSnapshot.value.length ? CodeSelectOp.DELETE : CodeSelectOp.ADD;
115
- diffValues = xor(codeIds, lastTagSnapshot.value) as string[];
116
- }
117
- // 记录绑定关系
118
- await services?.codeBlockService.setCombineRelation(id, diffValues, opFlag, props.prop);
95
+ const changeHandler = async () => {
96
+ emit('change', props.model[props.name]);
119
97
  };
120
98
 
121
- const viewHandler = async () => {
122
- if (props.model[props.name].length === 0) {
123
- tMagicMessage.error('请先绑定代码块');
124
- return;
125
- }
126
- // 记录当前已被绑定的代码块,为查看弹窗的展示内容
127
- await services?.codeBlockService.setCombineIds(props.model[props.name]);
128
- await services?.codeBlockService.setMode(CodeEditorMode.LIST);
129
- services?.codeBlockService.setCodeEditorContent(true, props.model[props.name][0]);
99
+ const getParamsConfig = (codeId: Id) => {
100
+ if (!codeDsl.value) return [];
101
+ const paramStatements = codeDsl.value[codeId]?.params;
102
+ if (isEmpty(paramStatements)) return [];
103
+ return paramStatements.map((paramState: CodeParamStatement) => ({
104
+ name: paramState.name,
105
+ text: paramState.name,
106
+ labelWidth: '100px',
107
+ type: 'text',
108
+ }));
130
109
  };
131
110
  </script>
@@ -12,7 +12,7 @@ const props = withDefaults(
12
12
  defineProps<{
13
13
  initValues?: string | Object;
14
14
  modifiedValues?: string | Object;
15
- type?: string;
15
+ type?: 'diff';
16
16
  language?: string;
17
17
  options?: {
18
18
  [key: string]: any;
@@ -20,7 +20,6 @@ const props = withDefaults(
20
20
  autoSave?: boolean;
21
21
  }>(),
22
22
  {
23
- type: '',
24
23
  autoSave: true,
25
24
  language: 'javascript',
26
25
  options: () => ({