@tmagic/editor 1.2.14 → 1.3.0-alpha.0

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.
Files changed (59) hide show
  1. package/dist/style.css +173 -80
  2. package/dist/tmagic-editor.js +975 -222
  3. package/dist/tmagic-editor.js.map +1 -1
  4. package/dist/tmagic-editor.umd.cjs +984 -230
  5. package/dist/tmagic-editor.umd.cjs.map +1 -1
  6. package/package.json +10 -9
  7. package/src/Editor.vue +2 -0
  8. package/src/components/CodeDraftEditor.vue +2 -2
  9. package/src/components/SearchInput.vue +6 -3
  10. package/src/editorProps.ts +5 -0
  11. package/src/fields/CodeSelect.vue +1 -0
  12. package/src/fields/DataSourceFields.vue +178 -0
  13. package/src/fields/EventSelect.vue +1 -0
  14. package/src/fields/KeyValue.vue +93 -0
  15. package/src/icons/AppManageIcon.vue +1 -1
  16. package/src/icons/CodeIcon.vue +1 -10
  17. package/src/index.ts +6 -0
  18. package/src/initService.ts +59 -3
  19. package/src/layouts/sidebar/LayerPanel.vue +2 -0
  20. package/src/layouts/sidebar/Sidebar.vue +24 -14
  21. package/src/layouts/sidebar/code-block/CodeBlockList.vue +11 -4
  22. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +84 -0
  23. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +130 -0
  24. package/src/services/codeBlock.ts +0 -2
  25. package/src/services/dataSource.ts +90 -0
  26. package/src/services/props.ts +2 -15
  27. package/src/theme/code-block.scss +4 -66
  28. package/src/theme/data-source-fields.scss +13 -0
  29. package/src/theme/data-source.scss +17 -0
  30. package/src/theme/dep-list.scss +59 -0
  31. package/src/theme/key-value.scss +13 -0
  32. package/src/theme/sidebar.scss +26 -6
  33. package/src/theme/theme.scss +4 -0
  34. package/src/type.ts +3 -1
  35. package/src/utils/data-source/formConfigs/base.ts +32 -0
  36. package/src/utils/data-source/formConfigs/http.ts +50 -0
  37. package/src/utils/data-source/index.ts +31 -0
  38. package/src/utils/dep.ts +9 -1
  39. package/src/utils/props.ts +1 -0
  40. package/types/Editor.vue.d.ts +9 -0
  41. package/types/components/ContentMenu.vue.d.ts +2 -2
  42. package/types/components/ScrollViewer.vue.d.ts +1 -1
  43. package/types/editorProps.d.ts +4 -0
  44. package/types/fields/DataSourceFields.vue.d.ts +40 -0
  45. package/types/fields/KeyValue.vue.d.ts +50 -0
  46. package/types/index.d.ts +2 -0
  47. package/types/initService.d.ts +1 -1
  48. package/types/layouts/CodeEditor.vue.d.ts +1 -1
  49. package/types/layouts/sidebar/Sidebar.vue.d.ts +2 -2
  50. package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +22 -0
  51. package/types/layouts/sidebar/data-source/DataSourceListPanel.vue.d.ts +12 -0
  52. package/types/services/dataSource.d.ts +25 -0
  53. package/types/services/props.d.ts +17 -12
  54. package/types/type.d.ts +3 -1
  55. package/types/utils/data-source/formConfigs/base.d.ts +3 -0
  56. package/types/utils/data-source/formConfigs/http.d.ts +3 -0
  57. package/types/utils/data-source/index.d.ts +2 -0
  58. package/types/utils/dep.d.ts +2 -1
  59. package/types/utils/props.d.ts +2 -3
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.14",
2
+ "version": "1.3.0-alpha.0",
3
3
  "name": "@tmagic/editor",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -46,12 +46,13 @@
46
46
  "dependencies": {
47
47
  "@babel/core": "^7.18.0",
48
48
  "@element-plus/icons-vue": "^2.0.9",
49
- "@tmagic/core": "1.2.14",
50
- "@tmagic/design": "1.2.14",
51
- "@tmagic/form": "1.2.14",
52
- "@tmagic/schema": "1.2.14",
53
- "@tmagic/stage": "1.2.14",
54
- "@tmagic/utils": "1.2.14",
49
+ "@tmagic/core": "1.3.0-alpha.0",
50
+ "@tmagic/design": "1.3.0-alpha.0",
51
+ "@tmagic/form": "1.3.0-alpha.0",
52
+ "@tmagic/schema": "1.3.0-alpha.0",
53
+ "@tmagic/stage": "1.3.0-alpha.0",
54
+ "@tmagic/table": "1.3.0-alpha.0",
55
+ "@tmagic/utils": "1.3.0-alpha.0",
55
56
  "buffer": "^6.0.3",
56
57
  "color": "^3.1.3",
57
58
  "events": "^3.3.0",
@@ -63,8 +64,8 @@
63
64
  "vue": "^3.2.37"
64
65
  },
65
66
  "peerDependencies": {
66
- "@tmagic/design": "1.2.14",
67
- "@tmagic/form": "1.2.14",
67
+ "@tmagic/design": "1.3.0-alpha.0",
68
+ "@tmagic/form": "1.3.0-alpha.0",
68
69
  "monaco-editor": "^0.34.0",
69
70
  "vue": "^3.2.37"
70
71
  },
package/src/Editor.vue CHANGED
@@ -73,6 +73,7 @@ import Sidebar from './layouts/sidebar/Sidebar.vue';
73
73
  import Workspace from './layouts/workspace/Workspace.vue';
74
74
  import codeBlockService from './services/codeBlock';
75
75
  import componentListService from './services/componentList';
76
+ import dataSourceService from './services/dataSource';
76
77
  import depService from './services/dep';
77
78
  import editorService from './services/editor';
78
79
  import eventsService from './services/events';
@@ -110,6 +111,7 @@ export default defineComponent({
110
111
  storageService,
111
112
  codeBlockService,
112
113
  depService,
114
+ dataSourceService,
113
115
  };
114
116
 
115
117
  initServiceEvents(props, emit, services);
@@ -13,13 +13,13 @@
13
13
  {{ isFullScreen ? '退出全屏' : '全屏' }}</TMagicButton
14
14
  >
15
15
  <TMagicButton type="primary" class="button" @click="saveAndClose">确认</TMagicButton>
16
- <TMagicButton type="primary" class="button" @click="close">关闭</TMagicButton>
16
+ <TMagicButton class="button" @click="close">关闭</TMagicButton>
17
17
  </div>
18
18
  <div class="m-editor-content-bottom" v-else>
19
19
  <TMagicButton type="primary" class="button" @click="toggleFullScreen">
20
20
  {{ isFullScreen ? '退出全屏' : '全屏' }}</TMagicButton
21
21
  >
22
- <TMagicButton type="primary" class="button" @click="close">关闭</TMagicButton>
22
+ <TMagicButton class="button" @click="close">关闭</TMagicButton>
23
23
  </div>
24
24
  </div>
25
25
  </template>
@@ -5,16 +5,19 @@
5
5
  size="small"
6
6
  placeholder="输入关键字进行过滤"
7
7
  clearable
8
- :prefix-icon="Search"
9
8
  @input="filterTextChangeHandler"
10
- ></TMagicInput>
9
+ >
10
+ <template #prefix>
11
+ <TMagicIcon><Search /></TMagicIcon>
12
+ </template>
13
+ </TMagicInput>
11
14
  </template>
12
15
 
13
16
  <script setup lang="ts">
14
17
  import { ref } from 'vue';
15
18
  import { Search } from '@element-plus/icons-vue';
16
19
 
17
- import { TMagicInput } from '@tmagic/design';
20
+ import { TMagicIcon, TMagicInput } from '@tmagic/design';
18
21
 
19
22
  const emit = defineEmits(['search']);
20
23
 
@@ -79,6 +79,11 @@ export default {
79
79
  default: () => ({}),
80
80
  },
81
81
 
82
+ dataScourceConfigs: {
83
+ type: Object as PropType<Record<string, FormConfig>>,
84
+ default: () => ({}),
85
+ },
86
+
82
87
  /** 画布中组件选中框的移动范围 */
83
88
  moveableOptions: {
84
89
  type: [Object, Function] as PropType<
@@ -35,6 +35,7 @@ const codeConfig = computed(() => ({
35
35
  items: [
36
36
  {
37
37
  type: 'code-select-col',
38
+ labelWidth: 0,
38
39
  },
39
40
  ],
40
41
  }));
@@ -0,0 +1,178 @@
1
+ <template>
2
+ <div class="m-editor-data-source-fields">
3
+ <MagicTable :data="model[name]" :columns="filedColumns"></MagicTable>
4
+
5
+ <div class="m-editor-data-source-fields-footer">
6
+ <TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
7
+ </div>
8
+
9
+ <MFormDialog
10
+ ref="addDialog"
11
+ :title="filedTitle"
12
+ :config="dataSourceFieldsConfig"
13
+ :values="fieldValues"
14
+ :parentValues="model[name]"
15
+ @submit="fieldChange"
16
+ ></MFormDialog>
17
+ </div>
18
+ </template>
19
+
20
+ <script setup lang="ts">
21
+ import { ref } from 'vue';
22
+
23
+ import { TMagicButton, tMagicMessageBox } from '@tmagic/design';
24
+ import { FormConfig, FormState, MFormDialog } from '@tmagic/form';
25
+ import { MagicTable } from '@tmagic/table';
26
+
27
+ const props = withDefaults(
28
+ defineProps<{
29
+ config: {
30
+ type: 'data-source-fields';
31
+ };
32
+ model: any;
33
+ prop: string;
34
+ disabled: boolean;
35
+ name: string;
36
+ }>(),
37
+ {
38
+ disabled: false,
39
+ },
40
+ );
41
+
42
+ const emit = defineEmits(['change']);
43
+
44
+ const addDialog = ref<InstanceType<typeof MFormDialog>>();
45
+ const fieldValues = ref<Record<string, any>>({});
46
+ const filedTitle = ref('');
47
+
48
+ const newHandler = () => {
49
+ if (!addDialog.value) return;
50
+ fieldValues.value = {};
51
+ filedTitle.value = '新增属性';
52
+ addDialog.value.dialogVisible = true;
53
+ };
54
+
55
+ const fieldChange = ({ index, ...value }: Record<string, any>) => {
56
+ if (!addDialog.value) return;
57
+
58
+ if (index > -1) {
59
+ props.model[props.name][index] = value;
60
+ } else {
61
+ props.model[props.name].push(value);
62
+ }
63
+
64
+ addDialog.value.dialogVisible = false;
65
+
66
+ emit('change', props.model[props.name]);
67
+ };
68
+
69
+ const filedColumns = [
70
+ {
71
+ label: '属性名称',
72
+ prop: 'title',
73
+ },
74
+ {
75
+ label: '属性key',
76
+ prop: 'name',
77
+ },
78
+ {
79
+ label: '属性描述',
80
+ prop: 'desc',
81
+ },
82
+ {
83
+ label: '操作',
84
+ fixed: 'right',
85
+ actions: [
86
+ {
87
+ text: '编辑',
88
+ handler: (row: Record<string, any>, index: number) => {
89
+ if (!addDialog.value) return;
90
+ fieldValues.value = {
91
+ ...row,
92
+ index,
93
+ };
94
+ filedTitle.value = `编辑${row.title}`;
95
+ addDialog.value.dialogVisible = true;
96
+ },
97
+ },
98
+ {
99
+ text: '删除',
100
+ buttonType: 'danger',
101
+ handler: async (row: Record<string, any>, index: number) => {
102
+ await tMagicMessageBox.confirm(`确定删除${row.title}(${row.name})?`, '提示');
103
+ props.model[props.name].splice(index, 1);
104
+ emit('change', props.model[props.name]);
105
+ },
106
+ },
107
+ ],
108
+ },
109
+ ];
110
+
111
+ const dataSourceFieldsConfig: FormConfig = [
112
+ { name: 'index', type: 'hidden', filter: 'number', defaultValue: -1 },
113
+ {
114
+ name: 'type',
115
+ text: '数据类型',
116
+ type: 'select',
117
+ defaultValue: 'string',
118
+ options: [
119
+ { text: '字符串', value: 'string' },
120
+ { text: '数字', value: 'number' },
121
+ { text: '布尔值', value: 'boolean' },
122
+ { text: '对象', value: 'object' },
123
+ { text: '数组', value: 'array' },
124
+ { text: 'null', value: 'null' },
125
+ { text: 'any', value: 'any' },
126
+ ],
127
+ },
128
+ {
129
+ name: 'name',
130
+ text: '字段名称',
131
+ rules: [
132
+ {
133
+ required: true,
134
+ message: '请输入字段名称',
135
+ },
136
+ {
137
+ validator: ({ value, callback }, { model, parent }) => {
138
+ const index = parent.findIndex((item: Record<string, any>) => item.name === value);
139
+ if ((model.index === -1 && index > -1) || (model.index > -1 && index !== model.index)) {
140
+ return callback(`属性key(${value})已存在`);
141
+ }
142
+ callback();
143
+ },
144
+ },
145
+ ],
146
+ },
147
+ {
148
+ name: 'title',
149
+ text: '展示名称',
150
+ rules: [
151
+ {
152
+ required: true,
153
+ message: '请输入展示名称',
154
+ },
155
+ ],
156
+ },
157
+ {
158
+ name: 'description',
159
+ text: '描述',
160
+ },
161
+ {
162
+ name: 'defaultValue',
163
+ text: '默认值',
164
+ },
165
+ {
166
+ name: 'enable',
167
+ text: '是否可用',
168
+ type: 'switch',
169
+ defaultValue: true,
170
+ },
171
+ {
172
+ name: 'fields',
173
+ type: 'data-source-fields',
174
+ defaultValue: [],
175
+ display: (formState: FormState | undefined, { model }: any) => model.type === 'object',
176
+ },
177
+ ];
178
+ </script>
@@ -132,6 +132,7 @@ const compActionConfig = computed(() => {
132
132
  const codeActionConfig = computed(() => {
133
133
  const defaultCodeActionConfig = {
134
134
  type: 'code-select-col',
135
+ labelWidth: 0,
135
136
  display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.CODE,
136
137
  };
137
138
  return { ...defaultCodeActionConfig, ...props.config.codeActionConfig };
@@ -0,0 +1,93 @@
1
+ <template>
2
+ <div class="m-fields-key-value">
3
+ <div class="m-fields-key-value-item" v-for="(item, index) in records" :key="index">
4
+ <TMagicInput
5
+ placeholder="key"
6
+ v-model="records[index][0]"
7
+ :disabled="disabled"
8
+ :size="size"
9
+ @change="keyChangeHandler"
10
+ ></TMagicInput>
11
+ <span class="m-fileds-key-value-delimiter">:</span>
12
+ <TMagicInput
13
+ placeholder="value"
14
+ v-model="records[index][1]"
15
+ :disabled="disabled"
16
+ :size="size"
17
+ @change="valueChangeHandler"
18
+ ></TMagicInput>
19
+
20
+ <TMagicButton
21
+ class="m-fileds-key-value-delete"
22
+ type="danger"
23
+ :size="size"
24
+ circle
25
+ plain
26
+ :icon="Delete"
27
+ @click="deleteHandler(index)"
28
+ ></TMagicButton>
29
+ </div>
30
+
31
+ <TMagicButton type="primary" :size="size" plain :icon="Plus" @click="addHandler">添加</TMagicButton>
32
+ </div>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import { ref, watchEffect } from 'vue';
37
+ import { Delete, Plus } from '@element-plus/icons-vue';
38
+
39
+ import { TMagicButton, TMagicInput } from '@tmagic/design';
40
+
41
+ const props = withDefaults(
42
+ defineProps<{
43
+ config: {
44
+ type: 'key-value';
45
+ name: string;
46
+ text: string;
47
+ };
48
+ model: Record<string, any>;
49
+ name: string;
50
+ prop: string;
51
+ disabled: boolean;
52
+ lastValues?: Record<string, any>;
53
+ size?: 'large' | 'default' | 'small';
54
+ }>(),
55
+ {
56
+ disabled: false,
57
+ },
58
+ );
59
+
60
+ const emit = defineEmits<(e: 'change', value: Record<string, any>) => void>();
61
+
62
+ const records = ref<[string, string][]>([]);
63
+
64
+ watchEffect(() => {
65
+ records.value = Object.entries(props.model[props.name] || {});
66
+ });
67
+
68
+ const getValue = () => {
69
+ const record: Record<string, string> = {};
70
+ records.value.forEach(([key, value]) => {
71
+ if (key) {
72
+ record[key] = value;
73
+ }
74
+ });
75
+ return record;
76
+ };
77
+
78
+ const keyChangeHandler = () => {
79
+ emit('change', getValue());
80
+ };
81
+
82
+ const valueChangeHandler = () => {
83
+ emit('change', getValue());
84
+ };
85
+
86
+ const addHandler = () => {
87
+ records.value.push(['', '']);
88
+ };
89
+
90
+ const deleteHandler = (index: number) => {
91
+ records.value.splice(index, 1);
92
+ };
93
+ </script>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <!-- app-manage cdn链接:https://cloudcache.tencent-cloud.com/qcloud/ui/static/console_aside_v4/6638c8a5-2e7f-477a-83b1-a413a0e4ba39.svg -->
3
- <svg width="16px" height="16px" xmlns="http://www.w3.org/2000/svg">
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
4
  <g fill="#7C878E" fill-rule="evenodd">
5
5
  <path d="M15.3,5.5 L8,0 L0.7,5.5 L8,11 L15.3,5.5 Z M8,2.5 L12,5.5 L8,8.5 L4,5.5 L8,2.5 Z" fill-rule="nonzero" />
6
6
  <path d="M8 13.5L2.3 9.2 0.7 10.5 8 16 15.3 10.5 13.7 9.2z" />
@@ -1,16 +1,7 @@
1
1
  <template>
2
2
  <!-- 代码icon,cdn链接:https://cloudcache.tencent-cloud.com/qcloud/ui/static/government/0d463ed5-6407-4498-8865-3d05b5e70115.svg -->
3
- <svg
4
- width="32px"
5
- height="32px"
6
- viewBox="0 0 32 32"
7
- version="1.1"
8
- xmlns="http://www.w3.org/2000/svg"
9
- xmlns:xlink="http://www.w3.org/1999/xlink"
10
- >
3
+ <svg viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
11
4
  <!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
12
- <title>gsd-icon-line-Universal-code</title>
13
- <desc>Created with Sketch.</desc>
14
5
  <defs><rect id="path-1" x="0" y="0" width="32" height="32"></rect></defs>
15
6
  <g id="组件规范" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
16
7
  <g id="03图标" transform="translate(-561.000000, -2356.000000)">
package/src/index.ts CHANGED
@@ -21,7 +21,9 @@ import Code from './fields/Code.vue';
21
21
  import CodeLink from './fields/CodeLink.vue';
22
22
  import CodeSelect from './fields/CodeSelect.vue';
23
23
  import CodeSelectCol from './fields/CodeSelectCol.vue';
24
+ import DataSourceFields from './fields/DataSourceFields.vue';
24
25
  import EventSelect from './fields/EventSelect.vue';
26
+ import KeyValue from './fields/KeyValue.vue';
25
27
  import uiSelect from './fields/UISelect.vue';
26
28
  import CodeEditor from './layouts/CodeEditor.vue';
27
29
  import { setConfig } from './utils/config';
@@ -47,7 +49,9 @@ export { default as ComponentListPanel } from './layouts/sidebar/ComponentListPa
47
49
  export { default as LayerPanel } from './layouts/sidebar/LayerPanel.vue';
48
50
  export { default as CodeSelect } from './fields/CodeSelect.vue';
49
51
  export { default as CodeSelectCol } from './fields/CodeSelectCol.vue';
52
+ export { default as DataSourceFields } from './fields/DataSourceFields.vue';
50
53
  export { default as EventSelect } from './fields/EventSelect.vue';
54
+ export { default as KeyValue } from './fields/KeyValue.vue';
51
55
  export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
52
56
  export { default as PropsPanel } from './layouts/PropsPanel.vue';
53
57
  export { default as ToolButton } from './components/ToolButton.vue';
@@ -74,5 +78,7 @@ export default {
74
78
  app.component('m-fields-code-select', CodeSelect);
75
79
  app.component('m-fields-code-select-col', CodeSelectCol);
76
80
  app.component('m-fields-event-select', EventSelect);
81
+ app.component('m-fields-data-source-fields', DataSourceFields);
82
+ app.component('m-fields-key-value', KeyValue);
77
83
  },
78
84
  };
@@ -2,9 +2,10 @@ import type { ExtractPropTypes } from 'vue';
2
2
  import { onUnmounted, toRaw, watch } from 'vue';
3
3
 
4
4
  import type { EventOption } from '@tmagic/core';
5
- import type { CodeBlockContent, Id, MApp, MNode, MPage } from '@tmagic/schema';
5
+ import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage } from '@tmagic/schema';
6
6
 
7
- import { createCodeBlockTarget } from './utils/dep';
7
+ import type { Target } from './services/dep';
8
+ import { createCodeBlockTarget, createDataSourceTarget } from './utils/dep';
8
9
  import editorProps from './editorProps';
9
10
  import type { Services } from './type';
10
11
 
@@ -107,8 +108,31 @@ export const initServiceState = (
107
108
  export const initServiceEvents = (
108
109
  props: Readonly<LooseRequired<Readonly<ExtractPropTypes<typeof editorProps>>>>,
109
110
  emit: (event: 'props-panel-mounted' | 'update:modelValue', ...args: any[]) => void,
110
- { editorService, codeBlockService, depService }: Services,
111
+ { editorService, codeBlockService, dataSourceService, depService }: Services,
111
112
  ) => {
113
+ const targetAddHandler = (target: Target) => {
114
+ if (target.type !== 'data-source') return;
115
+
116
+ const root = editorService.get('root');
117
+ if (!root) return;
118
+
119
+ if (!root.dataSourceDeps) {
120
+ root.dataSourceDeps = {};
121
+ }
122
+
123
+ root.dataSourceDeps[target.id] = target.deps;
124
+ };
125
+
126
+ const targetRemoveHandler = (id: string | number) => {
127
+ const root = editorService.get('root');
128
+ if (!root?.dataSourceDeps) return;
129
+
130
+ delete root.dataSourceDeps[id];
131
+ };
132
+
133
+ depService.on('add-target', targetAddHandler);
134
+ depService.on('remove-target', targetRemoveHandler);
135
+
112
136
  const rootChangeHandler = async (value: MApp, preValue?: MApp | null) => {
113
137
  const nodeId = editorService.get('node')?.id || props.defaultSelected;
114
138
  let node;
@@ -131,8 +155,10 @@ export const initServiceEvents = (
131
155
  }
132
156
 
133
157
  value.codeBlocks = value.codeBlocks || {};
158
+ value.dataSources = value.dataSources || [];
134
159
 
135
160
  codeBlockService.setCodeDsl(value.codeBlocks);
161
+ dataSourceService.set('dataSources', value.dataSources);
136
162
 
137
163
  depService.removeTargets('code-block');
138
164
 
@@ -140,10 +166,15 @@ export const initServiceEvents = (
140
166
  depService.addTarget(createCodeBlockTarget(id, code));
141
167
  });
142
168
 
169
+ value.dataSources.forEach((ds) => {
170
+ depService.addTarget(createDataSourceTarget(ds.id, ds));
171
+ });
172
+
143
173
  if (value && Array.isArray(value.items)) {
144
174
  depService.collect(value.items, true);
145
175
  } else {
146
176
  depService.clear();
177
+ delete value.dataSourceDeps;
147
178
  }
148
179
  };
149
180
 
@@ -185,7 +216,28 @@ export const initServiceEvents = (
185
216
  codeBlockService.on('addOrUpdate', codeBlockAddOrUpdateHandler);
186
217
  codeBlockService.on('remove', codeBlockRemoveHandler);
187
218
 
219
+ const dataSourceAddHandler = (config: DataSourceSchema) => {
220
+ depService.addTarget(createDataSourceTarget(config.id, config));
221
+ };
222
+
223
+ const dataSourceUpdateHandler = (config: DataSourceSchema) => {
224
+ if (config.title) {
225
+ depService.getTarget(config.id)!.name = config.title;
226
+ }
227
+ };
228
+
229
+ const dataSourceRemoveHandler = (id: string) => {
230
+ depService.removeTarget(id);
231
+ };
232
+
233
+ dataSourceService.on('add', dataSourceAddHandler);
234
+ dataSourceService.on('update', dataSourceUpdateHandler);
235
+ dataSourceService.on('remove', dataSourceRemoveHandler);
236
+
188
237
  onUnmounted(() => {
238
+ depService.off('add-target', targetAddHandler);
239
+ depService.off('remove-target', targetRemoveHandler);
240
+
189
241
  editorService.off('history-change', historyChangeHandler);
190
242
  editorService.off('root-change', rootChangeHandler);
191
243
  editorService.off('add', nodeAddHandler);
@@ -194,5 +246,9 @@ export const initServiceEvents = (
194
246
 
195
247
  codeBlockService.off('addOrUpdate', codeBlockAddOrUpdateHandler);
196
248
  codeBlockService.off('remove', codeBlockRemoveHandler);
249
+
250
+ dataSourceService.off('add', dataSourceAddHandler);
251
+ dataSourceService.off('update', dataSourceUpdateHandler);
252
+ dataSourceService.off('remove', dataSourceRemoveHandler);
197
253
  });
198
254
  };
@@ -87,6 +87,8 @@ const clicked = ref(false);
87
87
 
88
88
  const treeProps = {
89
89
  children: 'items',
90
+ label: 'name',
91
+ value: 'id',
90
92
  disabled: (data: MNode) => Boolean(data.items?.length),
91
93
  class: (data: MNode) => {
92
94
  if (clicked.value || isPage(data)) return '';
@@ -1,16 +1,16 @@
1
1
  <template>
2
- <TMagicTabs
2
+ <component
3
3
  v-if="data.type === 'tabs' && data.items.length"
4
- class="m-editor-sidebar"
5
4
  v-model="activeTabName"
6
- type="card"
7
- tab-position="left"
5
+ class="m-editor-sidebar tmagic-design-tabs"
6
+ v-bind="tabsComponent.props({ type: 'card', tabPosition: 'left' })"
7
+ :is="tabsComponent.component"
8
8
  >
9
9
  <component
10
- :is="uiComponent.component"
11
10
  v-for="(config, index) in sideBarItems"
11
+ v-bind="tabPaneComponent.props({ name: config.text })"
12
+ :is="tabPaneComponent.component"
12
13
  :key="config.$key || index"
13
- :name="config.text"
14
14
  >
15
15
  <template #label>
16
16
  <div :key="config.text">
@@ -80,34 +80,36 @@
80
80
  </template>
81
81
  </component>
82
82
  </component>
83
- </TMagicTabs>
83
+ </component>
84
84
  </template>
85
85
 
86
86
  <script lang="ts" setup name="MEditorSidebar">
87
87
  import { computed, ref, watch } from 'vue';
88
- import { Coin, EditPen, Files } from '@element-plus/icons-vue';
88
+ import { Coin, EditPen, Goods, List } from '@element-plus/icons-vue';
89
89
 
90
- import { getConfig, TMagicTabs } from '@tmagic/design';
90
+ import { getConfig } from '@tmagic/design';
91
91
 
92
92
  import MIcon from '@editor/components/Icon.vue';
93
93
  import type { MenuButton, MenuComponent, SideComponent, SideItem } from '@editor/type';
94
94
  import { SideBarData } from '@editor/type';
95
95
 
96
96
  import CodeBlockList from './code-block/CodeBlockList.vue';
97
+ import DataSourceListPanel from './data-source/DataSourceListPanel.vue';
97
98
  import ComponentListPanel from './ComponentListPanel.vue';
98
99
  import LayerPanel from './LayerPanel.vue';
99
100
 
100
101
  const props = withDefaults(
101
102
  defineProps<{
102
- data?: SideBarData;
103
+ data: SideBarData;
103
104
  layerContentMenu: (MenuButton | MenuComponent)[];
104
105
  }>(),
105
106
  {
106
- data: () => ({ type: 'tabs', status: '组件', items: ['component-list', 'layer', 'code-block'] }),
107
+ data: () => ({ type: 'tabs', status: '组件', items: ['component-list', 'layer', 'code-block', 'data-source'] }),
107
108
  },
108
109
  );
109
110
 
110
- const uiComponent = getConfig('components').tabPane;
111
+ const tabPaneComponent = getConfig('components').tabPane;
112
+ const tabsComponent = getConfig('components').tabs;
111
113
 
112
114
  const activeTabName = ref(props.data?.status);
113
115
 
@@ -116,7 +118,7 @@ const getItemConfig = (data: SideItem): SideComponent => {
116
118
  'component-list': {
117
119
  $key: 'component-list',
118
120
  type: 'component',
119
- icon: Coin,
121
+ icon: Goods,
120
122
  text: '组件',
121
123
  component: ComponentListPanel,
122
124
  slots: {},
@@ -124,7 +126,7 @@ const getItemConfig = (data: SideItem): SideComponent => {
124
126
  layer: {
125
127
  $key: 'layer',
126
128
  type: 'component',
127
- icon: Files,
129
+ icon: List,
128
130
  text: '已选组件',
129
131
  props: {
130
132
  layerContentMenu: props.layerContentMenu,
@@ -140,6 +142,14 @@ const getItemConfig = (data: SideItem): SideComponent => {
140
142
  component: CodeBlockList,
141
143
  slots: {},
142
144
  },
145
+ 'data-source': {
146
+ $key: 'data-source',
147
+ type: 'component',
148
+ icon: Coin,
149
+ text: '数据源',
150
+ component: DataSourceListPanel,
151
+ slots: {},
152
+ },
143
153
  };
144
154
 
145
155
  return typeof data === 'string' ? map[data] : data;