mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-09-14 16:09:08 +00:00
将字段挂载时的 model 写入迁移至 registerField effect, 渲染与无渲染校验共用 applyMountValueEffects; nested 重命名为 innerConfig 以区分配置派生与值写入。
178 lines
6.9 KiB
TypeScript
178 lines
6.9 KiB
TypeScript
/*
|
||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||
*
|
||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
|
||
import {
|
||
type DisplayCondsConfig,
|
||
type EventSelectConfig,
|
||
type FieldInnerConfig,
|
||
type FieldMountValueEffect,
|
||
filterFunction,
|
||
type FormItemConfig,
|
||
type HeadlessFieldOptions,
|
||
} from '@tmagic/form/headless';
|
||
import type { StyleSchema } from '@tmagic/schema';
|
||
|
||
import { editorTypeMatchRules, validateDataSourceFieldSelect } from '@editor/utils/type-match-rules';
|
||
|
||
import { createCodeSelectConfig, normalizeCodeSelectValue } from './configs/codeSelect';
|
||
import { createDisplayCondsConfig } from './configs/displayConds';
|
||
import { createEventSelectConfig, isLegacyEventValue } from './configs/eventSelect';
|
||
import { createStyleSetterConfig } from './StyleSetter/configs';
|
||
|
||
const getName = (config: FormItemConfig): string => `${(config as any).name ?? ''}`;
|
||
|
||
/** `code-select` 旧数据兼容:空值改写成 `{ hookType, hookData }`。 */
|
||
const codeSelectEffect: FieldMountValueEffect = ({ config, model }) => {
|
||
normalizeCodeSelectValue(model, getName(config));
|
||
};
|
||
|
||
/** `event-select`:表单 init 对未声明 defaultValue 的自定义 type 会写成空串,统一收成数组。 */
|
||
const eventSelectEffect: FieldMountValueEffect = ({ config, model }) => {
|
||
const name = getName(config);
|
||
if (model && !Array.isArray(model[name])) {
|
||
model[name] = [];
|
||
}
|
||
};
|
||
|
||
/**
|
||
* `code-select` 的内部配置。
|
||
*
|
||
* 对应 `fields/CodeSelect.vue` 内部
|
||
* `<MContainer :config="codeConfig" :model="model[name]" :prop="prop">`。
|
||
*
|
||
* @param ctx - innerConfig 回调入参
|
||
* @returns 内部容器的 config / model / prop
|
||
*/
|
||
const codeSelectInnerConfig: FieldInnerConfig = ({ config, model, prop }) => {
|
||
const name = getName(config);
|
||
|
||
return {
|
||
config: createCodeSelectConfig(config as any),
|
||
model: model?.[name],
|
||
prop,
|
||
};
|
||
};
|
||
|
||
/**
|
||
* `display-conds` 的内部配置。
|
||
*
|
||
* 对应 `fields/DisplayConds.vue` 内部把同一份 groupList 配置交给 `MGroupList`。
|
||
*
|
||
* @param ctx - innerConfig 回调入参
|
||
* @returns 内部 group-list 配置;prop 基准为 parentProp,避免 name 被拼两次
|
||
*/
|
||
const displayCondsInnerConfig: FieldInnerConfig = ({ config, model, prop, parentProp, mForm }) => {
|
||
const name = getName(config);
|
||
const parentFields =
|
||
filterFunction<string[]>(mForm, (config as DisplayCondsConfig).parentFields, {
|
||
model,
|
||
config,
|
||
prop,
|
||
}) || [];
|
||
|
||
return {
|
||
config: createDisplayCondsConfig(config as DisplayCondsConfig, name, parentFields),
|
||
// 内部 group-list 复用了字段自身的 name,prop 基准要退回父级,否则 name 会被拼两次
|
||
prop: parentProp,
|
||
};
|
||
};
|
||
|
||
/**
|
||
* `event-select` 的内部配置。
|
||
*
|
||
* 对应 `fields/EventSelect.vue`:列表走 group-list,事件名渲染在 title slot,
|
||
* 无渲染校验仍把事件名当作列表项字段,路径为 `<prop>.<index>.name`。
|
||
*
|
||
* @param ctx - innerConfig 回调入参
|
||
* @returns 合成的 group-list 配置;旧数据格式返回 null,不参与校验
|
||
*/
|
||
const eventSelectInnerConfig: FieldInnerConfig = ({ config, model, parentProp }) => {
|
||
const name = getName(config);
|
||
const events = model?.[name];
|
||
|
||
// 旧数据格式走的是另一套表格配置,其中不含任何 rules,不参与校验
|
||
if (!Array.isArray(events) || isLegacyEventValue(events)) return null;
|
||
|
||
return {
|
||
config: createEventSelectConfig(config as EventSelectConfig, name, { includeEventName: true }),
|
||
prop: parentProp,
|
||
};
|
||
};
|
||
|
||
/**
|
||
* `style-setter` 的内部配置。
|
||
*
|
||
* 对应 `fields/StyleSetter/Index.vue`:6 个面板共用 `:values="model[name]"`、`:prop="prop || name"`。
|
||
* `theme` 按 `useTheme` 缺省空串传入,只改 flexWrap 的 UI childType,不影响校验字段。
|
||
*
|
||
* @param ctx - innerConfig 回调入参
|
||
* @returns style 面板配置及 styleModel
|
||
*/
|
||
const styleSetterInnerConfig: FieldInnerConfig = ({ config, model, prop }) => {
|
||
const name = getName(config);
|
||
const styleModel = (model?.[name] ?? {}) as Partial<StyleSchema>;
|
||
|
||
return {
|
||
config: createStyleSetterConfig(styleModel, '', validateDataSourceFieldSelect),
|
||
model: styleModel,
|
||
prop,
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 编辑器字段的无渲染登记表(不含 Vue 组件)。
|
||
*
|
||
* Node 里 `registerFields(editorFields)` 即可配合 `validateForm` / `submitForm`。
|
||
* 安装编辑器时由 plugin 把 `component` 叠上去再传给 `@tmagic/form`。
|
||
*
|
||
* - 叶子:内部只渲染叶子 UI,或把子表单渲染在独立的 MForm / MFormBox 实例里
|
||
* - innerConfig:内部再渲染 MContainer / MPanel / MGroupList,把运行期配置交出来
|
||
* - typeMatch:该 type 自身的类型匹配校验
|
||
*
|
||
* innerConfig 返回的 config / model / prop 与组件模板里传给内部容器的那一组保持一一对应,
|
||
* 且配置本身与组件共用同一份工厂(`fields/configs/`)。
|
||
*/
|
||
export const editorFields: Record<string, HeadlessFieldOptions> = {
|
||
'vs-code': {},
|
||
'code-link': {},
|
||
'ui-select': { typeMatch: editorTypeMatchRules['ui-select'] },
|
||
'cond-op-select': { typeMatch: editorTypeMatchRules['cond-op-select'] },
|
||
'page-fragment-select': { typeMatch: editorTypeMatchRules['page-fragment-select'] },
|
||
'data-source-select': { typeMatch: editorTypeMatchRules['data-source-select'] },
|
||
'data-source-input': { typeMatch: editorTypeMatchRules['data-source-input'] },
|
||
'key-value': { typeMatch: editorTypeMatchRules['key-value'] },
|
||
'code-select-col': { typeMatch: editorTypeMatchRules['code-select-col'] },
|
||
'data-source-fields': { typeMatch: editorTypeMatchRules['data-source-fields'] },
|
||
'data-source-mocks': { typeMatch: editorTypeMatchRules['data-source-mocks'] },
|
||
'data-source-methods': { typeMatch: editorTypeMatchRules['data-source-methods'] },
|
||
'data-source-method-select': { typeMatch: editorTypeMatchRules['data-source-method-select'] },
|
||
'data-source-field-select': { typeMatch: editorTypeMatchRules['data-source-field-select'] },
|
||
'code-select': {
|
||
effect: codeSelectEffect,
|
||
innerConfig: codeSelectInnerConfig,
|
||
typeMatch: editorTypeMatchRules['code-select'],
|
||
},
|
||
'display-conds': { innerConfig: displayCondsInnerConfig, typeMatch: editorTypeMatchRules['display-conds'] },
|
||
'event-select': {
|
||
effect: eventSelectEffect,
|
||
innerConfig: eventSelectInnerConfig,
|
||
typeMatch: editorTypeMatchRules['event-select'],
|
||
},
|
||
'style-setter': { innerConfig: styleSetterInnerConfig, typeMatch: editorTypeMatchRules['style-setter'] },
|
||
};
|