From c79034befc620a90dc0452e8ae6fb2e9d40b17e5 Mon Sep 17 00:00:00 2001 From: roymondchen Date: Wed, 18 Mar 2026 20:19:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(editor,form):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=8C=89=E9=9C=80=E8=AE=BE=E7=BD=AE=E8=A1=A8=E5=8D=95=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../theme/components/demo-block.vue | 6 ++ packages/editor/src/fields/CodeLink.vue | 4 +- .../fields/DataSourceFieldSelect/Index.vue | 12 ++- packages/editor/src/fields/EventSelect.vue | 9 +- packages/editor/src/index.ts | 74 +------------ packages/editor/src/plugin.ts | 92 ++++++++++++++++ packages/editor/src/type.ts | 2 +- packages/editor/src/utils/index.ts | 1 + packages/form/src/containers/Container.vue | 26 +++-- packages/form/src/fields/Hidden.vue | 8 +- packages/form/src/index.ts | 92 ++-------------- packages/form/src/plugin.ts | 102 ++++++++++++++++++ packages/form/src/table/useSortable.ts | 12 ++- packages/form/src/utils/config.ts | 17 ++- playground/src/pages/Form.vue | 8 +- 15 files changed, 279 insertions(+), 186 deletions(-) create mode 100644 packages/editor/src/plugin.ts create mode 100644 packages/form/src/plugin.ts diff --git a/docs/.vitepress/theme/components/demo-block.vue b/docs/.vitepress/theme/components/demo-block.vue index bfccc7a3..4c251545 100644 --- a/docs/.vitepress/theme/components/demo-block.vue +++ b/docs/.vitepress/theme/components/demo-block.vue @@ -189,6 +189,8 @@ import hljs from 'highlight.js'; import serialize from 'serialize-javascript'; +import { MForm } from '@tmagic/form'; + export function stripScript(content) { const result = content.match(/<(script)>([\s\S]+)<\/\1>/); return result && result[2] ? result[2].trim() : ''; @@ -210,6 +212,10 @@ export function stripTemplate(content) { export default { props: ['type', 'config'], + components: { + MForm, + }, + data() { return { codepen: { diff --git a/packages/editor/src/fields/CodeLink.vue b/packages/editor/src/fields/CodeLink.vue index 89b05b02..58d9a58c 100644 --- a/packages/editor/src/fields/CodeLink.vue +++ b/packages/editor/src/fields/CodeLink.vue @@ -1,12 +1,12 @@ diff --git a/packages/form/src/index.ts b/packages/form/src/index.ts index 4673607e..3079ff16 100644 --- a/packages/form/src/index.ts +++ b/packages/form/src/index.ts @@ -16,44 +16,8 @@ * limitations under the License. */ -import type { App } from 'vue'; - -import Container from './containers/Container.vue'; -import Fieldset from './containers/Fieldset.vue'; -import FlexLayout from './containers/FlexLayout.vue'; -import GroupList from './containers/GroupList.vue'; -import Panel from './containers/Panel.vue'; -import Row from './containers/Row.vue'; -import MStep from './containers/Step.vue'; -import Tabs from './containers/Tabs.vue'; -import Cascader from './fields/Cascader.vue'; -import Checkbox from './fields/Checkbox.vue'; -import CheckboxGroup from './fields/CheckboxGroup.vue'; -import ColorPicker from './fields/ColorPicker.vue'; -import Date from './fields/Date.vue'; -import Daterange from './fields/Daterange.vue'; -import DateTime from './fields/DateTime.vue'; -import Display from './fields/Display.vue'; -import DynamicField from './fields/DynamicField.vue'; -import Hidden from './fields/Hidden.vue'; -import Link from './fields/Link.vue'; -import Number from './fields/Number.vue'; -import NumberRange from './fields/NumberRange.vue'; -import RadioGroup from './fields/RadioGroup.vue'; -import Select from './fields/Select.vue'; -import Switch from './fields/Switch.vue'; -import Text from './fields/Text.vue'; -import Textarea from './fields/Textarea.vue'; -import Time from './fields/Time.vue'; -import Timerange from './fields/Timerange.vue'; -import Table from './table/Table.vue'; -import { setConfig } from './utils/config'; -import Form from './Form.vue'; -import FormDialog from './FormDialog.vue'; import type { FormConfig } from './schema'; -import './theme/index.scss'; - export * from './schema'; export * from './utils/form'; export * from './utils/useAddField'; @@ -91,52 +55,14 @@ export { default as MSelect } from './fields/Select.vue'; export { default as MCascader } from './fields/Cascader.vue'; export { default as MDynamicField } from './fields/DynamicField.vue'; +export { + deleteField as deleteFormField, + getField as getFormField, + registerField as registerFormField, +} from './utils/config'; + +export type { FormInstallOptions } from './plugin'; + export const createForm = (config: FormConfig | T) => config; -export interface FormInstallOptions { - [key: string]: any; -} - -const defaultInstallOpt: FormInstallOptions = {}; - -export default { - install(app: App, opt: FormInstallOptions = {}) { - const option = Object.assign(defaultInstallOpt, opt); - - app.config.globalProperties.$MAGIC_FORM = option; - setConfig(option); - - app.component('m-form', Form); - app.component('m-form-dialog', FormDialog); - app.component('m-form-container', Container); - app.component('m-form-fieldset', Fieldset); - app.component('m-form-group-list', GroupList); - app.component('m-form-panel', Panel); - app.component('m-form-row', Row); - app.component('m-form-step', MStep); - app.component('m-form-table', Table); - app.component('m-form-tab', Tabs); - app.component('m-form-flex-layout', FlexLayout); - app.component('m-fields-text', Text); - app.component('m-fields-img-upload', Text); - app.component('m-fields-number', Number); - app.component('m-fields-number-range', NumberRange); - app.component('m-fields-textarea', Textarea); - app.component('m-fields-hidden', Hidden); - app.component('m-fields-date', Date); - app.component('m-fields-datetime', DateTime); - app.component('m-fields-daterange', Daterange); - app.component('m-fields-timerange', Timerange); - app.component('m-fields-time', Time); - app.component('m-fields-checkbox', Checkbox); - app.component('m-fields-switch', Switch); - app.component('m-fields-color-picker', ColorPicker); - app.component('m-fields-checkbox-group', CheckboxGroup); - app.component('m-fields-radio-group', RadioGroup); - app.component('m-fields-display', Display); - app.component('m-fields-link', Link); - app.component('m-fields-select', Select); - app.component('m-fields-cascader', Cascader); - app.component('m-fields-dynamic-field', DynamicField); - }, -}; +export { default } from './plugin'; diff --git a/packages/form/src/plugin.ts b/packages/form/src/plugin.ts new file mode 100644 index 00000000..8abc6f55 --- /dev/null +++ b/packages/form/src/plugin.ts @@ -0,0 +1,102 @@ +/* + * 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 App } from 'vue'; + +import Container from './containers/Container.vue'; +import Fieldset from './containers/Fieldset.vue'; +import FlexLayout from './containers/FlexLayout.vue'; +import GroupList from './containers/GroupList.vue'; +import Panel from './containers/Panel.vue'; +import Row from './containers/Row.vue'; +import MStep from './containers/Step.vue'; +import Tabs from './containers/Tabs.vue'; +import Cascader from './fields/Cascader.vue'; +import Checkbox from './fields/Checkbox.vue'; +import CheckboxGroup from './fields/CheckboxGroup.vue'; +import ColorPicker from './fields/ColorPicker.vue'; +import Date from './fields/Date.vue'; +import Daterange from './fields/Daterange.vue'; +import DateTime from './fields/DateTime.vue'; +import Display from './fields/Display.vue'; +import DynamicField from './fields/DynamicField.vue'; +import Hidden from './fields/Hidden.vue'; +import Link from './fields/Link.vue'; +import Number from './fields/Number.vue'; +import NumberRange from './fields/NumberRange.vue'; +import RadioGroup from './fields/RadioGroup.vue'; +import Select from './fields/Select.vue'; +import Switch from './fields/Switch.vue'; +import Text from './fields/Text.vue'; +import Textarea from './fields/Textarea.vue'; +import Time from './fields/Time.vue'; +import Timerange from './fields/Timerange.vue'; +import Table from './table/Table.vue'; +import { setConfig } from './utils/config'; +import Form from './Form.vue'; +import FormDialog from './FormDialog.vue'; + +import './theme/index.scss'; + +export interface FormInstallOptions { + [key: string]: any; +} + +const defaultInstallOpt: FormInstallOptions = {}; + +export default { + install(app: App, opt: FormInstallOptions = {}) { + const option = Object.assign(defaultInstallOpt, opt); + + app.config.globalProperties.$MAGIC_FORM = option; + setConfig(option); + + app.component('m-form', Form); + app.component('m-form-dialog', FormDialog); + app.component('m-form-container', Container); + app.component('m-form-fieldset', Fieldset); + app.component('m-form-group-list', GroupList); + app.component('m-form-panel', Panel); + app.component('m-form-row', Row); + app.component('m-form-step', MStep); + app.component('m-form-table', Table); + app.component('m-form-tab', Tabs); + app.component('m-form-flex-layout', FlexLayout); + app.component('m-fields-text', Text); + app.component('m-fields-img-upload', Text); + app.component('m-fields-number', Number); + app.component('m-fields-number-range', NumberRange); + app.component('m-fields-textarea', Textarea); + app.component('m-fields-hidden', Hidden); + app.component('m-fields-date', Date); + app.component('m-fields-datetime', DateTime); + app.component('m-fields-daterange', Daterange); + app.component('m-fields-timerange', Timerange); + app.component('m-fields-time', Time); + app.component('m-fields-checkbox', Checkbox); + app.component('m-fields-switch', Switch); + app.component('m-fields-color-picker', ColorPicker); + app.component('m-fields-checkbox-group', CheckboxGroup); + app.component('m-fields-radio-group', RadioGroup); + app.component('m-fields-display', Display); + app.component('m-fields-link', Link); + app.component('m-fields-select', Select); + app.component('m-fields-cascader', Cascader); + app.component('m-fields-dynamic-field', DynamicField); + }, +}; diff --git a/packages/form/src/table/useSortable.ts b/packages/form/src/table/useSortable.ts index ed5449e3..47483b5e 100644 --- a/packages/form/src/table/useSortable.ts +++ b/packages/form/src/table/useSortable.ts @@ -1,5 +1,5 @@ import { inject, nextTick, type Ref, type ShallowRef, watchEffect } from 'vue'; -import Sortable, { type SortableEvent } from 'sortablejs'; +import type { default as SortableType, SortableEvent } from 'sortablejs'; import { type TMagicTable } from '@tmagic/design'; import type { FormState } from '@tmagic/form-schema'; @@ -8,6 +8,9 @@ import { sortArray } from '../utils/form'; import type { TableProps } from './type'; +let SortablePromise: Promise | undefined; +const loadSortable = () => (SortablePromise ??= import('sortablejs').then((m) => m.default)); + export const useSortable = ( props: TableProps, emit: (event: 'select' | 'change' | 'addDiffCount', ...args: any[]) => void, @@ -17,15 +20,16 @@ export const useSortable = ( ) => { const mForm = inject('mForm'); - let sortable: Sortable | undefined; - const rowDrop = () => { + let sortable: SortableType | undefined; + const rowDrop = async () => { sortable?.destroy(); const tableEl = tMagicTableRef.value?.getEl(); const tBodyEl = tableEl?.querySelector('.el-table__body > tbody') || tableEl?.querySelector('.t-table__body'); if (!tBodyEl) { return; } - sortable = Sortable.create(tBodyEl, { + + sortable = (await loadSortable()).create(tBodyEl, { draggable: '.tmagic-design-table-row', filter: 'input', // 表单组件选字操作和触发拖拽会冲突,优先保证选字操作 preventOnFilter: false, // 允许选字 diff --git a/packages/form/src/utils/config.ts b/packages/form/src/utils/config.ts index 916bf91d..56cf552e 100644 --- a/packages/form/src/utils/config.ts +++ b/packages/form/src/utils/config.ts @@ -16,6 +16,8 @@ * limitations under the License. */ +import type { Component } from 'vue'; + let $MAGIC_FORM = {} as any; const setConfig = (option: any): void => { @@ -24,4 +26,17 @@ const setConfig = (option: any): void => { const getConfig = (key: string): T => $MAGIC_FORM[key]; -export { getConfig, setConfig }; +const fieldRegistry = new Map(); + +const registerField = (tagName: string, component: Component): void => { + if (fieldRegistry.has(tagName)) { + return; + } + fieldRegistry.set(tagName, component); +}; + +const getField = (tagName: string): Component | undefined => fieldRegistry.get(tagName); + +const deleteField = (tagName: string): boolean => fieldRegistry.delete(tagName); + +export { deleteField, getConfig, getField, registerField, setConfig }; diff --git a/playground/src/pages/Form.vue b/playground/src/pages/Form.vue index 254a9adb..5a08a8c3 100644 --- a/playground/src/pages/Form.vue +++ b/playground/src/pages/Form.vue @@ -3,7 +3,7 @@
开启表单对比功能
- + >
表单字段展示
- - + +