From 88c04c6dac6ada5bacf30b33b6ed0a7649020fa2 Mon Sep 17 00:00:00 2001 From: qwertyyb Date: Mon, 13 May 2024 17:23:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=BA=90=E4=BA=8B=E4=BB=B6=20(#605)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 添加observedData * feat: 修改错误 * fix: 修复单测报错问题 * feat: 完善数据源事件 * fix: 修复数据源事件调用组件方法时报错的异常 * fix: 修复多个相同类型的数据源数据变化的事件混淆的问题 * chore: 删除无用代码 * feat: 默认使用SimpleObservedData * feat: 删除无用代码 --------- Co-authored-by: marchyang --- packages/core/src/App.ts | 43 +++++++++++++++- packages/data-source/package.json | 3 +- packages/data-source/src/DataSourceManager.ts | 18 ++++++- packages/data-source/src/data-sources/Base.ts | 41 ++++++++++------ packages/data-source/src/index.ts | 1 + .../src/observed-data/DeepObservedData.ts | 49 +++++++++++++++++++ .../src/observed-data/ObservedData.ts | 11 +++++ .../src/observed-data/SimpleObservedData.ts | 38 ++++++++++++++ .../data-source/src/observed-data/index.ts | 3 ++ packages/data-source/src/types.ts | 4 ++ .../src/fields/DataSourceFieldSelect.vue | 43 ++-------------- packages/editor/src/fields/EventSelect.vue | 39 +++++++++++---- .../editor/src/utils/data-source/index.ts | 42 ++++++++++++++-- packages/form/src/fields/Cascader.vue | 39 ++++++++++++--- packages/form/src/schema.ts | 5 +- packages/schema/src/index.ts | 2 + packages/utils/src/index.ts | 2 + pnpm-lock.yaml | 8 +++ 18 files changed, 314 insertions(+), 77 deletions(-) create mode 100644 packages/data-source/src/observed-data/DeepObservedData.ts create mode 100644 packages/data-source/src/observed-data/ObservedData.ts create mode 100644 packages/data-source/src/observed-data/SimpleObservedData.ts create mode 100644 packages/data-source/src/observed-data/index.ts diff --git a/packages/core/src/App.ts b/packages/core/src/App.ts index 661729442..36dd0c9cb 100644 --- a/packages/core/src/App.ts +++ b/packages/core/src/App.ts @@ -20,7 +20,7 @@ import { EventEmitter } from 'events'; import { has, isEmpty } from 'lodash-es'; -import { createDataSourceManager, DataSourceManager } from '@tmagic/data-source'; +import { createDataSourceManager, DataSourceManager, ObservedDataClass } from '@tmagic/data-source'; import { ActionType, type AppCore, @@ -35,6 +35,7 @@ import { type MApp, type RequestFunction, } from '@tmagic/schema'; +import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX } from '@tmagic/utils'; import Env from './Env'; import { bindCommonEventListener, isCommonMethod, triggerCommonMethod } from './events'; @@ -52,6 +53,7 @@ interface AppOptionsConfig { useMock?: boolean; transformStyle?: (style: Record) => Record; request?: RequestFunction; + DataSourceObservedData?: ObservedDataClass; } interface EventCache { @@ -79,6 +81,7 @@ class App extends EventEmitter implements AppCore { public eventQueueMap: Record = {}; private eventList = new Map<(fromCpt: Node, ...args: any[]) => void, string>(); + private dataSourceEventList = new Map void>>(); constructor(options: AppOptionsConfig) { super(); @@ -272,6 +275,7 @@ class App extends EventEmitter implements AppCore { this.on(eventName, eventHandler); }); } + this.bindDataSourceEvents(); } public emit(name: string | symbol, ...args: any[]): boolean { @@ -356,6 +360,43 @@ class App extends EventEmitter implements AppCore { } } + private bindDataSourceEvents() { + if (this.platform === 'editor') return; + + // 先清掉之前注册的事件,重新注册 + Array.from(this.dataSourceEventList.keys()).forEach((dataSourceId) => { + const dataSourceEvent = this.dataSourceEventList.get(dataSourceId)!; + Array.from(dataSourceEvent.keys()).forEach((eventName) => { + const [prefix, ...path] = eventName.split('.'); + if (prefix === DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX) { + this.dataSourceManager?.offDataChange(dataSourceId, path.join('.'), dataSourceEvent.get(eventName)!); + } else { + this.dataSourceManager?.get(dataSourceId)?.off(prefix, dataSourceEvent.get(eventName)!); + } + }); + }); + + (this.dsl?.dataSources || []).forEach((dataSource) => { + const dataSourceEvent = this.dataSourceEventList.get(dataSource.id) ?? new Map void>(); + (dataSource.events || []).forEach((event) => { + const [prefix, ...path] = event.name?.split('.') || []; + if (!prefix) return; + const handler = (...args: any[]) => { + this.eventHandler(event, this.dataSourceManager?.get(dataSource.id), args); + }; + dataSourceEvent.set(event.name, handler); + if (prefix === DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX) { + // 数据源数据变化 + this.dataSourceManager?.onDataChange(dataSource.id, path.join('.'), handler); + } else { + // 数据源自定义事件 + this.dataSourceManager?.get(dataSource.id)?.on(prefix, handler); + } + }); + this.dataSourceEventList.set(dataSource.id, dataSourceEvent); + }); + } + /** * 事件联动处理函数 * @param eventsConfigIndex 事件配置索引,可以通过此索引从node.event中获取最新事件配置 diff --git a/packages/data-source/package.json b/packages/data-source/package.json index 23feb360d..8536c086f 100644 --- a/packages/data-source/package.json +++ b/packages/data-source/package.json @@ -35,8 +35,9 @@ ], "dependencies": { "@tmagic/dep": "workspace:*", - "@tmagic/utils": "workspace:*", "@tmagic/schema": "workspace:*", + "@tmagic/utils": "workspace:*", + "deep-state-observer": "^5.5.13", "events": "^3.3.0", "lodash-es": "^4.17.21" }, diff --git a/packages/data-source/src/DataSourceManager.ts b/packages/data-source/src/DataSourceManager.ts index c1bee2de1..0125d82dc 100644 --- a/packages/data-source/src/DataSourceManager.ts +++ b/packages/data-source/src/DataSourceManager.ts @@ -23,12 +23,15 @@ import { cloneDeep } from 'lodash-es'; import type { AppCore, DataSourceSchema, Id, MNode } from '@tmagic/schema'; import { compiledNode } from '@tmagic/utils'; +import { SimpleObservedData } from './observed-data/SimpleObservedData'; import { DataSource, HttpDataSource } from './data-sources'; -import type { ChangeEvent, DataSourceManagerData, DataSourceManagerOptions } from './types'; +import type { ChangeEvent, DataSourceManagerData, DataSourceManagerOptions, ObservedDataClass } from './types'; import { compiledNodeField, compliedConditions, compliedIteratorItems } from './utils'; class DataSourceManager extends EventEmitter { private static dataSourceClassMap = new Map(); + // eslint-disable-next-line @typescript-eslint/naming-convention + private static ObservedDataClass: ObservedDataClass = SimpleObservedData; public static register(type: string, dataSource: T) { DataSourceManager.dataSourceClassMap.set(type, dataSource); @@ -45,6 +48,10 @@ class DataSourceManager extends EventEmitter { return DataSourceManager.dataSourceClassMap.get(type); } + public static registerObservedData(ObservedDataClass: ObservedDataClass) { + DataSourceManager.ObservedDataClass = ObservedDataClass; + } + public app: AppCore; public dataSourceMap = new Map(); @@ -133,6 +140,7 @@ class DataSourceManager extends EventEmitter { request: this.app.request, useMock: this.useMock, initialData: this.data[config.id], + ObservedDataClass: DataSourceManager.ObservedDataClass, }); this.dataSourceMap.set(config.id, ds); @@ -210,6 +218,14 @@ class DataSourceManager extends EventEmitter { }); this.dataSourceMap.clear(); } + + public onDataChange(id: string, path: string, callback: (newVal: any) => void) { + return this.get(id)?.onDataChange(path, callback); + } + + public offDataChange(id: string, path: string, callback: (newVal: any) => void) { + return this.get(id)?.offDataChange(path, callback); + } } DataSourceManager.register('http', HttpDataSource as any); diff --git a/packages/data-source/src/data-sources/Base.ts b/packages/data-source/src/data-sources/Base.ts index 3b2e81ae3..892ee6120 100644 --- a/packages/data-source/src/data-sources/Base.ts +++ b/packages/data-source/src/data-sources/Base.ts @@ -18,8 +18,10 @@ import EventEmitter from 'events'; import type { AppCore, CodeBlockContent, DataSchema, DataSourceSchema } from '@tmagic/schema'; -import { getDefaultValueFromFields, setValueByKeyPath } from '@tmagic/utils'; +import { getDefaultValueFromFields } from '@tmagic/utils'; +import { ObservedData } from '@data-source/observed-data/ObservedData'; +import { SimpleObservedData } from '@data-source/observed-data/SimpleObservedData'; import type { ChangeEvent, DataSourceOptions } from '@data-source/types'; /** @@ -28,8 +30,6 @@ import type { ChangeEvent, DataSourceOptions } from '@data-source/types'; export default class DataSource extends EventEmitter { public isInit = false; - public data: Record = {}; - /** @tmagic/core 实例 */ public app: AppCore; @@ -38,6 +38,7 @@ export default class DataSource e #type = 'base'; #id: string; #schema: T; + #observedData: ObservedData; /** 数据源自定义字段配置 */ #fields: DataSchema[] = []; @@ -55,22 +56,27 @@ export default class DataSource e this.setFields(options.schema.fields); this.setMethods(options.schema.methods || []); + let data = options.initialData; + // eslint-disable-next-line @typescript-eslint/naming-convention + const ObservedDataClass = options.ObservedDataClass || SimpleObservedData; if (this.app.platform === 'editor') { // 编辑器中有mock使用mock,没有使用默认值 this.mockData = options.schema.mocks?.find((mock) => mock.useInEditor)?.data || this.getDefaultData(); - this.setData(this.mockData); + data = this.mockData; } else if (typeof options.useMock === 'boolean' && options.useMock) { // 设置了使用mock就使用mock数据 this.mockData = options.schema.mocks?.find((mock) => mock.enable)?.data || this.getDefaultData(); - this.setData(this.mockData); + data = this.mockData; } else if (!options.initialData) { - this.setData(this.getDefaultData()); + data = this.getDefaultData(); } else { // 在ssr模式下,会将server端获取的数据设置到initialData - this.setData(options.initialData); + this.#observedData = new ObservedDataClass(options.initialData ?? {}); // 设置isInit,防止manager中执行init方法 this.isInit = true; + return; } + this.#observedData = new ObservedDataClass(data ?? {}); } public get id() { @@ -101,13 +107,12 @@ export default class DataSource e this.#methods = methods; } + public get data() { + return this.#observedData.getData(''); + } + public setData(data: any, path?: string) { - if (path) { - setValueByKeyPath(path, data, this.data); - } else { - // todo: 校验数据,看是否符合 schema - this.data = data; - } + this.#observedData.update(data, path); const changeEvent: ChangeEvent = { updateData: data, @@ -117,6 +122,14 @@ export default class DataSource e this.emit('change', changeEvent); } + public onDataChange(path: string, callback: (newVal: any) => void) { + this.#observedData.on(path, callback); + } + + public offDataChange(path: string, callback: (newVal: any) => void) { + this.#observedData.off(path, callback); + } + public getDefaultData() { return getDefaultValueFromFields(this.#fields); } @@ -126,8 +139,8 @@ export default class DataSource e } public destroy() { - this.data = {}; this.#fields = []; this.removeAllListeners(); + this.#observedData.destroy(); } } diff --git a/packages/data-source/src/index.ts b/packages/data-source/src/index.ts index 648523015..84fd066ee 100644 --- a/packages/data-source/src/index.ts +++ b/packages/data-source/src/index.ts @@ -19,5 +19,6 @@ export { default as DataSourceManager } from './DataSourceManager'; export * from './data-sources'; export * from './createDataSourceManager'; +export * from './observed-data'; export * from './utils'; export * from './types'; diff --git a/packages/data-source/src/observed-data/DeepObservedData.ts b/packages/data-source/src/observed-data/DeepObservedData.ts new file mode 100644 index 000000000..476c854cf --- /dev/null +++ b/packages/data-source/src/observed-data/DeepObservedData.ts @@ -0,0 +1,49 @@ +import State from 'deep-state-observer'; + +import { ObservedData } from './ObservedData'; + +const ignoreFirstCall = any>(fn: F) => { + let calledTimes = 0; + return (...args: Parameters) => { + if (calledTimes === 0) { + calledTimes += 1; + return; + } + return fn(...args); + }; +}; + +export class DeepObservedData extends ObservedData { + state?: State; + subscribers = new Map void>>(); + constructor(initialData: Record) { + super(); + this.state = new State(initialData); + } + update = (data: any, path?: string) => { + this.state?.update(path ?? '', data); + }; + on = (path: string, callback: (newVal: any) => void) => { + // subscribe 会立即执行一次,ignoreFirstCall 会忽略第一次执行 + const unsubscribe = this.state!.subscribe(path, ignoreFirstCall(callback)); + + // 把取消监听的函数保存下来,供 off 时调用 + const pathSubscribers = this.subscribers.get(path) ?? new Map void>(); + pathSubscribers.set(callback, unsubscribe); + this.subscribers.set(path, pathSubscribers); + }; + off = (path: string, callback: (newVal: any) => void) => { + const pathSubscribers = this.subscribers.get(path); + if (!pathSubscribers) return; + + pathSubscribers.get(callback)?.(); + pathSubscribers.delete(callback); + }; + getData = (path: string) => (!this.state ? {} : this.state?.get(path)); + destroy = () => { + // 销毁所有未被取消的监听 + this.subscribers.forEach((pathSubscribers) => { + pathSubscribers.forEach((unsubscribe) => unsubscribe()); + }); + }; +} diff --git a/packages/data-source/src/observed-data/ObservedData.ts b/packages/data-source/src/observed-data/ObservedData.ts new file mode 100644 index 000000000..ec846285c --- /dev/null +++ b/packages/data-source/src/observed-data/ObservedData.ts @@ -0,0 +1,11 @@ +export abstract class ObservedData { + abstract update(data: any, path?: string): void; + + abstract on(path: string, callback: (newVal: any) => void): void; + + abstract off(path: string, callback: (newVal: any) => void): void; + + abstract getData(path: string): any; + + abstract destroy(): void; +} diff --git a/packages/data-source/src/observed-data/SimpleObservedData.ts b/packages/data-source/src/observed-data/SimpleObservedData.ts new file mode 100644 index 000000000..77db4f056 --- /dev/null +++ b/packages/data-source/src/observed-data/SimpleObservedData.ts @@ -0,0 +1,38 @@ +import { EventEmitter } from 'events'; + +import { getValueByKeyPath, setValueByKeyPath } from '@tmagic/utils'; + +import { ObservedData } from './ObservedData'; + +export class SimpleObservedData extends ObservedData { + data: Record = {}; + private event = new EventEmitter(); + + constructor(initialData: Record) { + super(); + this.data = initialData; + } + update(data: any, path?: string): void { + if (path) { + setValueByKeyPath(path, data, this.data); + } else { + this.data = data; + } + + const changeEvent = { + updateData: data, + path: path ?? '', + }; + this.event.emit(path ?? '', changeEvent); + } + on(path: string, callback: (newVal: any) => void): void { + this.event.on(path, callback); + } + off(path: string, callback: (newVal: any) => void): void { + this.event.off(path, callback); + } + getData(path: string) { + return path ? getValueByKeyPath(path, this.data) : this.data; + } + destroy(): void {} +} diff --git a/packages/data-source/src/observed-data/index.ts b/packages/data-source/src/observed-data/index.ts new file mode 100644 index 000000000..9acb9d701 --- /dev/null +++ b/packages/data-source/src/observed-data/index.ts @@ -0,0 +1,3 @@ +export { ObservedData } from './ObservedData'; +export { DeepObservedData } from './DeepObservedData'; +export { SimpleObservedData } from './SimpleObservedData'; diff --git a/packages/data-source/src/types.ts b/packages/data-source/src/types.ts index d09229b7c..be4c7486a 100644 --- a/packages/data-source/src/types.ts +++ b/packages/data-source/src/types.ts @@ -2,6 +2,9 @@ import type { AppCore, DataSourceSchema, HttpOptions, RequestFunction } from '@t import type DataSource from './data-sources/Base'; import type HttpDataSource from './data-sources/Http'; +import { ObservedData } from './observed-data/ObservedData'; + +export type ObservedDataClass = new (...args: any[]) => ObservedData; export interface DataSourceOptions { schema: T; @@ -9,6 +12,7 @@ export interface DataSourceOptions; useMock?: boolean; request?: RequestFunction; + ObservedDataClass?: ObservedDataClass; [key: string]: any; } diff --git a/packages/editor/src/fields/DataSourceFieldSelect.vue b/packages/editor/src/fields/DataSourceFieldSelect.vue index 40fab7abe..ac8236b83 100644 --- a/packages/editor/src/fields/DataSourceFieldSelect.vue +++ b/packages/editor/src/fields/DataSourceFieldSelect.vue @@ -38,14 +38,14 @@ import { computed, inject, ref, resolveComponent, watch } from 'vue'; import { Coin, Edit, View } from '@element-plus/icons-vue'; import { TMagicButton } from '@tmagic/design'; -import type { CascaderConfig, CascaderOption, FieldProps, FormState } from '@tmagic/form'; +import type { CascaderConfig, FieldProps, FormState } from '@tmagic/form'; import { filterFunction, MCascader } from '@tmagic/form'; -import type { DataSchema, DataSourceFieldType } from '@tmagic/schema'; import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils'; import MIcon from '@editor/components/Icon.vue'; import type { DataSourceFieldSelectConfig, EventBus, Services } from '@editor/type'; import { SideItemKey } from '@editor/type'; +import { getCascaderOptionsFromFields } from '@editor/utils'; defineOptions({ name: 'MFieldsDataSourceFieldSelect', @@ -76,43 +76,6 @@ const selectedDataSourceId = computed(() => { const dataSources = computed(() => services?.dataSourceService.get('dataSources')); -const getOptionChildren = ( - fields: DataSchema[] = [], - dataSourceFieldType: DataSourceFieldType[] = ['any'], -): CascaderOption[] => { - const child: CascaderOption[] = []; - fields.forEach((field) => { - if (!dataSourceFieldType.length) { - dataSourceFieldType.push('any'); - } - - const children = getOptionChildren(field.fields, dataSourceFieldType); - - const item = { - label: field.title || field.name, - value: field.name, - children, - }; - - const fieldType = field.type || 'any'; - if (dataSourceFieldType.includes('any') || dataSourceFieldType.includes(fieldType)) { - child.push(item); - return; - } - - if (!dataSourceFieldType.includes(fieldType) && !['array', 'object', 'any'].includes(fieldType)) { - return; - } - - if (!children.length && ['object', 'array', 'any'].includes(field.type || '')) { - return; - } - - child.push(item); - }); - return child; -}; - const cascaderConfig = computed(() => { const valueIsKey = props.config.value === 'key'; @@ -125,7 +88,7 @@ const cascaderConfig = computed(() => { dataSources.value?.map((ds) => ({ label: ds.title || ds.id, value: valueIsKey ? ds.id : `${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${ds.id}`, - children: getOptionChildren(ds.fields, props.config.dataSourceFieldType), + children: getCascaderOptionsFromFields(ds.fields, props.config.dataSourceFieldType), })) || []; return options.filter((option) => option.children.length); }, diff --git a/packages/editor/src/fields/EventSelect.vue b/packages/editor/src/fields/EventSelect.vue index c61f2129c..891dea558 100644 --- a/packages/editor/src/fields/EventSelect.vue +++ b/packages/editor/src/fields/EventSelect.vue @@ -55,11 +55,13 @@ import { has } from 'lodash-es'; import type { EventOption } from '@tmagic/core'; import { TMagicButton } from '@tmagic/design'; -import type { FieldProps, FormState, PanelConfig } from '@tmagic/form'; +import type { CascaderOption, FieldProps, FormState, PanelConfig } from '@tmagic/form'; import { MContainer, MPanel } from '@tmagic/form'; import { ActionType } from '@tmagic/schema'; +import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX } from '@tmagic/utils'; import type { CodeSelectColConfig, DataSourceMethodSelectConfig, EventSelectConfig, Services } from '@editor/type'; +import { getCascaderOptionsFromFields } from '@editor/utils'; defineOptions({ name: 'MFieldsEventSelect', @@ -78,26 +80,45 @@ const codeBlockService = services?.codeBlockService; // 事件名称下拉框表单配置 const eventNameConfig = computed(() => { + const fieldType = props.config.src === 'component' ? 'select' : 'cascader'; const defaultEventNameConfig = { name: 'name', text: '事件', - type: 'select', + type: fieldType, labelWidth: '40px', + checkStrictly: true, + valueSeparator: '.', options: (mForm: FormState, { formValue }: any) => { - let events: EventOption[] = []; + let events: EventOption[] | CascaderOption[] = []; if (!eventsService || !dataSourceService) return events; if (props.config.src === 'component') { events = eventsService.getEvent(formValue.type); - } else if (props.config.src === 'datasource') { - events = dataSourceService.getFormEvent(formValue.type); + return events.map((option) => ({ + text: option.label, + value: option.value, + })); } + if (props.config.src === 'datasource') { + // 从数据源类型中获取到相关事件 + events = dataSourceService.getFormEvent(formValue.type); + // 从数据源类型和实例中分别获取数据以追加数据变化的事件 + const dataSource = dataSourceService.getDataSourceById(formValue.id); + const fields = dataSource?.fields || []; + if (fields.length > 0) { + return [ + ...events, + { + label: '数据变化', + value: DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, + children: getCascaderOptionsFromFields(fields), + }, + ]; + } - return events.map((option) => ({ - text: option.label, - value: option.value, - })); + return events; + } }, }; return { ...defaultEventNameConfig, ...props.config.eventNameConfig }; diff --git a/packages/editor/src/utils/data-source/index.ts b/packages/editor/src/utils/data-source/index.ts index ac1c76ac0..a39eb1832 100644 --- a/packages/editor/src/utils/data-source/index.ts +++ b/packages/editor/src/utils/data-source/index.ts @@ -1,5 +1,5 @@ -import { FormConfig, FormState } from '@tmagic/form'; -import { DataSchema, DataSourceSchema } from '@tmagic/schema'; +import { CascaderOption, FormConfig, FormState } from '@tmagic/form'; +import { DataSchema, DataSourceFieldType, DataSourceSchema } from '@tmagic/schema'; import BaseFormConfig from './formConfigs/base'; import HttpFormConfig from './formConfigs/http'; @@ -32,7 +32,6 @@ const fillConfig = (config: FormConfig): FormConfig => [ }, { title: '事件配置', - display: false, items: [ { name: 'events', @@ -198,3 +197,40 @@ export const getDisplayField = (dataSources: DataSourceSchema[], key: string) => return displayState; }; + +export const getCascaderOptionsFromFields = ( + fields: DataSchema[] = [], + dataSourceFieldType: DataSourceFieldType[] = ['any'], +): CascaderOption[] => { + const child: CascaderOption[] = []; + fields.forEach((field) => { + if (!dataSourceFieldType.length) { + dataSourceFieldType.push('any'); + } + + const children = getCascaderOptionsFromFields(field.fields, dataSourceFieldType); + + const item = { + label: field.title || field.name, + value: field.name, + children, + }; + + const fieldType = field.type || 'any'; + if (dataSourceFieldType.includes('any') || dataSourceFieldType.includes(fieldType)) { + child.push(item); + return; + } + + if (!dataSourceFieldType.includes(fieldType) && !['array', 'object', 'any'].includes(fieldType)) { + return; + } + + if (!children.length && ['object', 'array', 'any'].includes(field.type || '')) { + return; + } + + child.push(item); + }); + return child; +}; diff --git a/packages/form/src/fields/Cascader.vue b/packages/form/src/fields/Cascader.vue index 115b6d230..b9f52a49f 100644 --- a/packages/form/src/fields/Cascader.vue +++ b/packages/form/src/fields/Cascader.vue @@ -1,6 +1,6 @@