@tmagic/editor 1.3.0-alpha.8 → 1.3.0-alpha.9

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.3.0-alpha.8",
2
+ "version": "1.3.0-alpha.9",
3
3
  "name": "@tmagic/editor",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -46,13 +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.3.0-alpha.8",
50
- "@tmagic/design": "1.3.0-alpha.8",
51
- "@tmagic/form": "1.3.0-alpha.8",
52
- "@tmagic/schema": "1.3.0-alpha.8",
53
- "@tmagic/stage": "1.3.0-alpha.8",
54
- "@tmagic/table": "1.3.0-alpha.8",
55
- "@tmagic/utils": "1.3.0-alpha.8",
49
+ "@tmagic/core": "1.3.0-alpha.9",
50
+ "@tmagic/design": "1.3.0-alpha.9",
51
+ "@tmagic/form": "1.3.0-alpha.9",
52
+ "@tmagic/schema": "1.3.0-alpha.9",
53
+ "@tmagic/stage": "1.3.0-alpha.9",
54
+ "@tmagic/table": "1.3.0-alpha.9",
55
+ "@tmagic/utils": "1.3.0-alpha.9",
56
56
  "buffer": "^6.0.3",
57
57
  "color": "^3.1.3",
58
58
  "events": "^3.3.0",
@@ -64,8 +64,8 @@
64
64
  "vue": "^3.3.4"
65
65
  },
66
66
  "peerDependencies": {
67
- "@tmagic/design": "1.3.0-alpha.8",
68
- "@tmagic/form": "1.3.0-alpha.8",
67
+ "@tmagic/design": "1.3.0-alpha.9",
68
+ "@tmagic/form": "1.3.0-alpha.9",
69
69
  "monaco-editor": "^0.39.0",
70
70
  "vue": "^3.3.4"
71
71
  },
@@ -49,7 +49,7 @@ defineOptions({
49
49
  name: 'MEditorFunctionEditor',
50
50
  });
51
51
 
52
- provide('mForm', {});
52
+ provide('mForm', null);
53
53
 
54
54
  const defaultParamColConfig: ColumnConfig = {
55
55
  type: 'row',
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <MSelect
3
+ :model="model"
4
+ :name="name"
5
+ :size="size"
6
+ :prop="prop"
7
+ :disabled="disabled"
8
+ :config="selectConfig"
9
+ :last-values="lastValues"
10
+ @change="changeHandler"
11
+ ></MSelect>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { computed, inject } from 'vue';
16
+
17
+ import { FieldSize } from '@tmagic/design';
18
+ import { MSelect, SelectConfig } from '@tmagic/form';
19
+
20
+ import { Services } from '../type';
21
+
22
+ defineOptions({
23
+ name: 'MEditorDataSourceSelect',
24
+ });
25
+
26
+ const emit = defineEmits(['change']);
27
+
28
+ const props = withDefaults(
29
+ defineProps<{
30
+ config: {
31
+ type: 'data-source-select';
32
+ name: string;
33
+ text: string;
34
+ placeholder: string;
35
+ dataSourceType?: string;
36
+ };
37
+ model: Record<string, any>;
38
+ name: string;
39
+ prop: string;
40
+ disabled: boolean;
41
+ lastValues?: Record<string, any>;
42
+ size?: FieldSize;
43
+ }>(),
44
+ {
45
+ disabled: false,
46
+ },
47
+ );
48
+
49
+ const { dataSourceService } = inject<Services>('services') || {};
50
+
51
+ const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
52
+
53
+ const selectConfig = computed<SelectConfig>(() => {
54
+ const { type, dataSourceType, ...config } = props.config;
55
+ return {
56
+ ...config,
57
+ type: 'select',
58
+ valueKey: 'dataSourceId',
59
+ options: dataSources.value
60
+ .filter((ds) => !dataSourceType || ds.type === dataSourceType)
61
+ .map((ds) => ({
62
+ value: {
63
+ isBindDataSource: true,
64
+ dataSourceType: ds.type,
65
+ dataSourceId: ds.id,
66
+ },
67
+ text: ds.title || ds.id,
68
+ })),
69
+ };
70
+ });
71
+
72
+ const changeHandler = (value: any) => {
73
+ emit('change', value);
74
+ };
75
+ </script>
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ import CodeSelect from './fields/CodeSelect.vue';
23
23
  import CodeSelectCol from './fields/CodeSelectCol.vue';
24
24
  import DataSourceFields from './fields/DataSourceFields.vue';
25
25
  import DataSourceInput from './fields/DataSourceInput.vue';
26
+ import DataSourceSelect from './fields/DataSourceSelect.vue';
26
27
  import EventSelect from './fields/EventSelect.vue';
27
28
  import KeyValue from './fields/KeyValue.vue';
28
29
  import uiSelect from './fields/UISelect.vue';
@@ -53,6 +54,7 @@ export { default as CodeSelect } from './fields/CodeSelect.vue';
53
54
  export { default as CodeSelectCol } from './fields/CodeSelectCol.vue';
54
55
  export { default as DataSourceFields } from './fields/DataSourceFields.vue';
55
56
  export { default as DataSourceInput } from './fields/DataSourceInput.vue';
57
+ export { default as DataSourceSelect } from './fields/DataSourceSelect.vue';
56
58
  export { default as EventSelect } from './fields/EventSelect.vue';
57
59
  export { default as KeyValue } from './fields/KeyValue.vue';
58
60
  export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
@@ -87,5 +89,6 @@ export default {
87
89
  app.component('m-fields-data-source-fields', DataSourceFields);
88
90
  app.component('m-fields-key-value', KeyValue);
89
91
  app.component('m-fields-data-source-input', DataSourceInput);
92
+ app.component('m-fields-data-source-select', DataSourceSelect);
90
93
  },
91
94
  };
@@ -236,18 +236,22 @@ export const initServiceEvents = (
236
236
  }
237
237
  };
238
238
 
239
+ // 新增节点,收集依赖
239
240
  const nodeAddHandler = (nodes: MNode[]) => {
240
241
  depService.collect(nodes);
241
242
  };
242
243
 
244
+ // 节点更新,收集依赖
243
245
  const nodeUpdateHandler = (nodes: MNode[]) => {
244
246
  depService.collect(nodes);
245
247
  };
246
248
 
249
+ // 节点删除,清除对齐的依赖收集
247
250
  const nodeRemoveHandler = (nodes: MNode[]) => {
248
251
  depService.clear(nodes);
249
252
  };
250
253
 
254
+ // 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖
251
255
  const historyChangeHandler = (page: MPage) => {
252
256
  depService.collect([page], true);
253
257
  };
@@ -37,10 +37,14 @@ const emit = defineEmits(['initd', 'save']);
37
37
  const toString = (v: string | any, language: string): string => {
38
38
  let value = '';
39
39
  if (typeof v !== 'string') {
40
- value = serialize(v, {
41
- space: 2,
42
- unsafe: true,
43
- }).replace(/"(\w+)":\s/g, '$1: ');
40
+ if (props.language.toLocaleLowerCase() === 'json') {
41
+ value = JSON.stringify(v, null, 2);
42
+ } else {
43
+ value = serialize(v, {
44
+ space: 2,
45
+ unsafe: true,
46
+ }).replace(/"(\w+)":\s/g, '$1: ');
47
+ }
44
48
  } else {
45
49
  value = v;
46
50
  }
@@ -81,7 +85,7 @@ const setEditorValue = (v: string | any, m: string | any) => {
81
85
  };
82
86
 
83
87
  const getEditorValue = () =>
84
- props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue();
88
+ (props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue()) || '';
85
89
 
86
90
  const init = async () => {
87
91
  if (!codeEditor.value) return;
@@ -109,13 +113,19 @@ const init = async () => {
109
113
  if (e.keyCode === 83 && (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)) {
110
114
  e.preventDefault();
111
115
  e.stopPropagation();
112
- emit('save', getEditorValue());
116
+ const newValue = getEditorValue();
117
+ values.value = newValue;
118
+ emit('save', newValue);
113
119
  }
114
120
  });
115
121
 
116
122
  if (props.type !== 'diff' && props.autoSave) {
117
123
  vsEditor?.onDidBlurEditorWidget(() => {
118
- emit('save', getEditorValue());
124
+ const newValue = getEditorValue();
125
+ if (values.value !== newValue) {
126
+ values.value = newValue;
127
+ emit('save', newValue);
128
+ }
119
129
  });
120
130
  }
121
131
 
@@ -146,10 +156,20 @@ onUnmounted(() => {
146
156
  });
147
157
 
148
158
  defineExpose({
159
+ values,
160
+
149
161
  getEditor() {
150
162
  return vsEditor || vsDiffEditor;
151
163
  },
152
164
 
165
+ getVsEditor() {
166
+ return vsEditor;
167
+ },
168
+
169
+ getVsDiffEditor() {
170
+ return vsDiffEditor;
171
+ },
172
+
153
173
  setEditorValue,
154
174
 
155
175
  focus() {
@@ -83,14 +83,13 @@ const appendComponent = ({ text, type, data = {} }: ComponentItem): void => {
83
83
 
84
84
  const dragstartHandler = ({ text, type, data = {} }: ComponentItem, e: DragEvent) => {
85
85
  if (e.dataTransfer) {
86
- e.dataTransfer.effectAllowed = 'move';
87
86
  e.dataTransfer.setData(
88
- 'data',
87
+ 'text/html',
89
88
  serialize({
90
89
  name: text,
91
90
  type,
92
91
  ...data,
93
- }).replace(/"(\w+)":\s/g, '$1: '),
92
+ }),
94
93
  );
95
94
  }
96
95
  };
@@ -121,6 +120,6 @@ const dragHandler = (e: DragEvent) => {
121
120
 
122
121
  if (timeout || !stage.value) return;
123
122
 
124
- timeout = stage.value.getAddContainerHighlightClassNameTimeout(e);
123
+ timeout = stage.value.delayedMarkContainer(e);
125
124
  };
126
125
  </script>
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <div>
3
+ {{ `${data.name} (${data.id})` }}
4
+ </div>
5
+ <div class="layer-node-tool">
6
+ <template v-if="data.type !== 'page'">
7
+ <el-icon v-if="data.visible === false" @click.stop="setNodeVisible(true)" title="点击显示">
8
+ <Hide />
9
+ </el-icon>
10
+ <el-icon v-else @click.stop="setNodeVisible(false)" class="node-lock" title="点击隐藏">
11
+ <View />
12
+ </el-icon>
13
+ </template>
14
+ </div>
15
+ </template>
16
+
17
+ <script lang="ts" setup>
18
+ import { inject } from 'vue';
19
+ import { Hide, View } from '@element-plus/icons-vue';
20
+
21
+ import { MNode } from '@tmagic/schema';
22
+
23
+ import { Services } from '@editor/type';
24
+
25
+ const props = defineProps<{
26
+ data: MNode;
27
+ }>();
28
+
29
+ const services = inject<Services>('services');
30
+ const editorService = services?.editorService;
31
+
32
+ const setNodeVisible = (visible: boolean) => {
33
+ if (!editorService) return;
34
+
35
+ editorService.update({
36
+ id: props.data.id,
37
+ visible,
38
+ });
39
+ };
40
+ </script>
@@ -35,9 +35,7 @@
35
35
  <template #default="{ node, data }">
36
36
  <div class="cus-tree-node" :id="data.id" @mouseenter="highlightHandler(data)">
37
37
  <slot name="layer-node-content" :node="node" :data="data">
38
- <span>
39
- {{ `${data.name} (${data.id})` }}
40
- </span>
38
+ <LayerNode :data="data"></LayerNode>
41
39
  </slot>
42
40
  </div>
43
41
  </template>
@@ -63,6 +61,7 @@ import type { MenuButton, MenuComponent, Services } from '@editor/type';
63
61
  import { Layout } from '@editor/type';
64
62
 
65
63
  import LayerMenu from './LayerMenu.vue';
64
+ import LayerNode from './LayerNode.vue';
66
65
 
67
66
  defineOptions({
68
67
  name: 'MEditorLayerPanel',
@@ -15,6 +15,7 @@
15
15
  :expand-on-click-node="false"
16
16
  :data="list"
17
17
  :highlight-current="true"
18
+ @node-click="clickHandler"
18
19
  >
19
20
  <template #default="{ data }">
20
21
  <div :id="data.id" class="list-container">
@@ -53,7 +54,7 @@ import { computed, inject, ref } from 'vue';
53
54
  import { Aim, Close, Coin, Edit } from '@element-plus/icons-vue';
54
55
 
55
56
  import { TMagicButton, tMagicMessageBox, TMagicScrollbar, TMagicTooltip, TMagicTree } from '@tmagic/design';
56
- import { DataSourceSchema } from '@tmagic/schema';
57
+ import { DataSourceSchema, Id } from '@tmagic/schema';
57
58
 
58
59
  import Icon from '@editor/components/Icon.vue';
59
60
  import SearchInput from '@editor/components/SearchInput.vue';
@@ -66,7 +67,7 @@ defineOptions({
66
67
  });
67
68
 
68
69
  const services = inject<Partial<Services>>('services', {});
69
- const { dataSourceService, depService } = inject<Services>('services') || {};
70
+ const { dataSourceService, depService, editorService } = inject<Services>('services') || {};
70
71
 
71
72
  const list = computed(() =>
72
73
  Object.values(depService?.targets['data-source'] || {}).map((target) => ({
@@ -131,4 +132,19 @@ const tree = ref<InstanceType<typeof TMagicTree>>();
131
132
  const filterTextChangeHandler = (val: string) => {
132
133
  tree.value?.filter(val);
133
134
  };
135
+
136
+ // 选中组件
137
+ const selectComp = (compId: Id) => {
138
+ const stage = editorService?.get('stage');
139
+ editorService?.select(compId);
140
+ stage?.select(compId);
141
+ };
142
+
143
+ const clickHandler = (data: any, node: any) => {
144
+ if (data.type === 'node') {
145
+ selectComp(data.id);
146
+ } else if (data.type === 'key') {
147
+ selectComp(node.parent.data.id);
148
+ }
149
+ };
134
150
  </script>
@@ -146,9 +146,8 @@ const contextmenuHandler = (e: MouseEvent) => {
146
146
 
147
147
  const dragoverHandler = (e: DragEvent) => {
148
148
  e.preventDefault();
149
- if (e.dataTransfer) {
150
- e.dataTransfer.dropEffect = 'move';
151
- }
149
+ if (!e.dataTransfer) return;
150
+ e.dataTransfer.dropEffect = 'move';
152
151
  };
153
152
 
154
153
  const dropHandler = async (e: DragEvent) => {
@@ -164,7 +163,15 @@ const dropHandler = async (e: DragEvent) => {
164
163
 
165
164
  if (e.dataTransfer && parent && stageContainer.value && stage) {
166
165
  const parseDSL = getConfig('parseDSL');
167
- const config = parseDSL(`(${e.dataTransfer.getData('data')})`);
166
+
167
+ const data = e.dataTransfer.getData('text/html');
168
+
169
+ if (!data) return;
170
+
171
+ const config = parseDSL(`(${data})`);
172
+
173
+ if (!config) return;
174
+
168
175
  const layout = await services?.editorService.getLayout(parent);
169
176
 
170
177
  const containerRect = stageContainer.value.getBoundingClientRect();
@@ -262,6 +262,7 @@ export class Watcher extends EventEmitter {
262
262
  Object.values(this.targets).forEach((targets) => {
263
263
  Object.values(targets).forEach((target) => {
264
264
  nodes.forEach((node) => {
265
+ // 先删除原有依赖,重新收集
265
266
  target.removeDep(node);
266
267
  this.collectItem(node, target, deep);
267
268
  });
@@ -44,6 +44,12 @@
44
44
  .magic-editor-layer-panel .cus-tree-node {
45
45
  width: 100%;
46
46
  overflow: hidden;
47
+ display: flex;
48
+ justify-content: space-between;
49
+
50
+ .layer-node-tool {
51
+ margin-right: 15px;
52
+ }
47
53
  }
48
54
 
49
55
  .magic-editor-layer-panel .cus-tree-node-hover {
package/src/utils/dep.ts CHANGED
@@ -11,6 +11,10 @@ export const createCodeBlockTarget = (id: Id, codeBlock: CodeBlockContent) =>
11
11
  id,
12
12
  name: codeBlock.name,
13
13
  isTarget: (key: string | number, value: any) => {
14
+ if (id === value) {
15
+ return true;
16
+ }
17
+
14
18
  if (value?.hookType === HookType.CODE && !isEmpty(value.hookData)) {
15
19
  const index = value.hookData.findIndex((item: HookData) => item.codeId === id);
16
20
  return Boolean(index > -1);
@@ -25,5 +29,7 @@ export const createDataSourceTarget = (id: Id, ds: DataSourceSchema) =>
25
29
  type: 'data-source',
26
30
  id,
27
31
  name: ds.title || `${id}`,
28
- isTarget: (key: string | number, value: any) => typeof value === 'string' && value.includes(`${id}`),
32
+ isTarget: (key: string | number, value: any) =>
33
+ // 关联数据源对象或者在模板在使用数据源
34
+ (value.isBindDataSource && value.dataSourceId) || (typeof value === 'string' && value.includes(`${id}`)),
29
35
  });
@@ -116,6 +116,7 @@ declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_T
116
116
  onClose?: ((...args: any[]) => any) | undefined;
117
117
  onSaveAndClose?: ((...args: any[]) => any) | undefined;
118
118
  }, {
119
+ /** 代码块id */
119
120
  saveAndClose: () => void;
120
121
  close: () => Promise<void>;
121
122
  }, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("close" | "saveAndClose")[], string, {
@@ -168,6 +169,7 @@ declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_T
168
169
  onClose?: ((...args: any[]) => any) | undefined;
169
170
  onSaveAndClose?: ((...args: any[]) => any) | undefined;
170
171
  } & import("vue").ShallowUnwrapRef<{
172
+ /** 代码块id */
171
173
  saveAndClose: () => void;
172
174
  close: () => Promise<void>;
173
175
  }> & {} & import("vue").ComponentCustomProperties & {}) | undefined>;
@@ -0,0 +1,56 @@
1
+ import { FieldSize } from '@tmagic/design';
2
+ declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
3
+ config: {
4
+ type: "data-source-select";
5
+ name: string;
6
+ text: string;
7
+ placeholder: string;
8
+ dataSourceType?: string | undefined;
9
+ };
10
+ model: Record<string, any>;
11
+ name: string;
12
+ prop: string;
13
+ disabled: boolean;
14
+ lastValues?: Record<string, any> | undefined;
15
+ size?: FieldSize | undefined;
16
+ }>, {
17
+ disabled: boolean;
18
+ }>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "change"[], "change", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
19
+ config: {
20
+ type: "data-source-select";
21
+ name: string;
22
+ text: string;
23
+ placeholder: string;
24
+ dataSourceType?: string | undefined;
25
+ };
26
+ model: Record<string, any>;
27
+ name: string;
28
+ prop: string;
29
+ disabled: boolean;
30
+ lastValues?: Record<string, any> | undefined;
31
+ size?: FieldSize | undefined;
32
+ }>, {
33
+ disabled: boolean;
34
+ }>>> & {
35
+ onChange?: ((...args: any[]) => any) | undefined;
36
+ }, {
37
+ disabled: boolean;
38
+ }, {}>;
39
+ export default _default;
40
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
41
+ type __VLS_TypePropsToRuntimeProps<T> = {
42
+ [K in keyof T]-?: {} extends Pick<T, K> ? {
43
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
44
+ } : {
45
+ type: import('vue').PropType<T[K]>;
46
+ required: true;
47
+ };
48
+ };
49
+ type __VLS_WithDefaults<P, D> = {
50
+ [K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
51
+ default: D[K];
52
+ }> : P[K];
53
+ };
54
+ type __VLS_Prettify<T> = {
55
+ [K in keyof T]: T[K];
56
+ } & {};
package/types/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export { default as CodeSelect } from './fields/CodeSelect.vue';
21
21
  export { default as CodeSelectCol } from './fields/CodeSelectCol.vue';
22
22
  export { default as DataSourceFields } from './fields/DataSourceFields.vue';
23
23
  export { default as DataSourceInput } from './fields/DataSourceInput.vue';
24
+ export { default as DataSourceSelect } from './fields/DataSourceSelect.vue';
24
25
  export { default as EventSelect } from './fields/EventSelect.vue';
25
26
  export { default as KeyValue } from './fields/KeyValue.vue';
26
27
  export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
@@ -15,7 +15,10 @@ declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_T
15
15
  tabSize: number;
16
16
  };
17
17
  }>, {
18
+ values: import("vue").Ref<string>;
18
19
  getEditor(): monaco.editor.IStandaloneCodeEditor | monaco.editor.IStandaloneDiffEditor | null;
20
+ getVsEditor(): monaco.editor.IStandaloneCodeEditor | null;
21
+ getVsDiffEditor(): monaco.editor.IStandaloneDiffEditor | null;
19
22
  setEditorValue: (v: any, m: any) => void | undefined;
20
23
  focus(): void;
21
24
  }, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("initd" | "save")[], "initd" | "save", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
@@ -0,0 +1,16 @@
1
+ import { MNode } from '@tmagic/schema';
2
+ declare const _default: import("vue").DefineComponent<__VLS_TypePropsToRuntimeProps<{
3
+ data: MNode;
4
+ }>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{
5
+ data: MNode;
6
+ }>>>, {}, {}>;
7
+ export default _default;
8
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
9
+ type __VLS_TypePropsToRuntimeProps<T> = {
10
+ [K in keyof T]-?: {} extends Pick<T, K> ? {
11
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
12
+ } : {
13
+ type: import('vue').PropType<T[K]>;
14
+ required: true;
15
+ };
16
+ };