@tmagic/editor 1.8.0-beta.25 → 1.8.0-beta.26
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/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +10 -16
- package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +5 -8
- package/dist/es/components/ViewForm.vue_vue_type_script_setup_true_lang.js +3 -5
- package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +3 -11
- package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +29 -124
- package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +5 -6
- package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +4 -5
- package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +6 -10
- package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +5 -7
- package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +1 -4
- package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +4 -6
- package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +9 -142
- package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +51 -326
- package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +2 -4
- package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +3 -34
- package/dist/es/fields/StyleSetter/configs.js +663 -0
- package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +5 -108
- package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +3 -9
- package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +5 -102
- package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +5 -265
- package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +5 -85
- package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +3 -22
- package/dist/es/fields/configs/codeSelect.js +98 -0
- package/dist/es/fields/configs/displayConds.js +158 -0
- package/dist/es/fields/configs/eventSelect.js +225 -0
- package/dist/es/fields/configs/stickyAddButton.js +20 -0
- package/dist/es/fields/headless-validation.js +139 -0
- package/dist/es/headless.js +2 -0
- package/dist/es/hooks/use-compare-form.js +6 -23
- package/dist/es/hooks/use-form-context.js +19 -0
- package/dist/es/index.js +2 -1
- package/dist/es/initService.js +17 -1
- package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +1 -7
- package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +1 -11
- package/dist/es/layouts/history-list/useHistoryRevert.js +3 -11
- package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +10 -23
- package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +3 -13
- package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/sidebar/code-block/useContentMenu.js +1 -1
- package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/sidebar/data-source/useContentMenu.js +1 -1
- package/dist/es/plugin.js +33 -32
- package/dist/es/style.css +116 -60
- package/dist/es/utils/data-source/index.js +2 -1
- package/dist/es/utils/props.js +1 -4
- package/dist/es/utils/type-match-rules.js +2 -2
- package/dist/style.css +116 -60
- package/dist/themes/magic-admin.css +128 -67
- package/dist/tmagic-editor-headless.umd.cjs +10867 -0
- package/dist/tmagic-editor.umd.cjs +2123 -2015
- package/package.json +12 -7
- package/src/Editor.vue +12 -16
- package/src/components/CodeParams.vue +10 -4
- package/src/components/CompareForm.vue +4 -5
- package/src/components/ViewForm.vue +2 -3
- package/src/editorProps.ts +1 -2
- package/src/fields/Code.vue +2 -10
- package/src/fields/CodeSelect.vue +20 -116
- package/src/fields/CodeSelectCol.vue +3 -5
- package/src/fields/DataSourceFieldSelect/FieldSelect.vue +1 -1
- package/src/fields/DataSourceFieldSelect/Index.vue +3 -5
- package/src/fields/DataSourceFields.vue +0 -7
- package/src/fields/DataSourceMethodSelect.vue +2 -5
- package/src/fields/DataSourceMethods.vue +1 -6
- package/src/fields/DataSourceMocks.vue +1 -10
- package/src/fields/DataSourceSelect.vue +1 -1
- package/src/fields/DisplayConds.vue +17 -157
- package/src/fields/EventSelect.vue +28 -375
- package/src/fields/KeyValue.vue +3 -4
- package/src/fields/StyleSetter/components/Border.vue +4 -38
- package/src/fields/StyleSetter/configs.ts +690 -0
- package/src/fields/StyleSetter/pro/Background.vue +4 -104
- package/src/fields/StyleSetter/pro/Border.vue +3 -12
- package/src/fields/StyleSetter/pro/Font.vue +4 -123
- package/src/fields/StyleSetter/pro/Layout.vue +4 -219
- package/src/fields/StyleSetter/pro/Position.vue +6 -110
- package/src/fields/StyleSetter/pro/Transform.vue +4 -30
- package/src/fields/configs/codeSelect.ts +127 -0
- package/src/fields/configs/displayConds.ts +184 -0
- package/src/fields/configs/eventSelect.ts +309 -0
- package/src/fields/configs/stickyAddButton.ts +31 -0
- package/src/fields/headless-validation.ts +177 -0
- package/src/headless.ts +33 -0
- package/src/hooks/use-compare-form.ts +7 -25
- package/src/hooks/use-form-context.ts +40 -0
- package/src/index.ts +2 -0
- package/src/initService.ts +23 -1
- package/src/layouts/history-list/HistoryDiffDialog.vue +0 -10
- package/src/layouts/history-list/HistoryListPanel.vue +1 -13
- package/src/layouts/history-list/useHistoryRevert.ts +7 -13
- package/src/layouts/props-panel/FormPanel.vue +17 -28
- package/src/layouts/props-panel/PropsPanel.vue +1 -4
- package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +1 -1
- package/src/layouts/sidebar/code-block/useContentMenu.ts +1 -1
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +1 -1
- package/src/layouts/sidebar/data-source/useContentMenu.ts +1 -1
- package/src/plugin.ts +32 -33
- package/src/theme/code-block.scss +0 -37
- package/src/theme/display-conds.scss +4 -0
- package/src/theme/event.scss +22 -45
- package/src/theme/props-panel.scss +17 -3
- package/src/theme/themes/magic-admin/index.scss +12 -1
- package/src/type.ts +10 -29
- package/src/utils/data-source/index.ts +1 -0
- package/src/utils/props.ts +1 -4
- package/src/utils/type-match-rules.ts +8 -3
- package/types/headless.d.ts +18 -0
- package/types/index.d.ts +154 -179
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2025 Tencent. All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 属性面板列表字段共用的吸底全宽「添加」按钮,展开到 group-list 配置上。
|
|
21
|
+
*
|
|
22
|
+
* 吸底按钮会盖住列表底部,新增的项必须同时滚进视口才看得见,两者一起给出避免漏配。
|
|
23
|
+
*/
|
|
24
|
+
export const stickyAddButton = (text: string) => ({
|
|
25
|
+
scrollLastItemIntoView: true as const,
|
|
26
|
+
addButtonConfig: {
|
|
27
|
+
sticky: true as const,
|
|
28
|
+
text,
|
|
29
|
+
props: { type: 'primary', plain: true, text: false },
|
|
30
|
+
},
|
|
31
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2025 Tencent. All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
type DisplayCondsConfig,
|
|
21
|
+
type EventSelectConfig,
|
|
22
|
+
type FieldInnerConfig,
|
|
23
|
+
type FieldMountValueEffect,
|
|
24
|
+
filterFunction,
|
|
25
|
+
type FormItemConfig,
|
|
26
|
+
type HeadlessFieldOptions,
|
|
27
|
+
} from '@tmagic/form/headless';
|
|
28
|
+
import type { StyleSchema } from '@tmagic/schema';
|
|
29
|
+
|
|
30
|
+
import { editorTypeMatchRules, validateDataSourceFieldSelect } from '@editor/utils/type-match-rules';
|
|
31
|
+
|
|
32
|
+
import { createCodeSelectConfig, normalizeCodeSelectValue } from './configs/codeSelect';
|
|
33
|
+
import { createDisplayCondsConfig } from './configs/displayConds';
|
|
34
|
+
import { createEventSelectConfig, isLegacyEventValue } from './configs/eventSelect';
|
|
35
|
+
import { createStyleSetterConfig } from './StyleSetter/configs';
|
|
36
|
+
|
|
37
|
+
const getName = (config: FormItemConfig): string => `${(config as any).name ?? ''}`;
|
|
38
|
+
|
|
39
|
+
/** `code-select` 旧数据兼容:空值改写成 `{ hookType, hookData }`。 */
|
|
40
|
+
const codeSelectEffect: FieldMountValueEffect = ({ config, model }) => {
|
|
41
|
+
normalizeCodeSelectValue(model, getName(config));
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** `event-select`:表单 init 对未声明 defaultValue 的自定义 type 会写成空串,统一收成数组。 */
|
|
45
|
+
const eventSelectEffect: FieldMountValueEffect = ({ config, model }) => {
|
|
46
|
+
const name = getName(config);
|
|
47
|
+
if (model && !Array.isArray(model[name])) {
|
|
48
|
+
model[name] = [];
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* `code-select` 的内部配置。
|
|
54
|
+
*
|
|
55
|
+
* 对应 `fields/CodeSelect.vue` 内部
|
|
56
|
+
* `<MContainer :config="codeConfig" :model="model[name]" :prop="prop">`。
|
|
57
|
+
*
|
|
58
|
+
* @param ctx - innerConfig 回调入参
|
|
59
|
+
* @returns 内部容器的 config / model / prop
|
|
60
|
+
*/
|
|
61
|
+
const codeSelectInnerConfig: FieldInnerConfig = ({ config, model, prop }) => {
|
|
62
|
+
const name = getName(config);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
config: createCodeSelectConfig(config as any),
|
|
66
|
+
model: model?.[name],
|
|
67
|
+
prop,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* `display-conds` 的内部配置。
|
|
73
|
+
*
|
|
74
|
+
* 对应 `fields/DisplayConds.vue` 内部把同一份 groupList 配置交给 `MGroupList`。
|
|
75
|
+
*
|
|
76
|
+
* @param ctx - innerConfig 回调入参
|
|
77
|
+
* @returns 内部 group-list 配置;prop 基准为 parentProp,避免 name 被拼两次
|
|
78
|
+
*/
|
|
79
|
+
const displayCondsInnerConfig: FieldInnerConfig = ({ config, model, prop, parentProp, mForm }) => {
|
|
80
|
+
const name = getName(config);
|
|
81
|
+
const parentFields =
|
|
82
|
+
filterFunction<string[]>(mForm, (config as DisplayCondsConfig).parentFields, {
|
|
83
|
+
model,
|
|
84
|
+
config,
|
|
85
|
+
prop,
|
|
86
|
+
}) || [];
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
config: createDisplayCondsConfig(config as DisplayCondsConfig, name, parentFields),
|
|
90
|
+
// 内部 group-list 复用了字段自身的 name,prop 基准要退回父级,否则 name 会被拼两次
|
|
91
|
+
prop: parentProp,
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* `event-select` 的内部配置。
|
|
97
|
+
*
|
|
98
|
+
* 对应 `fields/EventSelect.vue`:列表走 group-list,事件名渲染在 title slot,
|
|
99
|
+
* 无渲染校验仍把事件名当作列表项字段,路径为 `<prop>.<index>.name`。
|
|
100
|
+
*
|
|
101
|
+
* @param ctx - innerConfig 回调入参
|
|
102
|
+
* @returns 合成的 group-list 配置;旧数据格式返回 null,不参与校验
|
|
103
|
+
*/
|
|
104
|
+
const eventSelectInnerConfig: FieldInnerConfig = ({ config, model, parentProp }) => {
|
|
105
|
+
const name = getName(config);
|
|
106
|
+
const events = model?.[name];
|
|
107
|
+
|
|
108
|
+
// 旧数据格式走的是另一套表格配置,其中不含任何 rules,不参与校验
|
|
109
|
+
if (!Array.isArray(events) || isLegacyEventValue(events)) return null;
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
config: createEventSelectConfig(config as EventSelectConfig, name, { includeEventName: true }),
|
|
113
|
+
prop: parentProp,
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* `style-setter` 的内部配置。
|
|
119
|
+
*
|
|
120
|
+
* 对应 `fields/StyleSetter/Index.vue`:6 个面板共用 `:values="model[name]"`、`:prop="prop || name"`。
|
|
121
|
+
* `theme` 按 `useTheme` 缺省空串传入,只改 flexWrap 的 UI childType,不影响校验字段。
|
|
122
|
+
*
|
|
123
|
+
* @param ctx - innerConfig 回调入参
|
|
124
|
+
* @returns style 面板配置及 styleModel
|
|
125
|
+
*/
|
|
126
|
+
const styleSetterInnerConfig: FieldInnerConfig = ({ config, model, prop }) => {
|
|
127
|
+
const name = getName(config);
|
|
128
|
+
const styleModel = (model?.[name] ?? {}) as Partial<StyleSchema>;
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
config: createStyleSetterConfig(styleModel, '', validateDataSourceFieldSelect),
|
|
132
|
+
model: styleModel,
|
|
133
|
+
prop,
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 编辑器字段的无渲染登记表(不含 Vue 组件)。
|
|
139
|
+
*
|
|
140
|
+
* Node 里 `registerFields(editorFields)` 即可配合 `validateForm` / `submitForm`。
|
|
141
|
+
* 安装编辑器时由 plugin 把 `component` 叠上去再传给 `@tmagic/form`。
|
|
142
|
+
*
|
|
143
|
+
* - 叶子:内部只渲染叶子 UI,或把子表单渲染在独立的 MForm / MFormBox 实例里
|
|
144
|
+
* - innerConfig:内部再渲染 MContainer / MPanel / MGroupList,把运行期配置交出来
|
|
145
|
+
* - typeMatch:该 type 自身的类型匹配校验
|
|
146
|
+
*
|
|
147
|
+
* innerConfig 返回的 config / model / prop 与组件模板里传给内部容器的那一组保持一一对应,
|
|
148
|
+
* 且配置本身与组件共用同一份工厂(`fields/configs/`)。
|
|
149
|
+
*/
|
|
150
|
+
export const editorFields: Record<string, HeadlessFieldOptions> = {
|
|
151
|
+
'vs-code': {},
|
|
152
|
+
'code-link': {},
|
|
153
|
+
'ui-select': { typeMatch: editorTypeMatchRules['ui-select'] },
|
|
154
|
+
'cond-op-select': { typeMatch: editorTypeMatchRules['cond-op-select'] },
|
|
155
|
+
'page-fragment-select': { typeMatch: editorTypeMatchRules['page-fragment-select'] },
|
|
156
|
+
'data-source-select': { typeMatch: editorTypeMatchRules['data-source-select'] },
|
|
157
|
+
'data-source-input': { typeMatch: editorTypeMatchRules['data-source-input'] },
|
|
158
|
+
'key-value': { typeMatch: editorTypeMatchRules['key-value'] },
|
|
159
|
+
'code-select-col': { typeMatch: editorTypeMatchRules['code-select-col'] },
|
|
160
|
+
'data-source-fields': { typeMatch: editorTypeMatchRules['data-source-fields'] },
|
|
161
|
+
'data-source-mocks': { typeMatch: editorTypeMatchRules['data-source-mocks'] },
|
|
162
|
+
'data-source-methods': { typeMatch: editorTypeMatchRules['data-source-methods'] },
|
|
163
|
+
'data-source-method-select': { typeMatch: editorTypeMatchRules['data-source-method-select'] },
|
|
164
|
+
'data-source-field-select': { typeMatch: editorTypeMatchRules['data-source-field-select'] },
|
|
165
|
+
'code-select': {
|
|
166
|
+
effect: codeSelectEffect,
|
|
167
|
+
innerConfig: codeSelectInnerConfig,
|
|
168
|
+
typeMatch: editorTypeMatchRules['code-select'],
|
|
169
|
+
},
|
|
170
|
+
'display-conds': { innerConfig: displayCondsInnerConfig, typeMatch: editorTypeMatchRules['display-conds'] },
|
|
171
|
+
'event-select': {
|
|
172
|
+
effect: eventSelectEffect,
|
|
173
|
+
innerConfig: eventSelectInnerConfig,
|
|
174
|
+
typeMatch: editorTypeMatchRules['event-select'],
|
|
175
|
+
},
|
|
176
|
+
'style-setter': { innerConfig: styleSetterInnerConfig, typeMatch: editorTypeMatchRules['style-setter'] },
|
|
177
|
+
};
|
package/src/headless.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2025 Tencent. All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @fileoverview `@tmagic/editor/headless`:编辑器字段的无渲染登记表。
|
|
21
|
+
*
|
|
22
|
+
* 不加载编辑器界面(Editor.vue、各面板、monaco 等),可在无 DOM 的 Node 里 import。
|
|
23
|
+
* 配合 `@tmagic/form/headless`:`registerBuiltInFields(builtInFields); registerFields(editorFields);`
|
|
24
|
+
*
|
|
25
|
+
* 注意这里不是「零 Vue 依赖」:`style-setter` 的字段配置与编辑器 UI 共用同一份工厂
|
|
26
|
+
* (`fields/StyleSetter/configs.ts`),配置里的 `icon` / `component` 会带进来若干图标
|
|
27
|
+
* SFC;typeMatch 的提示文案要读画布与数据源,也会带进 editor services。这些都只在
|
|
28
|
+
* import 时定义、不访问 DOM,但产物体积明显大于 `@tmagic/form/headless`。
|
|
29
|
+
*
|
|
30
|
+
* @module @tmagic/editor/headless
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export { editorFields } from './fields/headless-validation';
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { computed, type ComputedRef, inject, provide, type Ref, ref, useTemplateRef, watch
|
|
19
|
+
import { computed, type ComputedRef, inject, provide, type Ref, ref, useTemplateRef, watch } from 'vue';
|
|
20
20
|
|
|
21
21
|
import type { CodeBlockContent, MNode } from '@tmagic/core';
|
|
22
|
-
import { type FormConfig, type
|
|
22
|
+
import { type FormConfig, type FormContext, type FormValue, MForm } from '@tmagic/form';
|
|
23
23
|
|
|
24
|
+
import { useEditorFormContext } from '@editor/hooks/use-form-context';
|
|
24
25
|
import type { CompareFormBaseProps } from '@editor/type';
|
|
25
26
|
import { getCodeBlockFormConfig } from '@editor/utils/code-block';
|
|
26
27
|
|
|
@@ -28,7 +29,7 @@ export interface UseCompareFormReturn {
|
|
|
28
29
|
config: Ref<FormConfig>;
|
|
29
30
|
currentValues: ComputedRef<FormValue>;
|
|
30
31
|
wrapperStyle: ComputedRef<Record<string, string> | undefined>;
|
|
31
|
-
|
|
32
|
+
formContext: ComputedRef<FormContext>;
|
|
32
33
|
loadConfig: () => Promise<void>;
|
|
33
34
|
formRef: Readonly<Ref<InstanceType<typeof MForm> | null>>;
|
|
34
35
|
normalizeCodeBlockValue: (v: Partial<CodeBlockContent> | Record<string, any> | undefined) => Record<string, any>;
|
|
@@ -39,7 +40,7 @@ export interface UseCompareFormReturn {
|
|
|
39
40
|
* - 按 `category`(node / data-source / code-block) 加载 FormConfig(支持自定义 `loadConfig`);
|
|
40
41
|
* - 代码块 `content` 归一化为字符串;
|
|
41
42
|
* - 外层容器固定高度 + 内部滚动的 `wrapperStyle`;
|
|
42
|
-
* - 将 services / stage
|
|
43
|
+
* - 将 services / stage 作为 form context 传给 MForm,保证 filterFunction 上下文一致。
|
|
43
44
|
*
|
|
44
45
|
* 两个组件的差异仅在于是否做新旧值对比,这部分逻辑保留在各自组件中。
|
|
45
46
|
*/
|
|
@@ -87,10 +88,7 @@ export const useCompareForm = (props: CompareFormBaseProps): UseCompareFormRetur
|
|
|
87
88
|
return style;
|
|
88
89
|
});
|
|
89
90
|
|
|
90
|
-
const
|
|
91
|
-
const extendState = props.extendState ?? ((s: FormState) => s);
|
|
92
|
-
return extendState(props.baseFormState || state);
|
|
93
|
-
};
|
|
91
|
+
const formContext = useEditorFormContext(() => props.services);
|
|
94
92
|
|
|
95
93
|
/**
|
|
96
94
|
* 内置的默认 FormConfig 加载逻辑:按 `category` 从对应 service / 工具取配置。
|
|
@@ -156,27 +154,11 @@ export const useCompareForm = (props: CompareFormBaseProps): UseCompareFormRetur
|
|
|
156
154
|
|
|
157
155
|
const formRef = useTemplateRef<InstanceType<typeof MForm>>('form');
|
|
158
156
|
|
|
159
|
-
/**
|
|
160
|
-
* 把 services / stage 注入 MForm 的 formState,避免 propsService 注入的表单配置中
|
|
161
|
-
* 形如 `display: ({ services }) => services.uiService.get(...)` 的 filterFunction
|
|
162
|
-
* 在执行时拿不到 `formState.services` 而报错。
|
|
163
|
-
*
|
|
164
|
-
* 与 props-panel/FormPanel.vue 中的注入方式保持一致:
|
|
165
|
-
* - services:整个 useServices() 返回的服务集合;
|
|
166
|
-
* - stage:当前 editorService.get('stage') 的最新值。
|
|
167
|
-
*/
|
|
168
|
-
watchEffect(() => {
|
|
169
|
-
if (formRef.value && props.services) {
|
|
170
|
-
formRef.value.formState.stage = props.services.editorService.get('stage');
|
|
171
|
-
formRef.value.formState.services = props.services;
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
|
|
175
157
|
return {
|
|
176
158
|
config,
|
|
177
159
|
currentValues,
|
|
178
160
|
wrapperStyle,
|
|
179
|
-
|
|
161
|
+
formContext,
|
|
180
162
|
loadConfig,
|
|
181
163
|
formRef,
|
|
182
164
|
normalizeCodeBlockValue,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2025 Tencent. All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { computed, type ComputedRef } from 'vue';
|
|
20
|
+
|
|
21
|
+
import type { FormContext } from '@tmagic/form';
|
|
22
|
+
|
|
23
|
+
import type { Services } from '@editor/type';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 编辑器注入给表单的业务上下文:`services` 与当前画布 `stage`。
|
|
27
|
+
*
|
|
28
|
+
* 由 `Editor.vue`(provide `FORM_CONTEXT_KEY`)、`FormPanel.vue` 与 `useCompareForm`
|
|
29
|
+
* 共用。`stage` 走 computed 而非快照,保证切换画布后配置回调读到的是最新实例。
|
|
30
|
+
*
|
|
31
|
+
* 字段类型见 `@editor/type` 里对 `@tmagic/form-schema` 的 `FormContext` 模块增强。
|
|
32
|
+
*/
|
|
33
|
+
export const useEditorFormContext = (getServices: () => Services | undefined): ComputedRef<FormContext> =>
|
|
34
|
+
computed(() => {
|
|
35
|
+
const services = getServices();
|
|
36
|
+
return {
|
|
37
|
+
services,
|
|
38
|
+
stage: services?.editorService.get('stage'),
|
|
39
|
+
};
|
|
40
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -91,4 +91,6 @@ export { default as DisplayConds } from './fields/DisplayConds.vue';
|
|
|
91
91
|
export { default as CondOpSelect } from './fields/CondOpSelect.vue';
|
|
92
92
|
export { default as StyleSetter } from './fields/StyleSetter/Index.vue';
|
|
93
93
|
|
|
94
|
+
export { editorFields } from './fields/headless-validation';
|
|
95
|
+
|
|
94
96
|
export { default } from './plugin';
|
package/src/initService.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { getDepNodeIds, getNodes, isPage, isValueIncludeDataSource } from '@tmag
|
|
|
29
29
|
|
|
30
30
|
import PropsPanel from './layouts/PropsPanel.vue';
|
|
31
31
|
import { isIncludeDataSource } from './utils/editor';
|
|
32
|
+
import { error as logError } from './utils/logger';
|
|
32
33
|
import { EditorProps } from './editorProps';
|
|
33
34
|
import { Services } from './type';
|
|
34
35
|
|
|
@@ -36,6 +37,19 @@ export declare type LooseRequired<T> = {
|
|
|
36
37
|
[P in string & keyof T]: T[P];
|
|
37
38
|
};
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* select 为异步串行执行,节点不可选中(如已从 DSL 中移除)时会抛错。
|
|
42
|
+
* 在 watch 回调中调用不能把拒绝抛回调用方,否则会变成 Vue 的「Unhandled error during execution of watcher callback」,
|
|
43
|
+
* 这里统一兜底成日志。
|
|
44
|
+
*/
|
|
45
|
+
const selectNode = async (editorService: Services['editorService'], id: Id) => {
|
|
46
|
+
try {
|
|
47
|
+
await editorService.select(id);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
logError(e);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
39
53
|
export const initServiceState = (
|
|
40
54
|
props: EditorProps,
|
|
41
55
|
{
|
|
@@ -193,9 +207,17 @@ export const initServiceState = (
|
|
|
193
207
|
},
|
|
194
208
|
);
|
|
195
209
|
|
|
210
|
+
// defaultSelected 可能早于 modelValue 到位(immediate 首次执行时 root 还是空的,或节点尚未出现在 DSL 中),
|
|
211
|
+
// 此时 select 会因为「获取不到组件信息」而抛错,交由 root-change 中的选中逻辑在 root 就绪后补上即可。
|
|
196
212
|
watch(
|
|
197
213
|
() => props.defaultSelected,
|
|
198
|
-
(defaultSelected) =>
|
|
214
|
+
(defaultSelected) => {
|
|
215
|
+
if (!defaultSelected || !editorService.getNodeById(defaultSelected)) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
selectNode(editorService, defaultSelected);
|
|
220
|
+
},
|
|
199
221
|
{
|
|
200
222
|
immediate: true,
|
|
201
223
|
},
|
|
@@ -43,8 +43,6 @@
|
|
|
43
43
|
:data-source-type="payload.dataSourceType"
|
|
44
44
|
:value="rightValue"
|
|
45
45
|
:last-value="leftValue"
|
|
46
|
-
:base-form-state="compareFormState"
|
|
47
|
-
:extend-state="extendState"
|
|
48
46
|
:load-config="loadConfig"
|
|
49
47
|
:self-diff-field-types="selfDiffFieldTypes"
|
|
50
48
|
:services="props.services"
|
|
@@ -86,7 +84,6 @@ import {
|
|
|
86
84
|
TMagicRadioGroup,
|
|
87
85
|
TMagicTag,
|
|
88
86
|
} from '@tmagic/design';
|
|
89
|
-
import type { FormState } from '@tmagic/form';
|
|
90
87
|
|
|
91
88
|
import CompareForm from '@editor/components/CompareForm.vue';
|
|
92
89
|
import CodeEditor from '@editor/layouts/CodeEditor.vue';
|
|
@@ -100,12 +97,6 @@ const props = withDefaults(
|
|
|
100
97
|
defineProps<{
|
|
101
98
|
/** 编辑器服务集合,由调用方传入(不再通过 inject('services') 获取)。 */
|
|
102
99
|
services?: Services;
|
|
103
|
-
/**
|
|
104
|
-
* 来自 Editor 顶层的 `extendFormState`,用于扩展 MForm.formState。
|
|
105
|
-
* 透传给 CompareForm,从而让差异对比时表单 item 中依赖业务上下文的
|
|
106
|
-
* `display` / `disabled` 等 filterFunction 正常工作。
|
|
107
|
-
*/
|
|
108
|
-
extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
109
100
|
/**
|
|
110
101
|
* 自定义 FormConfig 加载逻辑,透传给 CompareForm。传入后将接管内置的按 `category`
|
|
111
102
|
* 取配置逻辑,可通过 `ctx.defaultLoadConfig()` 复用默认结果再做二次加工。
|
|
@@ -117,7 +108,6 @@ const props = withDefaults(
|
|
|
117
108
|
isConfirm?: boolean;
|
|
118
109
|
onConfirm?: () => void;
|
|
119
110
|
selfDiffFieldTypes?: string[];
|
|
120
|
-
compareFormState?: FormState;
|
|
121
111
|
}>(),
|
|
122
112
|
{
|
|
123
113
|
width: '900px',
|
|
@@ -133,7 +133,6 @@ import { computed, inject, markRaw, ref, watch } from 'vue';
|
|
|
133
133
|
import { Clock, Close } from '@element-plus/icons-vue';
|
|
134
134
|
|
|
135
135
|
import { getDesignConfig, TMagicButton, tMagicMessage, TMagicPopover, TMagicTabs, TMagicTooltip } from '@tmagic/design';
|
|
136
|
-
import type { FormState } from '@tmagic/form';
|
|
137
136
|
|
|
138
137
|
import MIcon from '@editor/components/Icon.vue';
|
|
139
138
|
import { useServices } from '@editor/hooks/use-services';
|
|
@@ -193,17 +192,6 @@ watch([disabledDataSource, disabledCodeBlock], ([dsDisabled, cbDisabled]) => {
|
|
|
193
192
|
}
|
|
194
193
|
});
|
|
195
194
|
|
|
196
|
-
/**
|
|
197
|
-
* 通过 inject 拿到 Editor 顶层注入的 `extendFormState`,转交给 HistoryDiffDialog
|
|
198
|
-
* 内部的 CompareForm,使差异对比表单的 filterFunction 能拿到完整的业务上下文。
|
|
199
|
-
* 未提供时为 undefined,CompareForm/MForm 会跳过 extendState 处理。
|
|
200
|
-
*/
|
|
201
|
-
const extendFormState = inject<((_state: FormState) => Record<string, any> | Promise<Record<string, any>>) | undefined>(
|
|
202
|
-
'extendFormState',
|
|
203
|
-
undefined,
|
|
204
|
-
);
|
|
205
|
-
const getPropsPanelFormState = inject<(() => FormState | undefined) | undefined>('getPropsPanelFormState', undefined);
|
|
206
|
-
|
|
207
195
|
const {
|
|
208
196
|
expanded,
|
|
209
197
|
toggleGroup,
|
|
@@ -316,7 +304,7 @@ const onCodeBlockGotoInitial = (id: string | number) => {
|
|
|
316
304
|
* 业务方亦可直接 import useHistoryRevert(options, services) 调用,无需自行挂载任何弹窗。
|
|
317
305
|
*/
|
|
318
306
|
const { onPageRevert, onDataSourceRevert, onCodeBlockRevert, onPageDiff, onDataSourceDiff, onCodeBlockDiff } =
|
|
319
|
-
useHistoryRevert({
|
|
307
|
+
useHistoryRevert({}, services);
|
|
320
308
|
|
|
321
309
|
/**
|
|
322
310
|
* 把内存中(已清空对应类别后的)历史状态重新写回 IndexedDB,
|
|
@@ -74,7 +74,7 @@ interface MountedDiffDialog {
|
|
|
74
74
|
* 弹窗组件动态 import,避免拖累其它消费者。供「确认回滚」与「查看差异」两种交互共用。
|
|
75
75
|
*/
|
|
76
76
|
const mountHistoryDiffDialog = async (
|
|
77
|
-
options: Pick<UseHistoryRevertOptions, 'appContext'
|
|
77
|
+
options: Pick<UseHistoryRevertOptions, 'appContext'> &
|
|
78
78
|
CustomDiffFormOptions & {
|
|
79
79
|
services?: Services;
|
|
80
80
|
isConfirm?: boolean;
|
|
@@ -90,10 +90,8 @@ const mountHistoryDiffDialog = async (
|
|
|
90
90
|
const app = createApp(historyDiffDialog, {
|
|
91
91
|
services: options.services,
|
|
92
92
|
isConfirm: options.isConfirm,
|
|
93
|
-
extendState: options.extendState,
|
|
94
93
|
loadConfig: options.loadConfig,
|
|
95
94
|
selfDiffFieldTypes: options.selfDiffFieldTypes,
|
|
96
|
-
compareFormState: options.compareFormState,
|
|
97
95
|
width: options.width,
|
|
98
96
|
size: options.size ?? options.services?.uiService?.get('propsPanelSize'),
|
|
99
97
|
onClose: options.onClose,
|
|
@@ -125,7 +123,7 @@ const mountHistoryDiffDialog = async (
|
|
|
125
123
|
*/
|
|
126
124
|
const confirmRevertWithDiffDialog = async (
|
|
127
125
|
payload: DiffDialogPayload,
|
|
128
|
-
options: Pick<UseHistoryRevertOptions, 'appContext'
|
|
126
|
+
options: Pick<UseHistoryRevertOptions, 'appContext'> &
|
|
129
127
|
CustomDiffFormOptions & {
|
|
130
128
|
services?: Services;
|
|
131
129
|
},
|
|
@@ -147,7 +145,7 @@ const confirmRevertWithDiffDialog = async (
|
|
|
147
145
|
*/
|
|
148
146
|
const viewHistoryDiffDialog = async (
|
|
149
147
|
payload: DiffDialogPayload,
|
|
150
|
-
options: Pick<UseHistoryRevertOptions, 'appContext'
|
|
148
|
+
options: Pick<UseHistoryRevertOptions, 'appContext'> &
|
|
151
149
|
CustomDiffFormOptions & {
|
|
152
150
|
services?: Services;
|
|
153
151
|
},
|
|
@@ -194,7 +192,7 @@ const viewHistoryDiffDialog = async (
|
|
|
194
192
|
export const useHistoryRevert = (options: UseHistoryRevertOptions = {}, services?: Services) => {
|
|
195
193
|
// 自动捕获调用方所在组件的 appContext(在 setup 中调用时),业务方亦可显式覆盖。
|
|
196
194
|
const appContext = options.appContext ?? getCurrentInstance()?.appContext ?? null;
|
|
197
|
-
const {
|
|
195
|
+
const { dialogWidth } = options;
|
|
198
196
|
|
|
199
197
|
/** 目标数据已被删除、无法回滚时的统一提示。 */
|
|
200
198
|
const showRevertTargetMissing = () => {
|
|
@@ -220,7 +218,6 @@ export const useHistoryRevert = (options: UseHistoryRevertOptions = {}, services
|
|
|
220
218
|
if (payload) {
|
|
221
219
|
return confirmRevertWithDiffDialog(payload, {
|
|
222
220
|
appContext,
|
|
223
|
-
extendState,
|
|
224
221
|
services,
|
|
225
222
|
...extra,
|
|
226
223
|
width: extra?.width ?? dialogWidth,
|
|
@@ -307,7 +304,7 @@ export const useHistoryRevert = (options: UseHistoryRevertOptions = {}, services
|
|
|
307
304
|
showRevertTargetMissing();
|
|
308
305
|
return Promise.resolve(null);
|
|
309
306
|
}
|
|
310
|
-
return runRevert(buildPageDiffPayload(index), {
|
|
307
|
+
return runRevert(buildPageDiffPayload(index), {}).then((result) =>
|
|
311
308
|
result ? services?.editorService.revertPageStep(index) : null,
|
|
312
309
|
);
|
|
313
310
|
};
|
|
@@ -341,22 +338,20 @@ export const useHistoryRevert = (options: UseHistoryRevertOptions = {}, services
|
|
|
341
338
|
if (payload) {
|
|
342
339
|
return viewHistoryDiffDialog(payload, {
|
|
343
340
|
appContext,
|
|
344
|
-
extendState,
|
|
345
341
|
services,
|
|
346
342
|
width: dialogWidth,
|
|
347
|
-
compareFormState: getPropsPanelFormState?.(),
|
|
348
343
|
});
|
|
349
344
|
}
|
|
350
345
|
};
|
|
351
346
|
|
|
352
347
|
const onDataSourceDiff = (id: string | number, index: number): Promise<void> | void => {
|
|
353
348
|
const payload = buildDataSourceDiffPayload(id, index);
|
|
354
|
-
if (payload) return viewHistoryDiffDialog(payload, { appContext,
|
|
349
|
+
if (payload) return viewHistoryDiffDialog(payload, { appContext, services, width: dialogWidth });
|
|
355
350
|
};
|
|
356
351
|
|
|
357
352
|
const onCodeBlockDiff = (id: string | number, index: number): Promise<void> | void => {
|
|
358
353
|
const payload = buildCodeBlockDiffPayload(id, index);
|
|
359
|
-
if (payload) return viewHistoryDiffDialog(payload, { appContext,
|
|
354
|
+
if (payload) return viewHistoryDiffDialog(payload, { appContext, services, width: dialogWidth });
|
|
360
355
|
};
|
|
361
356
|
|
|
362
357
|
/**
|
|
@@ -398,7 +393,6 @@ export const useHistoryRevert = (options: UseHistoryRevertOptions = {}, services
|
|
|
398
393
|
if (payload)
|
|
399
394
|
return viewHistoryDiffDialog(payload, {
|
|
400
395
|
appContext,
|
|
401
|
-
extendState,
|
|
402
396
|
services,
|
|
403
397
|
...extra,
|
|
404
398
|
width: extra?.width ?? dialogWidth,
|