@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.
Files changed (111) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +10 -16
  2. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +4 -4
  3. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +5 -8
  4. package/dist/es/components/ViewForm.vue_vue_type_script_setup_true_lang.js +3 -5
  5. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +3 -11
  6. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +29 -124
  7. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +5 -6
  8. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  9. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +4 -5
  10. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +6 -10
  11. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +5 -7
  12. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +1 -4
  13. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +4 -6
  14. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  15. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +9 -142
  16. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +51 -326
  17. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +2 -4
  18. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +3 -34
  19. package/dist/es/fields/StyleSetter/configs.js +663 -0
  20. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +5 -108
  21. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +3 -9
  22. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +5 -102
  23. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +5 -265
  24. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +5 -85
  25. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +3 -22
  26. package/dist/es/fields/configs/codeSelect.js +98 -0
  27. package/dist/es/fields/configs/displayConds.js +158 -0
  28. package/dist/es/fields/configs/eventSelect.js +225 -0
  29. package/dist/es/fields/configs/stickyAddButton.js +20 -0
  30. package/dist/es/fields/headless-validation.js +139 -0
  31. package/dist/es/headless.js +2 -0
  32. package/dist/es/hooks/use-compare-form.js +6 -23
  33. package/dist/es/hooks/use-form-context.js +19 -0
  34. package/dist/es/index.js +2 -1
  35. package/dist/es/initService.js +17 -1
  36. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +1 -7
  37. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +1 -11
  38. package/dist/es/layouts/history-list/useHistoryRevert.js +3 -11
  39. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +10 -23
  40. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +3 -13
  41. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  42. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +1 -1
  43. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  44. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +1 -1
  45. package/dist/es/plugin.js +33 -32
  46. package/dist/es/style.css +116 -60
  47. package/dist/es/utils/data-source/index.js +2 -1
  48. package/dist/es/utils/props.js +1 -4
  49. package/dist/es/utils/type-match-rules.js +2 -2
  50. package/dist/style.css +116 -60
  51. package/dist/themes/magic-admin.css +128 -67
  52. package/dist/tmagic-editor-headless.umd.cjs +10867 -0
  53. package/dist/tmagic-editor.umd.cjs +2123 -2015
  54. package/package.json +12 -7
  55. package/src/Editor.vue +12 -16
  56. package/src/components/CodeParams.vue +10 -4
  57. package/src/components/CompareForm.vue +4 -5
  58. package/src/components/ViewForm.vue +2 -3
  59. package/src/editorProps.ts +1 -2
  60. package/src/fields/Code.vue +2 -10
  61. package/src/fields/CodeSelect.vue +20 -116
  62. package/src/fields/CodeSelectCol.vue +3 -5
  63. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +1 -1
  64. package/src/fields/DataSourceFieldSelect/Index.vue +3 -5
  65. package/src/fields/DataSourceFields.vue +0 -7
  66. package/src/fields/DataSourceMethodSelect.vue +2 -5
  67. package/src/fields/DataSourceMethods.vue +1 -6
  68. package/src/fields/DataSourceMocks.vue +1 -10
  69. package/src/fields/DataSourceSelect.vue +1 -1
  70. package/src/fields/DisplayConds.vue +17 -157
  71. package/src/fields/EventSelect.vue +28 -375
  72. package/src/fields/KeyValue.vue +3 -4
  73. package/src/fields/StyleSetter/components/Border.vue +4 -38
  74. package/src/fields/StyleSetter/configs.ts +690 -0
  75. package/src/fields/StyleSetter/pro/Background.vue +4 -104
  76. package/src/fields/StyleSetter/pro/Border.vue +3 -12
  77. package/src/fields/StyleSetter/pro/Font.vue +4 -123
  78. package/src/fields/StyleSetter/pro/Layout.vue +4 -219
  79. package/src/fields/StyleSetter/pro/Position.vue +6 -110
  80. package/src/fields/StyleSetter/pro/Transform.vue +4 -30
  81. package/src/fields/configs/codeSelect.ts +127 -0
  82. package/src/fields/configs/displayConds.ts +184 -0
  83. package/src/fields/configs/eventSelect.ts +309 -0
  84. package/src/fields/configs/stickyAddButton.ts +31 -0
  85. package/src/fields/headless-validation.ts +177 -0
  86. package/src/headless.ts +33 -0
  87. package/src/hooks/use-compare-form.ts +7 -25
  88. package/src/hooks/use-form-context.ts +40 -0
  89. package/src/index.ts +2 -0
  90. package/src/initService.ts +23 -1
  91. package/src/layouts/history-list/HistoryDiffDialog.vue +0 -10
  92. package/src/layouts/history-list/HistoryListPanel.vue +1 -13
  93. package/src/layouts/history-list/useHistoryRevert.ts +7 -13
  94. package/src/layouts/props-panel/FormPanel.vue +17 -28
  95. package/src/layouts/props-panel/PropsPanel.vue +1 -4
  96. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +1 -1
  97. package/src/layouts/sidebar/code-block/useContentMenu.ts +1 -1
  98. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +1 -1
  99. package/src/layouts/sidebar/data-source/useContentMenu.ts +1 -1
  100. package/src/plugin.ts +32 -33
  101. package/src/theme/code-block.scss +0 -37
  102. package/src/theme/display-conds.scss +4 -0
  103. package/src/theme/event.scss +22 -45
  104. package/src/theme/props-panel.scss +17 -3
  105. package/src/theme/themes/magic-admin/index.scss +12 -1
  106. package/src/type.ts +10 -29
  107. package/src/utils/data-source/index.ts +1 -0
  108. package/src/utils/props.ts +1 -4
  109. package/src/utils/type-match-rules.ts +8 -3
  110. package/types/headless.d.ts +18 -0
  111. package/types/index.d.ts +154 -179
@@ -1,4 +1,5 @@
1
- import { MContainer, defineFormItem } from "@tmagic/form";
1
+ import { transformConfig } from "../configs.js";
2
+ import { MContainer } from "@tmagic/form";
2
3
  import { createBlock, defineComponent, openBlock, unref } from "vue";
3
4
  //#region packages/editor/src/fields/StyleSetter/pro/Transform.vue?vue&type=script&setup=true&lang.ts
4
5
  var Transform_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
@@ -14,26 +15,6 @@ var Transform_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
14
15
  emits: ["change", "addDiffCount"],
15
16
  setup(__props, { emit: __emit }) {
16
17
  const emit = __emit;
17
- const config = defineFormItem({
18
- name: "transform",
19
- items: [{
20
- name: "rotate",
21
- text: "旋转角度",
22
- labelWidth: "68px",
23
- type: "data-source-field-select",
24
- checkStrictly: false,
25
- dataSourceFieldType: ["string", "number"],
26
- fieldConfig: { type: "text" }
27
- }, {
28
- name: "scale",
29
- text: "缩放",
30
- labelWidth: "68px",
31
- type: "data-source-field-select",
32
- checkStrictly: false,
33
- dataSourceFieldType: ["string", "number"],
34
- fieldConfig: { type: "text" }
35
- }]
36
- });
37
18
  const change = (value, eventData) => {
38
19
  emit("change", value, eventData);
39
20
  };
@@ -41,7 +22,7 @@ var Transform_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
41
22
  return (_ctx, _cache) => {
42
23
  return openBlock(), createBlock(unref(MContainer), {
43
24
  prop: __props.prop,
44
- config: unref(config),
25
+ config: unref(transformConfig),
45
26
  model: __props.values,
46
27
  "last-values": __props.lastValues,
47
28
  "is-compare": __props.isCompare,
@@ -0,0 +1,98 @@
1
+ import codeBlock_default from "../../services/codeBlock.js";
2
+ import dataSource_default from "../../services/dataSource.js";
3
+ import { stickyAddButton } from "./stickyAddButton.js";
4
+ import { HookCodeType, HookType } from "@tmagic/core";
5
+ import { isEmpty } from "lodash-es";
6
+ //#region packages/editor/src/fields/configs/codeSelect.ts
7
+ /**
8
+ * `fields/CodeSelect.vue` 内部渲染的钩子列表配置。
9
+ *
10
+ * 由组件与无渲染校验的嵌套配置共用:组件用它渲染,嵌套配置用它让父表单校验到这些字段。
11
+ */
12
+ var createCodeSelectConfig = (config) => {
13
+ return {
14
+ type: "group-list",
15
+ name: "hookData",
16
+ enableToggleMode: false,
17
+ expandAll: true,
18
+ defaultAdd: () => ({
19
+ codeType: HookCodeType.CODE,
20
+ codeId: ""
21
+ }),
22
+ ...stickyAddButton(`添加${config.text || ""}`),
23
+ title: (_mForm, { model, index }) => {
24
+ if (model.codeType === HookCodeType.DATA_SOURCE_METHOD) {
25
+ if (Array.isArray(model.codeId)) {
26
+ if (model.codeId.length < 2) return index;
27
+ return `${dataSource_default.getDataSourceById(model.codeId[0])?.title} / ${model.codeId[1]}`;
28
+ }
29
+ return Array.isArray(model.codeId) ? model.codeId.join("/") : index;
30
+ }
31
+ const codeContent = codeBlock_default.getCodeContentById(model.codeId);
32
+ if (codeContent) return codeContent.name;
33
+ return model.codeId || index;
34
+ },
35
+ titlePrefix: config.name === void 0 ? void 0 : String(config.name),
36
+ items: [
37
+ {
38
+ text: "代码类型",
39
+ type: "select",
40
+ name: "codeType",
41
+ rules: [{
42
+ typeMatch: true,
43
+ trigger: "change"
44
+ }],
45
+ options: [{
46
+ value: HookCodeType.CODE,
47
+ text: "代码块"
48
+ }, {
49
+ value: HookCodeType.DATA_SOURCE_METHOD,
50
+ text: "数据源方法"
51
+ }],
52
+ defaultValue: HookCodeType.CODE,
53
+ onChange: (_mForm, v, { setModel }) => {
54
+ if (v === HookCodeType.DATA_SOURCE_METHOD) setModel("codeId", []);
55
+ else setModel("codeId", "");
56
+ return v;
57
+ }
58
+ },
59
+ {
60
+ type: "code-select-col",
61
+ name: "codeId",
62
+ text: "代码块",
63
+ rules: [{
64
+ typeMatch: true,
65
+ trigger: "change"
66
+ }],
67
+ display: (_mForm, { model }) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
68
+ notEditable: () => !codeBlock_default.getEditStatus()
69
+ },
70
+ {
71
+ type: "data-source-method-select",
72
+ name: "codeId",
73
+ text: "数据源字段",
74
+ rules: [{
75
+ typeMatch: true,
76
+ trigger: "change"
77
+ }],
78
+ display: (_mForm, { model }) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
79
+ notEditable: () => !dataSource_default.get("editable")
80
+ }
81
+ ]
82
+ };
83
+ };
84
+ /**
85
+ * `code-select` 的旧数据兼容:钩子值可能是空值或空数组,改写成 `{ hookType, hookData }`。
86
+ *
87
+ * 表单值初始化时由 `code-select` 的 `effect` 执行(`applyMountValueEffects`);
88
+ * 组件里的 `watch`(无 immediate)只兜运行期被置空。必须幂等。
89
+ */
90
+ var normalizeCodeSelectValue = (model, name) => {
91
+ if (!model) return;
92
+ if (isEmpty(model[name])) model[name] = {
93
+ hookType: HookType.CODE,
94
+ hookData: []
95
+ };
96
+ };
97
+ //#endregion
98
+ export { createCodeSelectConfig, normalizeCodeSelectValue };
@@ -0,0 +1,158 @@
1
+ import { getCascaderOptionsFromFields, getFieldType } from "../../utils/data-source/index.js";
2
+ import dataSource_default from "../../services/dataSource.js";
3
+ import { stickyAddButton } from "./stickyAddButton.js";
4
+ import { removeDataSourceFieldPrefix } from "@tmagic/utils";
5
+ //#region packages/editor/src/fields/configs/displayConds.ts
6
+ /**
7
+ * `fields/DisplayConds.vue` 内部渲染的条件列表配置。
8
+ *
9
+ * 外层是条件组,内层 `cond` 是组内条件(groupList,每个字段单独一行)。
10
+ * 由组件与无渲染校验的嵌套配置共用。
11
+ *
12
+ * `parentFields` 由调用方求值:有父级字段路径时用 cascader 在该路径下选字段,
13
+ * 没有时用 data-source-field-select 从头选。
14
+ */
15
+ var createDisplayCondsConfig = (config, name, parentFields) => {
16
+ const resolveFieldPath = (path) => {
17
+ const [id, ...fieldNames] = path;
18
+ return {
19
+ ds: id ? dataSource_default.getDataSourceById(removeDataSourceFieldPrefix(`${id}`)) : void 0,
20
+ fieldNames
21
+ };
22
+ };
23
+ const fieldOnChange = (_formState, v, { model }) => {
24
+ const { ds, fieldNames } = resolveFieldPath([...parentFields, ...v]);
25
+ const type = getFieldType(ds, fieldNames);
26
+ if (type === "number") model.value = Number(model.value);
27
+ else if (type === "boolean") model.value = Boolean(model.value);
28
+ else if (type === "null") model.value = null;
29
+ else model.value = `${model.value}`;
30
+ return v;
31
+ };
32
+ const fieldItem = parentFields.length ? {
33
+ type: "cascader",
34
+ options: () => {
35
+ const { ds, fieldNames } = resolveFieldPath(parentFields);
36
+ if (!ds) return [];
37
+ let fields = ds.fields || [];
38
+ fieldNames.forEach((key) => {
39
+ fields = fields.find((f) => f.name === key)?.fields || [];
40
+ });
41
+ return getCascaderOptionsFromFields(fields, [
42
+ "string",
43
+ "number",
44
+ "boolean",
45
+ "any"
46
+ ]);
47
+ },
48
+ name: "field",
49
+ value: "key",
50
+ text: "字段",
51
+ checkStrictly: false,
52
+ onChange: fieldOnChange,
53
+ defaultValue: () => [],
54
+ rules: [{
55
+ required: true,
56
+ trigger: "blur",
57
+ message: "请选择字段"
58
+ }, {
59
+ typeMatch: true,
60
+ trigger: "change"
61
+ }]
62
+ } : {
63
+ type: "data-source-field-select",
64
+ name: "field",
65
+ value: "key",
66
+ text: "字段",
67
+ checkStrictly: false,
68
+ dataSourceFieldType: [
69
+ "string",
70
+ "number",
71
+ "boolean",
72
+ "any"
73
+ ],
74
+ onChange: fieldOnChange,
75
+ defaultValue: () => [],
76
+ rules: [{
77
+ required: true,
78
+ trigger: "blur",
79
+ message: "请选择字段"
80
+ }, {
81
+ typeMatch: true,
82
+ trigger: "change"
83
+ }]
84
+ };
85
+ return {
86
+ type: "groupList",
87
+ name,
88
+ titlePrefix: config.titlePrefix,
89
+ expandAll: true,
90
+ enableToggleMode: false,
91
+ defaultAdd: { cond: [] },
92
+ ...stickyAddButton(`新增${config.titlePrefix || "条件组"}`),
93
+ flat: config.flat,
94
+ defaultValue: config.defaultValue ?? [],
95
+ rules: config.rules ?? [{ typeMatch: true }],
96
+ items: [{
97
+ type: "groupList",
98
+ name: "cond",
99
+ titlePrefix: "条件",
100
+ expandAll: true,
101
+ enableToggleMode: false,
102
+ copyable: true,
103
+ movable: false,
104
+ flat: true,
105
+ labelWidth: "80px",
106
+ ...stickyAddButton("新增条件"),
107
+ items: [
108
+ fieldItem,
109
+ {
110
+ type: "cond-op-select",
111
+ parentFields,
112
+ text: "条件",
113
+ name: "op",
114
+ rules: [{
115
+ required: true,
116
+ trigger: "blur",
117
+ message: "请选择条件"
118
+ }, {
119
+ typeMatch: true,
120
+ trigger: "change"
121
+ }]
122
+ },
123
+ {
124
+ name: "value",
125
+ text: "值",
126
+ type: (_mForm, { model }) => {
127
+ const { ds, fieldNames } = resolveFieldPath([...parentFields, ...model.field || []]);
128
+ const type = getFieldType(ds, fieldNames);
129
+ if (type === "number") return "number";
130
+ if (type === "boolean") return "select";
131
+ if (type === "null") return "display";
132
+ return "text";
133
+ },
134
+ options: [{
135
+ text: "true",
136
+ value: true
137
+ }, {
138
+ text: "false",
139
+ value: false
140
+ }],
141
+ display: (_mForm, { model }) => !["between", "not_between"].includes(model.op),
142
+ displayText: (_mForm, { model }) => {
143
+ if (model.value === null) return "null";
144
+ return model.value;
145
+ }
146
+ },
147
+ {
148
+ name: "range",
149
+ text: "值",
150
+ type: "number-range",
151
+ display: (_mForm, { model }) => ["between", "not_between"].includes(model.op)
152
+ }
153
+ ]
154
+ }]
155
+ };
156
+ };
157
+ //#endregion
158
+ export { createDisplayCondsConfig };
@@ -0,0 +1,225 @@
1
+ import props_default from "../../services/props.js";
2
+ import editor_default from "../../services/editor.js";
3
+ import codeBlock_default from "../../services/codeBlock.js";
4
+ import dataSource_default from "../../services/dataSource.js";
5
+ import events_default from "../../services/events.js";
6
+ import { getCompActionAllowedValues, getCompActionOptions, getEventNameAllowedValues, getEventNameOptions, normalizeCompActionValue } from "../../utils/event.js";
7
+ import { stickyAddButton } from "./stickyAddButton.js";
8
+ import { ActionType } from "@tmagic/core";
9
+ import { has } from "lodash-es";
10
+ import { defineFormItem } from "@tmagic/form/headless";
11
+ //#region packages/editor/src/fields/configs/eventSelect.ts
12
+ /**
13
+ * `fields/EventSelect.vue` 内部渲染的各段配置。
14
+ *
15
+ * 由组件与无渲染校验的嵌套配置共用:组件用它们渲染,嵌套配置用它们让父表单校验到这些字段。
16
+ * 每段都支持通过 `config.xxxConfig` 覆盖,覆盖逻辑也必须两边一致,因此一并收口在此。
17
+ */
18
+ /** 事件名称下拉框配置,渲染在每张事件卡片的头部 */
19
+ var createEventNameConfig = (config) => {
20
+ return {
21
+ name: "name",
22
+ text: "事件类型",
23
+ type: (_mForm, { formValue }) => {
24
+ if (config.src !== "component" || formValue.type === "page-fragment-container" && formValue.pageFragmentId) return "cascader";
25
+ return "select";
26
+ },
27
+ labelWidth: "70px",
28
+ checkStrictly: () => config.src !== "component",
29
+ valueSeparator: ".",
30
+ options: (_mForm, { formValue }) => getEventNameOptions(config.src, formValue),
31
+ rules: [{
32
+ validator: ({ value, callback }, { formValue }) => {
33
+ const allowedNames = getEventNameAllowedValues(config, formValue);
34
+ if (allowedNames && allowedNames.size > 0 && value && !allowedNames.has(value)) return callback(`事件名(${value})不存在`);
35
+ callback();
36
+ },
37
+ trigger: "blur"
38
+ }],
39
+ ...config.eventNameConfig
40
+ };
41
+ };
42
+ var createActionTypeOptions = () => {
43
+ const o = [{
44
+ text: "组件",
45
+ label: "组件",
46
+ value: ActionType.COMP
47
+ }];
48
+ if (!props_default.getDisabledCodeBlock()) o.push({
49
+ text: "代码",
50
+ label: "代码",
51
+ disabled: !Object.keys(codeBlock_default.getCodeDsl() || {}).length,
52
+ value: ActionType.CODE
53
+ });
54
+ if (!props_default.getDisabledDataSource()) o.push({
55
+ text: "数据源",
56
+ label: "数据源",
57
+ value: ActionType.DATA_SOURCE
58
+ });
59
+ return o;
60
+ };
61
+ /** 联动类型 */
62
+ var createActionTypeConfig = (config) => {
63
+ return {
64
+ name: "actionType",
65
+ text: "联动类型",
66
+ type: "select",
67
+ defaultValue: ActionType.COMP,
68
+ options: createActionTypeOptions(),
69
+ rules: [{
70
+ required: true,
71
+ message: "联动类型不能为空"
72
+ }, {
73
+ typeMatch: true,
74
+ trigger: "blur"
75
+ }],
76
+ onChange: (_mForm, _v, { setModel }) => {
77
+ setModel("to", "");
78
+ setModel("method", "");
79
+ setModel("codeId", "");
80
+ setModel("dataSourceMethod", []);
81
+ },
82
+ ...config.actionTypeConfig
83
+ };
84
+ };
85
+ /** 联动组件 */
86
+ var createTargetCompConfig = (config) => {
87
+ return {
88
+ name: "to",
89
+ text: "联动组件",
90
+ type: "ui-select",
91
+ display: (_mForm, { model }) => model.actionType === ActionType.COMP,
92
+ onChange: (_mForm, _v, { setModel }) => {
93
+ setModel("method", "");
94
+ },
95
+ rules: [{
96
+ typeMatch: true,
97
+ trigger: "blur"
98
+ }],
99
+ ...config.targetCompConfig
100
+ };
101
+ };
102
+ /** 联动组件动作 */
103
+ var createCompActionConfig = (config) => {
104
+ return {
105
+ name: "method",
106
+ text: "动作",
107
+ type: (_mForm, { model }) => {
108
+ const to = editor_default.getNodeById(model.to);
109
+ if (to?.type === "page-fragment-container" && to.pageFragmentId) return "cascader";
110
+ return "select";
111
+ },
112
+ checkStrictly: () => config.src !== "component",
113
+ display: (_mForm, { model }) => model.actionType === ActionType.COMP,
114
+ options: (_mForm, { model }) => getCompActionOptions(model.to),
115
+ rules: [{
116
+ trigger: "blur",
117
+ validator: ({ value, callback }, { model }) => {
118
+ const allowedMethods = getCompActionAllowedValues(config, model);
119
+ const normalized = normalizeCompActionValue(value);
120
+ if (allowedMethods && allowedMethods.size > 0 && normalized && !allowedMethods.has(normalized)) return callback(`动作名(${normalized})不存在`);
121
+ callback();
122
+ }
123
+ }],
124
+ ...config.compActionConfig
125
+ };
126
+ };
127
+ /** 代码联动 */
128
+ var createCodeActionConfig = (config) => {
129
+ return {
130
+ type: "code-select-col",
131
+ text: "代码块",
132
+ name: "codeId",
133
+ notEditable: () => !codeBlock_default.getEditStatus(),
134
+ display: (_mForm, { model }) => model.actionType === ActionType.CODE,
135
+ ...config.codeActionConfig
136
+ };
137
+ };
138
+ /** 数据源联动 */
139
+ var createDataSourceActionConfig = (config) => {
140
+ return {
141
+ type: "data-source-method-select",
142
+ text: "数据源方法",
143
+ name: "dataSourceMethod",
144
+ notEditable: () => !dataSource_default.get("editable"),
145
+ display: (_mForm, { model }) => model.actionType === ActionType.DATA_SOURCE,
146
+ ...config.dataSourceActionConfig
147
+ };
148
+ };
149
+ /** 单张事件里的动作组 */
150
+ var createActionsConfig = (config) => defineFormItem({
151
+ type: "group-list",
152
+ name: "actions",
153
+ expandAll: true,
154
+ enableToggleMode: false,
155
+ titlePrefix: "动作",
156
+ labelPosition: "top",
157
+ flat: true,
158
+ ...stickyAddButton("新增动作"),
159
+ items: [
160
+ createActionTypeConfig(config),
161
+ createTargetCompConfig(config),
162
+ createCompActionConfig(config),
163
+ createCodeActionConfig(config),
164
+ createDataSourceActionConfig(config)
165
+ ]
166
+ });
167
+ /** 事件列表(外层 group-list)。事件名走 title slot,body 只放动作组。 */
168
+ var createEventSelectConfig = (config, name, options) => defineFormItem({
169
+ type: "group-list",
170
+ name,
171
+ titlePrefix: "事件",
172
+ expandAll: true,
173
+ enableToggleMode: false,
174
+ movable: false,
175
+ defaultAdd: {
176
+ name: "",
177
+ actions: []
178
+ },
179
+ ...stickyAddButton("添加事件"),
180
+ items: [...options?.includeEventName ? [createEventNameConfig(config)] : [], createActionsConfig(config)]
181
+ });
182
+ /** 兼容旧数据格式(事件列表里没有 actions)时渲染的表格配置,本身不带校验规则 */
183
+ var createLegacyTableConfig = (config) => defineFormItem({
184
+ type: "table",
185
+ name: "events",
186
+ items: [
187
+ {
188
+ name: "name",
189
+ label: "事件名",
190
+ type: createEventNameConfig(config).type,
191
+ options: (_mForm, { formValue }) => events_default.getEvent(formValue.type, { node: editor_default.getNodeById(formValue.id) }).map((option) => ({
192
+ text: option.label,
193
+ value: option.value
194
+ }))
195
+ },
196
+ {
197
+ name: "to",
198
+ label: "联动组件",
199
+ type: "ui-select"
200
+ },
201
+ {
202
+ name: "method",
203
+ label: "动作",
204
+ type: createCompActionConfig(config).type,
205
+ options: (_mForm, { model, formValue }) => {
206
+ const node = editor_default.getNodeById(model.to) || formValue;
207
+ if (!node?.type) return [];
208
+ return events_default.getMethod(node.type, {
209
+ targetId: model.to,
210
+ node
211
+ }).map((option) => ({
212
+ text: option.label,
213
+ value: option.value
214
+ }));
215
+ }
216
+ }
217
+ ]
218
+ });
219
+ /** 事件列表是否为旧数据格式(列表项里没有 actions) */
220
+ var isLegacyEventValue = (events) => {
221
+ if (!Array.isArray(events) || events.length === 0) return false;
222
+ return !has(events[0], "actions");
223
+ };
224
+ //#endregion
225
+ export { createActionsConfig, createEventNameConfig, createEventSelectConfig, createLegacyTableConfig, isLegacyEventValue };
@@ -0,0 +1,20 @@
1
+ //#region packages/editor/src/fields/configs/stickyAddButton.ts
2
+ /**
3
+ * 属性面板列表字段共用的吸底全宽「添加」按钮,展开到 group-list 配置上。
4
+ *
5
+ * 吸底按钮会盖住列表底部,新增的项必须同时滚进视口才看得见,两者一起给出避免漏配。
6
+ */
7
+ var stickyAddButton = (text) => ({
8
+ scrollLastItemIntoView: true,
9
+ addButtonConfig: {
10
+ sticky: true,
11
+ text,
12
+ props: {
13
+ type: "primary",
14
+ plain: true,
15
+ text: false
16
+ }
17
+ }
18
+ });
19
+ //#endregion
20
+ export { stickyAddButton };
@@ -0,0 +1,139 @@
1
+ import { editorTypeMatchRules, validateDataSourceFieldSelect } from "../utils/type-match-rules.js";
2
+ import { createCodeSelectConfig, normalizeCodeSelectValue } from "./configs/codeSelect.js";
3
+ import { createEventSelectConfig, isLegacyEventValue } from "./configs/eventSelect.js";
4
+ import { createDisplayCondsConfig } from "./configs/displayConds.js";
5
+ import { createStyleSetterConfig } from "./StyleSetter/configs.js";
6
+ import { filterFunction } from "@tmagic/form/headless";
7
+ //#region packages/editor/src/fields/headless-validation.ts
8
+ var getName = (config) => `${config.name ?? ""}`;
9
+ /** `code-select` 旧数据兼容:空值改写成 `{ hookType, hookData }`。 */
10
+ var codeSelectEffect = ({ config, model }) => {
11
+ normalizeCodeSelectValue(model, getName(config));
12
+ };
13
+ /** `event-select`:表单 init 对未声明 defaultValue 的自定义 type 会写成空串,统一收成数组。 */
14
+ var eventSelectEffect = ({ config, model }) => {
15
+ const name = getName(config);
16
+ if (model && !Array.isArray(model[name])) model[name] = [];
17
+ };
18
+ /**
19
+ * `code-select` 的内部配置。
20
+ *
21
+ * 对应 `fields/CodeSelect.vue` 内部
22
+ * `<MContainer :config="codeConfig" :model="model[name]" :prop="prop">`。
23
+ *
24
+ * @param ctx - innerConfig 回调入参
25
+ * @returns 内部容器的 config / model / prop
26
+ */
27
+ var codeSelectInnerConfig = ({ config, model, prop }) => {
28
+ const name = getName(config);
29
+ return {
30
+ config: createCodeSelectConfig(config),
31
+ model: model?.[name],
32
+ prop
33
+ };
34
+ };
35
+ /**
36
+ * `display-conds` 的内部配置。
37
+ *
38
+ * 对应 `fields/DisplayConds.vue` 内部把同一份 groupList 配置交给 `MGroupList`。
39
+ *
40
+ * @param ctx - innerConfig 回调入参
41
+ * @returns 内部 group-list 配置;prop 基准为 parentProp,避免 name 被拼两次
42
+ */
43
+ var displayCondsInnerConfig = ({ config, model, prop, parentProp, mForm }) => {
44
+ const name = getName(config);
45
+ const parentFields = filterFunction(mForm, config.parentFields, {
46
+ model,
47
+ config,
48
+ prop
49
+ }) || [];
50
+ return {
51
+ config: createDisplayCondsConfig(config, name, parentFields),
52
+ prop: parentProp
53
+ };
54
+ };
55
+ /**
56
+ * `event-select` 的内部配置。
57
+ *
58
+ * 对应 `fields/EventSelect.vue`:列表走 group-list,事件名渲染在 title slot,
59
+ * 无渲染校验仍把事件名当作列表项字段,路径为 `<prop>.<index>.name`。
60
+ *
61
+ * @param ctx - innerConfig 回调入参
62
+ * @returns 合成的 group-list 配置;旧数据格式返回 null,不参与校验
63
+ */
64
+ var eventSelectInnerConfig = ({ config, model, parentProp }) => {
65
+ const name = getName(config);
66
+ const events = model?.[name];
67
+ if (!Array.isArray(events) || isLegacyEventValue(events)) return null;
68
+ return {
69
+ config: createEventSelectConfig(config, name, { includeEventName: true }),
70
+ prop: parentProp
71
+ };
72
+ };
73
+ /**
74
+ * `style-setter` 的内部配置。
75
+ *
76
+ * 对应 `fields/StyleSetter/Index.vue`:6 个面板共用 `:values="model[name]"`、`:prop="prop || name"`。
77
+ * `theme` 按 `useTheme` 缺省空串传入,只改 flexWrap 的 UI childType,不影响校验字段。
78
+ *
79
+ * @param ctx - innerConfig 回调入参
80
+ * @returns style 面板配置及 styleModel
81
+ */
82
+ var styleSetterInnerConfig = ({ config, model, prop }) => {
83
+ const name = getName(config);
84
+ const styleModel = model?.[name] ?? {};
85
+ return {
86
+ config: createStyleSetterConfig(styleModel, "", validateDataSourceFieldSelect),
87
+ model: styleModel,
88
+ prop
89
+ };
90
+ };
91
+ /**
92
+ * 编辑器字段的无渲染登记表(不含 Vue 组件)。
93
+ *
94
+ * Node 里 `registerFields(editorFields)` 即可配合 `validateForm` / `submitForm`。
95
+ * 安装编辑器时由 plugin 把 `component` 叠上去再传给 `@tmagic/form`。
96
+ *
97
+ * - 叶子:内部只渲染叶子 UI,或把子表单渲染在独立的 MForm / MFormBox 实例里
98
+ * - innerConfig:内部再渲染 MContainer / MPanel / MGroupList,把运行期配置交出来
99
+ * - typeMatch:该 type 自身的类型匹配校验
100
+ *
101
+ * innerConfig 返回的 config / model / prop 与组件模板里传给内部容器的那一组保持一一对应,
102
+ * 且配置本身与组件共用同一份工厂(`fields/configs/`)。
103
+ */
104
+ var editorFields = {
105
+ "vs-code": {},
106
+ "code-link": {},
107
+ "ui-select": { typeMatch: editorTypeMatchRules["ui-select"] },
108
+ "cond-op-select": { typeMatch: editorTypeMatchRules["cond-op-select"] },
109
+ "page-fragment-select": { typeMatch: editorTypeMatchRules["page-fragment-select"] },
110
+ "data-source-select": { typeMatch: editorTypeMatchRules["data-source-select"] },
111
+ "data-source-input": { typeMatch: editorTypeMatchRules["data-source-input"] },
112
+ "key-value": { typeMatch: editorTypeMatchRules["key-value"] },
113
+ "code-select-col": { typeMatch: editorTypeMatchRules["code-select-col"] },
114
+ "data-source-fields": { typeMatch: editorTypeMatchRules["data-source-fields"] },
115
+ "data-source-mocks": { typeMatch: editorTypeMatchRules["data-source-mocks"] },
116
+ "data-source-methods": { typeMatch: editorTypeMatchRules["data-source-methods"] },
117
+ "data-source-method-select": { typeMatch: editorTypeMatchRules["data-source-method-select"] },
118
+ "data-source-field-select": { typeMatch: editorTypeMatchRules["data-source-field-select"] },
119
+ "code-select": {
120
+ effect: codeSelectEffect,
121
+ innerConfig: codeSelectInnerConfig,
122
+ typeMatch: editorTypeMatchRules["code-select"]
123
+ },
124
+ "display-conds": {
125
+ innerConfig: displayCondsInnerConfig,
126
+ typeMatch: editorTypeMatchRules["display-conds"]
127
+ },
128
+ "event-select": {
129
+ effect: eventSelectEffect,
130
+ innerConfig: eventSelectInnerConfig,
131
+ typeMatch: editorTypeMatchRules["event-select"]
132
+ },
133
+ "style-setter": {
134
+ innerConfig: styleSetterInnerConfig,
135
+ typeMatch: editorTypeMatchRules["style-setter"]
136
+ }
137
+ };
138
+ //#endregion
139
+ export { editorFields };
@@ -0,0 +1,2 @@
1
+ import { editorFields } from "./fields/headless-validation.js";
2
+ export { editorFields };