/* * 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` 内部 * ``。 * * @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(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, * 无渲染校验仍把事件名当作列表项字段,路径为 `..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; 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 = { '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'] }, };