diff --git a/CHANGELOG.md b/CHANGELOG.md index 838bad79..cceb7e36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,84 +1,8 @@ ## [Unreleased] + - ⚠️ [@mixte/snippets] 删除 `AcroDynamicForm` 组件 -## [v2.5.0], [v2.5.0-beta.2], [v2.5.0-beta.1] - - 📅 2024-11-05 - - 🌟 [@mixte/components] 组件 `AutoGrid` 新增 `fluid` 参数 - - 🌟 [@mixte/components] 组件 `AutoGrid` 新增 `columnCount` 变量导出, 标识每行可以渲染的子元素数量 - - 🌟 [@mixte/components] 组件 `AutoGrid` 新增 `isCollapsed` 变量导出, 标识子元素是否折叠 - - 🐞 [@mixte/use] 修复 `useRequest` 使用了 `resetOnExecute: false` 并请求失败后, `response` 和 `data` 未重置的问题 - - 🐞 [@mixte/use] 修复 `useRequest` 使用了 `resetOnExecute: false` 并请求成功后, `error` 未重置的问题 - -## [v2.4.0], [v2.4.0-beta.3], [v2.4.0-beta.2], [v2.4.0-beta.1] - - 📅 2024-08-27 - - 🌟 [@mixte/components] 新增 `ListAutoGrid` 组件, 支持 `Vue3`、`Vue2.7` - - 💄 [@mixte/components] 组件 `AutoGrid` 根节点添加 `mixte-auto-grid` 样式类 - -## [v2.3.0] - - 📅 2024-08-20 - - 🌟 [@mixte/use] 方法 `useRequest` 新增 `reset` 方法, 用于重置请求到初始状态 - -## [v2.2.0] - - 📅 2024-08-19 - - 🌟 [@mixte/snippets] 方法 `toggleThemeViewTransition` 新增 `reverseSelector` 选项, 用于自定义选择器, 默认 `.dark` - - 🌟 [@mixte/snippets] 方法 `toggleThemeViewTransition` 新增 `prefersReducedMotion` 选项, 用于自定义是否检测用户偏好是否是减少动画, 默认检测 - -## [v2.1.0] - - 📅 2024-08-05 - - 🌟 [@mixte/snippets] 新增 `toggleThemeViewTransition` 方法, 提供切换主题时的视图过渡动画 - -## [v2.0.3] - - 📅 2024-08-03 - - 💄 [@mixte/validator] 优化 `citizenID` 和 `email` 的正则 - - 🐞 [@mixte/use] 修复 `@mixte/use/nuxt` 仅导入了类型的问题 - -## [v2.0.2] - - 📅 2024-07-30 - - 🌟 [@mixte/use] 新增供 Nuxt 使用的模块 `@mixte/use/nuxt`, 提供自动导入功能 - -## [v2.0.1] - - 📅 2024-07-29 - - 💄 [@mixte/use] 内置 `whenever` 方法, 防止项目中 `@vueuse/core` 版本过低不支持 `once` 选项 - -## [v2.0.0], [v2.0.0-beta.3], [v2.0.0-beta.2], [v2.0.0-beta.1] - - 📅 2024-07-06 - - [@mixte/use] - - `useRequest` 方法 - - 💄 优化 `reactive` 返回值实现 - - ⚠️ 移除 `successCount` 参数及 `clearSuccessCount` 方法 - - - [@mixte/components] - - ⚠️ 移除该组件包的默认导出, 请按需导入 - - `AutoGrid` 组件 - - 💄 组件使用 Vue SFC 重构, 提供更好的类型提示 - - 💄 新增 `MixteAutoGridInstance` 导出项 - - ⚠️ 移除 `mixteAutoGridProps` 导出项, 保留 `MixteAutoGridProps` 导出项 - - - [@mixte/snippets] - - `AcroDynamicForm` 组件 - - 🌟 字段配置新增 `render` 选项 - - 可传入渲染函数, 渲染自定义组件 - - 可传入插槽名称, 将使用指定名称的插槽来渲染自定义组件 - - 🌟 字段配置新增 `preset` 选项 - - 用于定义需要继承的已配置的预设, 支持继承多个预设 - - 🌟 新增 `actionButtonArea`、`actionButtonPrepend`、`actionButtonAppend` 插槽 - - `actionButtonArea`: 可使用该插槽代替操作按钮区域的渲染 - - `actionButtonPrepend`: 可插入内容到提交按钮前面 - - `actionButtonAppend`: 可插入内容到重置按钮后面 - - 🌟 新增 `actionButtonArea.props`、`submitButton.props`、`resetButton.props` - - `actionButtonArea.props`: 传递给操作按钮区域 FormItem 组件的参数 - - `submitButton.props`: 传递给提交按钮组件的参数 - - `resetButton.props`: 传递给重置按钮组件的参数 - - 🌟 新增 `actionButtonArea.spaceProps` - - 传递给操作按钮区域 Space 组件的参数 - - 🌟 新增 `init` 导出方法 - - 🌟 新增对外导出方法 `defineAcroDynamicFormPreset`, 用于定义预设选项 - - 💄 优化传参类型, 整合 Acro 的 Form 表单组件传参 - - ⚠️ 修改 `AcroDynamicForm` 组件参数 - - 由 `showActionButtonArea` 改为 `actionButtonArea` 或 `actionButtonArea.show` - - 由 `showSubmitButton` 改为 `submitButton` 或 `submitButton.show` - - 由 `submitButtonText` 改为 `submitButton.text` - - 由 `showResetButton` 改为 `resetButton` 或 `resetButton.show` - - 由 `resetButtonText` 改为 `resetButton.text` +## [../v2](https://mixte-v2.moomfe.com) + - [更新日志](https://mixte-v2.moomfe.com/changelog) ## [../v1](https://mixte-v1.moomfe.com) - [更新日志](https://mixte-v1.moomfe.com/changelog) diff --git a/meta/docs.json b/meta/docs.json index 6749ebea..7309a29b 100644 --- a/meta/docs.json +++ b/meta/docs.json @@ -204,13 +204,6 @@ } ], "snippets": [ - { - "fn": "acro-dynamic-form", - "title": "AcroDynamicForm", - "name": "Acro 动态表单", - "hiddenTitle": false, - "sidebarTitle": "AcroDynamicForm" - }, { "fn": "getFastestCDN", "title": "", diff --git a/meta/packages.ts b/meta/packages.ts index 8ea368c7..95c144e4 100644 --- a/meta/packages.ts +++ b/meta/packages.ts @@ -63,15 +63,6 @@ export const packages = [ external: ['mixte'], dtsExternal: ['naive-ui'], }, - // @mixte/snippets/acro-dynamic-form - { - input: 'packages/snippets/src/acro-dynamic-form/index.ts', - outputDir: 'packages/snippets/dist', - outputFileName: 'acro-dynamic-form', - external: ['@arco-design/web-vue', 'mixte'], - dtsExternal: ['@arco-design/web-vue'], - vueComponent: true, - }, // @mixte/snippets/toggleThemeViewTransition { input: 'packages/snippets/src/toggleThemeViewTransition/index.ts', diff --git a/packages/.vitepress/config.mts b/packages/.vitepress/config.mts index e916e013..a1ed24b6 100644 --- a/packages/.vitepress/config.mts +++ b/packages/.vitepress/config.mts @@ -122,7 +122,6 @@ export default defineConfig({ ...alias, { find: '@', replacement: resolve(__dirname, '../') }, { find: '@@', replacement: resolve(__dirname, '../../') }, - { find: '@mixte/components/acro-dynamic-form', replacement: resolve(__dirname, '../snippets/dist/acro-dynamic-form') }, ], }, plugins: [ diff --git a/packages/.vitepress/theme/index.ts b/packages/.vitepress/theme/index.ts index 984fac20..f2828cb9 100644 --- a/packages/.vitepress/theme/index.ts +++ b/packages/.vitepress/theme/index.ts @@ -11,7 +11,6 @@ import './style.css'; import 'uno.css'; import 'element-plus/theme-chalk/dark/css-vars.css'; import 'element-plus/theme-chalk/el-message.css'; -import '@arco-design/web-vue/dist/arco.css'; import '@shikijs/vitepress-twoslash/style.css'; export default { diff --git a/packages/snippets/acro-dynamic-form.d.ts b/packages/snippets/acro-dynamic-form.d.ts deleted file mode 100644 index 6a1046b2..00000000 --- a/packages/snippets/acro-dynamic-form.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/acro-dynamic-form'; diff --git a/packages/snippets/package.json b/packages/snippets/package.json index 9a4cbc6b..83d01e06 100644 --- a/packages/snippets/package.json +++ b/packages/snippets/package.json @@ -23,11 +23,6 @@ "import": "./dist/useNaiveForm.mjs", "require": "./dist/useNaiveForm.cjs" }, - "./acro-dynamic-form": { - "types": "./dist/acro-dynamic-form.d.ts", - "import": "./dist/acro-dynamic-form.mjs", - "require": "./dist/acro-dynamic-form.cjs" - }, "./toggleThemeViewTransition": { "types": "./dist/toggleThemeViewTransition.d.ts", "import": "./dist/toggleThemeViewTransition.mjs", diff --git "a/packages/snippets/src/acro-dynamic-form/demo/\345\237\272\347\241\200\347\224\250\346\263\225.preview.vue" "b/packages/snippets/src/acro-dynamic-form/demo/\345\237\272\347\241\200\347\224\250\346\263\225.preview.vue" deleted file mode 100644 index 35f3233b..00000000 --- "a/packages/snippets/src/acro-dynamic-form/demo/\345\237\272\347\241\200\347\224\250\346\263\225.preview.vue" +++ /dev/null @@ -1,56 +0,0 @@ - - - diff --git "a/packages/snippets/src/acro-dynamic-form/demo/\346\223\215\344\275\234\346\214\211\351\222\256\345\214\272\345\237\237\346\217\222\346\247\275.preview.vue" "b/packages/snippets/src/acro-dynamic-form/demo/\346\223\215\344\275\234\346\214\211\351\222\256\345\214\272\345\237\237\346\217\222\346\247\275.preview.vue" deleted file mode 100644 index a139daf9..00000000 --- "a/packages/snippets/src/acro-dynamic-form/demo/\346\223\215\344\275\234\346\214\211\351\222\256\345\214\272\345\237\237\346\217\222\346\247\275.preview.vue" +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git "a/packages/snippets/src/acro-dynamic-form/demo/\346\270\262\346\237\223\350\207\252\345\256\232\344\271\211\347\273\204\344\273\266.preview.vue" "b/packages/snippets/src/acro-dynamic-form/demo/\346\270\262\346\237\223\350\207\252\345\256\232\344\271\211\347\273\204\344\273\266.preview.vue" deleted file mode 100644 index 73546ae8..00000000 --- "a/packages/snippets/src/acro-dynamic-form/demo/\346\270\262\346\237\223\350\207\252\345\256\232\344\271\211\347\273\204\344\273\266.preview.vue" +++ /dev/null @@ -1,73 +0,0 @@ - - - diff --git "a/packages/snippets/src/acro-dynamic-form/demo/\347\273\204\344\273\266\346\217\222\346\247\275&\350\241\250\345\215\225\351\241\271\346\217\222\346\247\275.preview.vue" "b/packages/snippets/src/acro-dynamic-form/demo/\347\273\204\344\273\266\346\217\222\346\247\275&\350\241\250\345\215\225\351\241\271\346\217\222\346\247\275.preview.vue" deleted file mode 100644 index 6732a16e..00000000 --- "a/packages/snippets/src/acro-dynamic-form/demo/\347\273\204\344\273\266\346\217\222\346\247\275&\350\241\250\345\215\225\351\241\271\346\217\222\346\247\275.preview.vue" +++ /dev/null @@ -1,61 +0,0 @@ - - - diff --git "a/packages/snippets/src/acro-dynamic-form/demo/\350\264\246\345\217\267\347\231\273\345\275\225.preview.vue" "b/packages/snippets/src/acro-dynamic-form/demo/\350\264\246\345\217\267\347\231\273\345\275\225.preview.vue" deleted file mode 100644 index ed1bfc59..00000000 --- "a/packages/snippets/src/acro-dynamic-form/demo/\350\264\246\345\217\267\347\231\273\345\275\225.preview.vue" +++ /dev/null @@ -1,94 +0,0 @@ - - - diff --git "a/packages/snippets/src/acro-dynamic-form/demo/\351\242\204\350\256\276.preview.vue" "b/packages/snippets/src/acro-dynamic-form/demo/\351\242\204\350\256\276.preview.vue" deleted file mode 100644 index 5883ab1e..00000000 --- "a/packages/snippets/src/acro-dynamic-form/demo/\351\242\204\350\256\276.preview.vue" +++ /dev/null @@ -1,55 +0,0 @@ - - - diff --git "a/packages/snippets/src/acro-dynamic-form/demo/\351\252\214\350\257\201\347\240\201\347\231\273\345\275\225.preview.vue" "b/packages/snippets/src/acro-dynamic-form/demo/\351\252\214\350\257\201\347\240\201\347\231\273\345\275\225.preview.vue" deleted file mode 100644 index fb7b932e..00000000 --- "a/packages/snippets/src/acro-dynamic-form/demo/\351\252\214\350\257\201\347\240\201\347\231\273\345\275\225.preview.vue" +++ /dev/null @@ -1,127 +0,0 @@ - - - diff --git a/packages/snippets/src/acro-dynamic-form/index.md b/packages/snippets/src/acro-dynamic-form/index.md deleted file mode 100644 index 5e446ec2..00000000 --- a/packages/snippets/src/acro-dynamic-form/index.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -outline: [1,3] ---- - -基于 [Arco Design Vue](https://arco.design/vue) 封装的动态表单组件 - -::: warning 注意 - - 依赖于 [@arco-design/web-vue](https://www.npmjs.com/package/@arco-design/web-vue) 组件库 - - 该组件仅支持 `Vue 3` -::: - -## 演示 - -### 基础用法 - -### 组件插槽&表单项插槽 - -### 操作按钮区域插槽 - -### 渲染自定义组件 - -### 预设 - -## 完整示例 - -### 账号登录 - -### 验证码登录 - -## API - -### Props - -| 参数名 | 描述 | 类型 | 默认值 | -| --- | --- | --- | --- | -| fields | 字段配置列表 | [`DynamicFormField[]`](#AcroDynamicFormField) | - | -| model | 表单数据 | `Record` | - | -| actionButtonArea | 操作按钮区域配置 | [`ActionButtonAreaOptions`](#ActionButtonAreaOptions) \| `boolean` | `true` | -| submitButton | 提交按钮配置 | [`SubmitButtonOptions`](#SubmitButtonOptions) \| `boolean` | `true` | -| resetButton | 重置按钮配置 | [`ResetButtonOptions`](#ResetButtonOptions) \| `boolean` | `true` | - -### Methods - -```ts twoslash -// ---cut-start--- -/* eslint-disable */ -import type { AcroDynamicFormInstance } from '@mixte/snippets/acro-dynamic-form'; -const { validate, validateField, resetFields, reset, clearValidate, setFields, scrollToField, init } = {} as AcroDynamicFormInstance; -// ---cut-end--- -validate; // 校验全部表单数据 -validateField; // 校验部分表单数据 -resetFields; // 重置表单数据 -clearValidate; // 清除校验状态 -setFields; // 设置表单项的值和状态 -scrollToField; // 滚动到指定表单项 - -reset; // 重置表单数据, 是 `resetFields` 方法的别名 -init; // 初始化表单数据 -``` - -### Events - -| 事件名 | 描述 | 回调参数 | -| --- | --- | --- | -| submit | 点击了提交按钮的事件 | `model: Record` | -| reset | 点击了重置按钮的事件 | - | - -### Slots - -| 名称 | 描述 | -| --- | --- | -| actionButtonArea | 操作按钮区域插槽, 可使用该插槽代替操作按钮区域的渲染 | -| actionButtonPrepend | 操作按钮前置插槽, 可插入内容到提交按钮前面 | -| actionButtonAppend | 操作按钮后置插槽, 可插入内容到重置按钮后面 | - -### 对外导出工具方法 - -```ts twoslash -// ---cut-start--- -/* eslint-disable */ -// ---cut-end--- -import { defineAcroDynamicFormField, defineAcroDynamicFormFields, defineAcroDynamicFormPreset } from '@mixte/snippets/acro-dynamic-form'; - -defineAcroDynamicFormField; // 定义单个字段配置 -defineAcroDynamicFormFields; // 定义字段配置列表 -defineAcroDynamicFormPreset; // 定义预设 -``` - -## 类型定义 - -### 字段配置 {#AcroDynamicFormField} -<<< ./src/types.ts#AcroDynamicFormField - -### 操作按钮区域配置 {#ActionButtonAreaOptions} -<<< ./src/types.ts#ActionButtonAreaOptions - -### 提交按钮配置 {#SubmitButtonOptions} -<<< ./src/types.ts#SubmitButtonOptions - -### 重置按钮配置 {#ResetButtonOptions} -<<< ./src/types.ts#ResetButtonOptions diff --git a/packages/snippets/src/acro-dynamic-form/index.test.ts b/packages/snippets/src/acro-dynamic-form/index.test.ts deleted file mode 100644 index 6501fd40..00000000 --- a/packages/snippets/src/acro-dynamic-form/index.test.ts +++ /dev/null @@ -1,1594 +0,0 @@ -import type { CheckboxInstance, FormItemInstance, InputInstance } from '@arco-design/web-vue'; -import type { StringKeyOf, ValueOf } from 'type-fest'; -import type { VNodeChild } from 'vue'; -import type { DOMWrapper } from '@vue/test-utils'; -import { AcroDynamicForm, defineAcroDynamicFormField, defineAcroDynamicFormFields, defineAcroDynamicFormPreset } from '@mixte/snippets/acro-dynamic-form'; -import { config, mount } from '@vue/test-utils'; -import { Button } from '@arco-design/web-vue'; -import { deepClone } from 'mixte'; -import { presetMap } from './src/utils/defineAcroDynamicFormPreset'; -import type { AcroDynamicFormField } from './src/types'; - -// 来源: arco-design/arco-design-vue/packages/web-vue/scripts/demo-test.ts -Object.defineProperty(window, 'matchMedia', { - writable: true, - value: vi.fn().mockImplementation(query => ({ - matches: false, - media: query, - onchange: null, - addListener: vi.fn(), // deprecated - removeListener: vi.fn(), // deprecated - addEventListener: vi.fn(), - removeEventListener: vi.fn(), - dispatchEvent: vi.fn(), - })), -}); - -config.global.config.warnHandler = (msg) => { - if (msg.includes(`' was accessed via 'this'.`)) return; - - console.error(msg); -}; - -describe(' 基础测试', () => { - it('未传入任何参数, 只会渲染一个含有操作按钮的空表单', () => { - const wrapper = mount(AcroDynamicForm); - - const form = wrapper.findAll('.arco-form'); - const formItems = wrapper.findAll('.arco-form-item'); - const btns = formItems[0].findAll('.arco-btn'); - - expect(form.length).toBe(1); - expect(formItems.length).toBe(1); - expect(btns.length).toBe(2); - }); - - it('组件可传入 model 参数以收集及控制表单数据', async () => { - const model = ref>({}); - const wrapper = mount(AcroDynamicForm, { - props: { - fields: [ - { field: 'name', label: '姓名', type: 'input', defaultValue: '张三' }, - { field: 'age', label: '年龄', type: 'input', defaultValue: '18' }, - ], - model, - }, - }); - - const [nameInput, ageInput] = wrapper.findAll('.arco-input') as [DOMWrapper, DOMWrapper]; - - expect(model.value).toStrictEqual({ name: '张三', age: '18' }); - expect(nameInput.element.value).toBe('张三'); - expect(ageInput.element.value).toBe('18'); - - await nameInput.setValue('李四'); - await ageInput.setValue('20'); - - expect(model.value).toStrictEqual({ name: '李四', age: '20' }); - expect(nameInput.element.value).toBe('李四'); - expect(ageInput.element.value).toBe('20'); - - model.value.name = '王五'; - model.value.age = '22'; - await wrapper.vm.$nextTick(); - - expect(nameInput.element.value).toBe('王五'); - expect(ageInput.element.value).toBe('22'); - - model.value = { name: '赵六', age: '24' }; - await wrapper.vm.$nextTick(); - - expect(nameInput.element.value).toBe('赵六'); - expect(ageInput.element.value).toBe('24'); - }); - - it('非组件本身的参数, 会继承至 a-form 上', async () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: [ - { field: 'name', label: '姓名', type: 'input', defaultValue: '张三' }, - { field: 'age', label: '年龄', type: 'input', defaultValue: '18' }, - ], - disabled: false, - }, - }); - - expect(wrapper.findAll('.arco-input-disabled').length).toBe(0); - - wrapper.setProps({ disabled: true }); - await wrapper.vm.$nextTick(); - - expect(wrapper.findAll('.arco-input-disabled').length).toBe(2); - }); -}); - -describe(' 字段配置', () => { - it('传入字段配置, 会根据配置渲染表单项', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([ - { field: 'name', label: '姓名', type: 'input' }, - { field: 'age', label: '年龄', type: 'input-number' }, - ]), - actionButtonArea: false, - }, - }); - - const formItems = wrapper.findAll('.arco-form-item'); - - expect(formItems.length).toBe(2); - - expect(formItems[0].find('.arco-input').exists()).toBe(true); - expect(formItems[1].find('.arco-input-number').exists()).toBe(true); - - expect(formItems.map(item => item.find('.arco-form-item-label').text())).toEqual(['姓名', '年龄']); - }); - - describe('type & componentProps', () => { - it('类型测试: 当 type 为支持的表单类组件时, componentProps 会切换为对应组件的类型', () => { - // Input - { - const field: AcroDynamicFormField = { field: 'name', type: 'input' }; // eslint-disable-line unused-imports/no-unused-vars - - type ComponentProps = NonNullable<(typeof field)['componentProps']>; - - expectTypeOf>().not.toEqualTypeOf>(); - expectTypeOf< - StringKeyOf | 'modelValue' - >().toEqualTypeOf< - StringKeyOf - >(); - } - - // Checkbox - { - const field: AcroDynamicFormField = { field: 'name', type: 'checkbox' }; // eslint-disable-line unused-imports/no-unused-vars - - type ComponentProps = NonNullable<(typeof field)['componentProps']>; - - expectTypeOf>().not.toEqualTypeOf>(); - expectTypeOf< - StringKeyOf | 'modelValue' - >().toEqualTypeOf< - StringKeyOf - >(); - } - }); - - it('类型测试: 当 type 为空, 此时 componentProps 为随意输入内容的对象', () => { - expectTypeOf().toMatchTypeOf<{ type?: string }>(); - - const field: AcroDynamicFormField = { field: 'name' }; // eslint-disable-line unused-imports/no-unused-vars - - type ComponentProps = NonNullable<(typeof field)['componentProps']>; - - expectTypeOf().toEqualTypeOf>(); - }); - - it('类型测试: 当 type 为非支持的表单类组件时, 此时 componentProps 为随意输入内容的对象', () => { - const field: AcroDynamicFormField = { field: 'name', type: 'button' }; // eslint-disable-line unused-imports/no-unused-vars - - type ComponentProps = NonNullable<(typeof field)['componentProps']>; - - expectTypeOf().toEqualTypeOf>(); - }); - }); - - describe('render', () => { - describe('render 传入函数', () => { - it('传入函数渲染自定义组件', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([ - { field: 'name', label: '姓名', render: () => h('div', { class: 'custom-render' }, '张三') }, - { field: 'age', label: '年龄', render: () => h('div', { class: 'custom-render' }, '18') }, - ]), - actionButtonArea: false, - }, - }); - - const formItems = wrapper.findAll('.arco-form-item .custom-render'); - - expect(formItems.length).toBe(2); - - expect(formItems[0].text()).toBe('张三'); - expect(formItems[1].text()).toBe('18'); - }); - - it('函数会传入 model 参数, 为当前表单的表单数据', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - model: { name: '张三', age: '18' }, - fields: defineAcroDynamicFormFields([ - { field: 'name', type: 'input', label: '姓名', render: ({ model }) => h('div', { class: 'custom-render' }, model.name) }, - { field: 'age', type: 'input', label: '年龄', render: ({ model }) => h('div', { class: 'custom-render' }, model.age) }, - ]), - actionButtonArea: false, - }, - }); - - const formItems = wrapper.findAll('.arco-form-item .custom-render'); - - expect(formItems.length).toBe(2); - - expect(formItems[0].text()).toBe('张三'); - expect(formItems[1].text()).toBe('18'); - }); - - it('函数会传入 Component 参数, 为原组件渲染函数', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - model: { name: '张三', age: '18' }, - fields: defineAcroDynamicFormFields([ - { field: 'name', type: 'input', label: '姓名', render: ({ Component }) => h('div', { class: 'custom-render' }, [Component()]) }, - { field: 'age', type: 'input', label: '年龄', render: ({ Component }) => h('div', { class: 'custom-render' }, [Component()]) }, - ]), - actionButtonArea: false, - }, - }); - - const [nameInput, ageInput] = wrapper.findAll('.arco-form-item .custom-render .arco-input') as [DOMWrapper, DOMWrapper]; - - expect(nameInput.element.value).toBe('张三'); - expect(ageInput.element.value).toBe('18'); - }); - }); - - describe('render 传入插槽名称', () => { - it('传入插槽名称时, 将使用指定名称的插槽来渲染自定义组件', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([ - { field: 'name', label: '姓名', render: 'name' }, - { field: 'age', label: '年龄', render: 'age' }, - ]), - actionButtonArea: false, - }, - slots: { - name: () => h('div', { class: 'custom-render' }, '张三'), - age: () => h('div', { class: 'custom-render' }, '18'), - }, - }); - - const formItems = wrapper.findAll('.arco-form-item .custom-render'); - - expect(formItems.length).toBe(2); - - expect(formItems[0].text()).toBe('张三'); - expect(formItems[1].text()).toBe('18'); - }); - - it('插槽会传入 model 参数, 为当前表单的表单数据', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - model: { name: '张三', age: '18' }, - fields: defineAcroDynamicFormFields([ - { field: 'name', label: '姓名', render: 'name' }, - { field: 'age', label: '年龄', render: 'age' }, - ]), - actionButtonArea: false, - }, - slots: { - name: ({ model }) => h('div', { class: 'custom-render' }, model.name), - age: ({ model }) => h('div', { class: 'custom-render' }, model.age), - }, - }); - - const formItems = wrapper.findAll('.arco-form-item .custom-render'); - - expect(formItems.length).toBe(2); - - expect(formItems[0].text()).toBe('张三'); - expect(formItems[1].text()).toBe('18'); - }); - - it('插槽会传入 Component 参数, 为原组件渲染函数', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - model: { name: '张三', age: '18' }, - fields: defineAcroDynamicFormFields([ - { field: 'name', type: 'input', label: '姓名', render: 'name' }, - { field: 'age', type: 'input', label: '年龄', render: 'age' }, - ]), - actionButtonArea: false, - }, - slots: { - name: ({ Component }) => h('div', { class: 'custom-render' }, [Component()]), - age: ({ Component }) => h('div', { class: 'custom-render' }, [Component()]), - }, - }); - - const [nameInput, ageInput] = wrapper.findAll('.arco-form-item .custom-render .arco-input') as [DOMWrapper, DOMWrapper]; - - expect(nameInput.element.value).toBe('张三'); - expect(ageInput.element.value).toBe('18'); - }); - }); - }); - - describe('defaultValue', () => { - it('组件初始化时, 会设置表单项的初始值', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([ - { field: 'name', label: '姓名', type: 'input', defaultValue: '张三' }, - { field: 'age', label: '年龄', type: 'input' }, - ]), - }, - }); - - const [nameInput, ageInput] = wrapper.findAll('.arco-input') as [DOMWrapper, DOMWrapper]; - - expect(nameInput.element.value).toBe('张三'); - expect(ageInput.element.value).toBe(''); - - expect(wrapper.vm.model).toStrictEqual({ name: '张三' }); - }); - - it('若未设置该选项, 不会写入值', () => { - const model = reactive>({ name: '张三' }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([ - { field: 'name', label: '姓名', type: 'input' }, - { field: 'age', label: '年龄', type: 'input' }, - ]), - model, - }, - }); - - const [nameInput, ageInput] = wrapper.findAll('.arco-input') as [DOMWrapper, DOMWrapper]; - - expect(nameInput.element.value).toBe('张三'); - expect(ageInput.element.value).toBe(''); - - expect(wrapper.vm.model).toStrictEqual({ name: '张三' }); - }); - - it('当从外部传入 model 时, defaultValue 不会覆盖有值的数据', () => { - const model = reactive>({ name: '张三' }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([ - { field: 'name', label: '姓名', type: 'input', defaultValue: '李四' }, - { field: 'age', label: '年龄', type: 'input' }, - ]), - model, - }, - }); - - const [nameInput, ageInput] = wrapper.findAll('.arco-input') as [DOMWrapper, DOMWrapper]; - - expect(nameInput.element.value).toBe('张三'); - expect(ageInput.element.value).toBe(''); - - expect(wrapper.vm.model).toStrictEqual({ name: '张三' }); - }); - }); - - describe('componentProps', () => { - describe('modelValue', () => { - it('使用 componentProps 传递的 modelValue 不会覆盖 v-model 的值', async () => { - const model = reactive>({}); - const componentProps = reactive>({}); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ field: 'name', label: '姓名', type: 'input', defaultValue: '', componentProps }]), - model, - }, - }); - - expect(model.name).toBe(''); - expect((wrapper.find('.arco-input').element as HTMLInputElement).value).toBe(''); - - // 组件修改值 √ - await wrapper.find('.arco-input').setValue('张三'); - expect(model.name).toBe('张三'); - expect((wrapper.find('.arco-input').element as HTMLInputElement).value).toBe('张三'); - - // model 修改值 √ - model.name = '李四'; - await wrapper.vm.$nextTick(); - expect(model.name).toBe('李四'); - expect((wrapper.find('.arco-input').element as HTMLInputElement).value).toBe('李四'); - - // 通过 componentProps 修改值 × - componentProps.modelValue = '王五'; - await wrapper.vm.$nextTick(); - expect(model.name).toBe('李四'); - expect((wrapper.find('.arco-input').element as HTMLInputElement).value).toBe('李四'); - }); - - it('使用 componentProps 传递的 onUpdate:modelValue 事件依旧可以触发', async () => { - const model = reactive>({}); - let value = ''; - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - label: '姓名', - type: 'input', - defaultValue: '', - componentProps: { - 'onUpdate:modelValue': (val) => { - value = val; - }, - }, - }]), - model, - }, - }); - - expect(value).toBe(''); - expect(model.name).toBe(''); - expect((wrapper.find('.arco-input').element as HTMLInputElement).value).toBe(''); - - await wrapper.find('.arco-input').setValue('张三'); - - expect(value).toBe('张三'); - expect(model.name).toBe('张三'); - expect((wrapper.find('.arco-input').element as HTMLInputElement).value).toBe('张三'); - }); - - it('类型测试: 已从 componentProps 中排除 modelValue 字段', () => { - const field: AcroDynamicFormField = { field: 'name', type: 'input' }; // eslint-disable-line unused-imports/no-unused-vars - - type ComponentProps = NonNullable<(typeof field)['componentProps']>; - - expectTypeOf>().not.toEqualTypeOf>(); - expectTypeOf< - StringKeyOf | 'modelValue' - >().toEqualTypeOf< - StringKeyOf - >(); - }); - }); - }); - - describe('componentSlots', () => { - it('使用 componentSlots 可传递插槽给组件', async () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: [{ field: 'name', label: '姓名', type: 'input' }], - }, - }); - - expect(wrapper.find('.slot-666').exists()).toBe(false); - expect(wrapper.find('.slot-777').exists()).toBe(false); // 默认插槽不生效 - - wrapper.setProps({ - fields: [{ - field: 'name', - label: '姓名', - type: 'input', - componentSlots: { - suffix: () => h('div', { class: 'slot-666' }), - default: () => h('div', { class: 'slot-777' }), - }, - }], - }); - - await wrapper.vm.$nextTick(); - - expect(wrapper.find('.slot-666').exists()).toBe(true); - expect(wrapper.find('.slot-777').exists()).toBe(false); // 默认插槽不生效 - }); - - it('类型测试: 不限制插槽方法传参类型, 插槽方法返回值为 VNodeChild', () => { - type ComponentSlots = NonNullable; - type ComponentSlotValue = ValueOf; - - expectTypeOf>().toEqualTypeOf(); - expectTypeOf>().toEqualTypeOf(); - }); - }); - - describe('formItemProps', () => { - it('使用 formItemProps 可传递参数给 a-form-item 组件', async () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ field: 'name', label: '姓名', type: 'input' }]), - }, - }); - - expect(wrapper.find('.test-666').exists()).toBe(false); - - await wrapper.setProps({ - fields: defineAcroDynamicFormFields([{ - field: 'name', - label: '姓名', - type: 'input', - formItemProps: { - class: 'test-666', - }, - }]), - }); - - expect(wrapper.find('.test-666').exists()).toBe(true); - }); - - describe('已排除的字段', () => { - it('传递的 field,label,rules,validateTrigger 不会覆盖表单项配置中的对应字段', async () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: [{ - field: 'name', - label: '姓名', - type: 'input', - rules: [{ required: true }], - validateTrigger: 'blur', - }], - }, - }); - - expect(wrapper.find('.arco-form-item-wrapper-col').attributes('id')).toBe('name'); - expect(wrapper.find('.arco-form-item-label').text()).toBe('姓名'); - expect(wrapper.find('.arco-form-item-label-required-symbol').exists()).toBe(true); - - expect(wrapper.find('.arco-form-item-message').exists()).toBe(false); - await wrapper.find('.arco-input').trigger('blur'); - expect(wrapper.find('.arco-form-item-message').exists()).toBe(true); - - wrapper.vm.reset(); - await wrapper.vm.$nextTick(); - expect(wrapper.find('.arco-form-item-message').exists()).toBe(false); - - // 同时存在时, 以表单项配置为准 - - await wrapper.setProps({ - fields: [{ - field: 'name', - label: '姓名', - type: 'input', - rules: [{ required: true }], - validateTrigger: 'blur', // @ts-expect-error - formItemProps: { field: 'name-666', label: '姓名-666', rules: [{ required: true }], validateTrigger: 'change' }, - }], - }); - - expect(wrapper.find('.arco-form-item-wrapper-col').attributes('id')).toBe('name'); - expect(wrapper.find('.arco-form-item-label').text()).toBe('姓名'); - expect(wrapper.find('.arco-form-item-label-required-symbol').exists()).toBe(true); - - expect(wrapper.find('.arco-form-item-message').exists()).toBe(false); - await wrapper.find('.arco-input').trigger('blur'); - expect(wrapper.find('.arco-form-item-message').exists()).toBe(true); - - wrapper.vm.reset(); - await wrapper.vm.$nextTick(); - expect(wrapper.find('.arco-form-item-message').exists()).toBe(false); - - // 单独在 formItemProps 中配置, 不会生效 - - await wrapper.setProps({ - fields: [{ - type: 'input', // @ts-expect-error - formItemProps: { field: 'name-666', label: '姓名-666', rules: [{ required: true }], validateTrigger: 'change' }, - }], - }); - - expect(wrapper.find('.arco-form-item-wrapper-col').attributes('id')).toBe(undefined); - expect(wrapper.find('.arco-form-item-label').text()).toBe(''); - expect(wrapper.find('.arco-form-item-label-required-symbol').exists()).toBe(false); - - expect(wrapper.find('.arco-form-item-message').exists()).toBe(false); - await wrapper.find('.arco-input').trigger('blur'); - expect(wrapper.find('.arco-form-item-message').exists()).toBe(false); - }); - - it('类型测试: 已排除 field,label,rules,validateTrigger 字段', () => { - expectTypeOf>>().not.toEqualTypeOf>(); - expectTypeOf< - StringKeyOf> | 'field' | 'label' | 'rules' | 'validateTrigger' - >().toEqualTypeOf< - StringKeyOf - >(); - }); - }); - }); - - describe('formItemSlots', () => { - it('使用 formItemSlots 可传递插槽给 a-form-item 组件', async () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: [{ field: 'name', label: '姓名', type: 'input' }], - }, - }); - - expect(wrapper.find('.arco-form-item-label > .slot-666').exists()).toBe(false); - expect(wrapper.find('.arco-form-item-message-help > .slot-777').exists()).toBe(false); - expect(wrapper.find('.arco-form-item-extra > .slot-888').exists()).toBe(false); - expect(wrapper.find('.slot-999').exists()).toBe(false); // 默认插槽不生效 - - await wrapper.setProps({ - fields: [{ - field: 'name', - label: '姓名', - type: 'input', - formItemSlots: { - label: () => h('div', { class: 'slot-666' }), - help: () => h('div', { class: 'slot-777' }), - extra: () => h('div', { class: 'slot-888' }), - default: () => h('div', { class: 'slot-999' }), - }, - }], - }); - - expect(wrapper.find('.arco-form-item-label > .slot-666').exists()).toBe(true); - expect(wrapper.find('.arco-form-item-message-help > .slot-777').exists()).toBe(true); - expect(wrapper.find('.arco-form-item-extra > .slot-888').exists()).toBe(true); - expect(wrapper.find('.slot-999').exists()).toBe(false); // 默认插槽不生效 - }); - - it('类型测试: 不限制插槽方法传参类型, 插槽方法返回值为 VNodeChild', () => { - type FormItemSlots = NonNullable; - type FormItemSlotValue = ValueOf; - - expectTypeOf>>().toEqualTypeOf(); - expectTypeOf>>().toEqualTypeOf(); - }); - }); - - describe('preset', () => { - it('字段配置会继承预设中的配置', () => { - const preset = defineAcroDynamicFormPreset({ - name: { label: '姓名', type: 'input', defaultValue: '张三' }, - }); - - // 不使用预设 - { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ field: 'name' }]), - actionButtonArea: false, - }, - }); - - const label = wrapper.find('.arco-form-item-label'); - const input = wrapper.find('.arco-input') as DOMWrapper; - - expect(label.text()).toBe(''); - expect(input.exists()).toBe(false); - } - - // 使用预设 - { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: preset.name, - }]), - actionButtonArea: false, - }, - }); - - const label = wrapper.find('.arco-form-item-label'); - const input = wrapper.find('.arco-input') as DOMWrapper; - - expect(label.text()).toBe('姓名'); - expect(input.element.value).toBe('张三'); - } - }); - - it('支持同时配置多个预设', () => { - const preset = defineAcroDynamicFormPreset({ - name: { label: '姓名', type: 'input' }, - }); - const preset2 = defineAcroDynamicFormPreset({ - name: { type: 'input' }, - }); - const preset3 = defineAcroDynamicFormPreset({ - name: { defaultValue: '张三' }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: [preset.name, preset2.name, preset3.name], - }]), - actionButtonArea: false, - }, - }); - - const label = wrapper.find('.arco-form-item-label'); - const input = wrapper.find('.arco-input') as DOMWrapper; - - expect(label.text()).toBe('姓名'); - expect(input.element.value).toBe('张三'); - }); - - it('字段配置不会继承预设中的 field 选项', () => { - // 和当前配置不合并 - { - const preset = defineAcroDynamicFormPreset({ // @ts-expect-error - name: { field: 'preset-name' }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: preset.name, - }]), - actionButtonArea: false, - }, - }); - - const formItem = wrapper.find('.arco-form-item-wrapper-col'); - - expect(formItem.attributes('id')).toBe('name'); - } - - // 不同预设之间不合并 - { - const preset = defineAcroDynamicFormPreset({ // @ts-expect-error - name: { field: 'preset-name' }, - }); - const preset2 = defineAcroDynamicFormPreset({ // @ts-expect-error - name: { field: 'preset-name-666' }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: [preset.name, preset2.name], - }]), - actionButtonArea: false, - }, - }); - - const formItem = wrapper.find('.arco-form-item-wrapper-col'); - - expect(formItem.attributes('id')).toBe('name'); - } - }); - - it('字段配置的 formItemProps 选项会进行合并', () => { - // 和当前配置合并 - { - const preset = defineAcroDynamicFormPreset({ - name: { label: '姓名', type: 'input', formItemProps: { class: 'test-666' } }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: preset.name, - formItemProps: { class: 'test-777' }, - }]), - actionButtonArea: false, - }, - }); - - const formItem = wrapper.find('.arco-form-item'); - - expect(formItem.classes()).toContain('test-666'); - expect(formItem.classes()).toContain('test-777'); - } - - // 不同预设之间合并 - { - const preset = defineAcroDynamicFormPreset({ - name: { label: '姓名', type: 'input', formItemProps: { class: 'test-666' } }, - }); - const preset2 = defineAcroDynamicFormPreset({ - name: { formItemProps: { class: 'test-777' } }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: [preset.name, preset2.name], - }]), - actionButtonArea: false, - }, - }); - - const formItem = wrapper.find('.arco-form-item'); - - expect(formItem.classes()).toContain('test-666'); - expect(formItem.classes()).toContain('test-777'); - } - }); - - it('字段配置的 formItemSlots 选项会进行覆盖', () => { - // 和当前配置合并 - { - const preset = defineAcroDynamicFormPreset({ - name: { - label: '姓名', - type: 'input', - formItemSlots: { - label: () => h('div', { class: 'slot-666' }), - }, - }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: preset.name, - formItemSlots: { - label: () => h('div', { class: 'slot-777' }), - }, - }]), - actionButtonArea: false, - }, - }); - - const formItem = wrapper.find('.arco-form-item'); - - expect(formItem.find('.slot-666').exists()).toBe(false); - expect(formItem.find('.slot-777').exists()).toBe(true); - } - - // 不同预设之间合并 - { - const preset = defineAcroDynamicFormPreset({ - name: { - label: '姓名', - type: 'input', - formItemSlots: { - label: () => h('div', { class: 'slot-666' }), - }, - }, - }); - const preset2 = defineAcroDynamicFormPreset({ - name: { - formItemSlots: { - label: () => h('div', { class: 'slot-777' }), - }, - }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: [preset.name, preset2.name], - }]), - actionButtonArea: false, - }, - }); - - const formItem = wrapper.find('.arco-form-item'); - - expect(formItem.find('.slot-666').exists()).toBe(false); - expect(formItem.find('.slot-777').exists()).toBe(true); - } - }); - - it('字段配置的 componentProps 选项会进行合并', () => { - // 和当前配置合并 - { - const preset = defineAcroDynamicFormPreset({ - name: { label: '姓名', type: 'input', componentProps: { class: 'test-666' } }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: preset.name, - componentProps: { class: 'test-777' }, - }]), - actionButtonArea: false, - }, - }); - - const input = wrapper.find('.arco-input-wrapper'); - - expect(input.classes()).toContain('test-666'); - expect(input.classes()).toContain('test-777'); - } - - // 不同预设之间合并 - { - const preset = defineAcroDynamicFormPreset({ - name: { label: '姓名', type: 'input', componentProps: { class: 'test-666' } }, - }); - const preset2 = defineAcroDynamicFormPreset({ - name: { componentProps: { class: 'test-777' } }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: [preset.name, preset2.name], - }]), - actionButtonArea: false, - }, - }); - - const input = wrapper.find('.arco-input-wrapper'); - - expect(input.classes()).toContain('test-666'); - expect(input.classes()).toContain('test-777'); - } - }); - - it('字段配置的 componentSlots 选项会进行覆盖', () => { - // 和当前配置合并 - { - const preset = defineAcroDynamicFormPreset({ - name: { - label: '姓名', - type: 'input', - componentSlots: { - suffix: () => h('div', { class: 'slot-666' }), - }, - }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: preset.name, - componentSlots: { - suffix: () => h('div', { class: 'slot-777' }), - }, - }]), - actionButtonArea: false, - }, - }); - - const input = wrapper.find('.arco-input-wrapper'); - - expect(input.find('.slot-666').exists()).toBe(false); - expect(input.find('.slot-777').exists()).toBe(true); - } - - // 不同预设之间合并 - { - const preset = defineAcroDynamicFormPreset({ - name: { - label: '姓名', - type: 'input', - componentSlots: { - suffix: () => h('div', { class: 'slot-666' }), - }, - }, - }); - const preset2 = defineAcroDynamicFormPreset({ - name: { - componentSlots: { - suffix: () => h('div', { class: 'slot-777' }), - }, - }, - }); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([{ - field: 'name', - preset: [preset.name, preset2.name], - }]), - actionButtonArea: false, - }, - }); - - const input = wrapper.find('.arco-input-wrapper'); - - expect(input.find('.slot-666').exists()).toBe(false); - expect(input.find('.slot-777').exists()).toBe(true); - } - }); - }); -}); - -describe(' 导出方法及对象', () => { - describe('methods', () => { - it('组件导出的 a-form 组件本身的方法', () => { - const wrapper = mount(AcroDynamicForm); - - // 校验全部表单数据 - expect(wrapper.vm.validate).toBeInstanceOf(Function); - // 校验部分表单数据 - expect(wrapper.vm.validateField).toBeInstanceOf(Function); - // 重置表单数据 - expect(wrapper.vm.resetFields).toBeInstanceOf(Function); - // 清除校验状态 - expect(wrapper.vm.clearValidate).toBeInstanceOf(Function); - // 设置表单项的值和状态 - expect(wrapper.vm.setFields).toBeInstanceOf(Function); - // 滚动到指定表单项 - expect(wrapper.vm.scrollToField).toBeInstanceOf(Function); - }); - - describe('组件导出的额外扩展方法', () => { - it('reset: 是 `resetFields` 方法的别名', () => { - const wrapper = mount(AcroDynamicForm); - - expect(wrapper.vm.reset).toBeInstanceOf(Function); - expect(wrapper.vm.reset).toBe(wrapper.vm.resetFields); - }); - - it('init', async () => { - const fields = defineAcroDynamicFormFields([ - { field: 'name', label: '姓名', type: 'input', defaultValue: '张三' }, - ]); - const fields2 = defineAcroDynamicFormFields([ - { field: 'age', label: '年龄', type: 'input', defaultValue: '18' }, - ]); - - const wrapper = mount(AcroDynamicForm, { - props: { - fields, - }, - }); - - expect(wrapper.vm.model).toStrictEqual({ name: '张三' }); - - await wrapper.setProps({ fields: fields2 }); - - expect(wrapper.vm.model).toStrictEqual({ name: '张三' }); - - wrapper.vm.init(); - expect(wrapper.vm.model).toStrictEqual({ name: '张三', age: '18' }); - }); - }); - }); - - describe('data', () => { - it('组件导出的 model 为收集的表单数据, 也可对值进行修改', async () => { - const wrapper = mount(AcroDynamicForm, { - props: { - fields: defineAcroDynamicFormFields([ - { field: 'name', label: '姓名', type: 'input' }, - { field: 'age', label: '年龄', type: 'input' }, - ]), - }, - }); - - expect(wrapper.vm.model).toStrictEqual({}); - - const [nameInput, ageInput] = wrapper.findAll('.arco-input') as [DOMWrapper, DOMWrapper]; - - await nameInput.setValue('张三'); - await ageInput.setValue('18'); - - expect(wrapper.vm.model).toStrictEqual({ name: '张三', age: '18' }); - - wrapper.vm.model.name = '李四'; - wrapper.vm.model.age = '20'; - - await wrapper.vm.$nextTick(); - - expect(nameInput.element.value).toBe('李四'); - expect(ageInput.element.value).toBe('20'); - }); - - it('若组件传入 model 参数, 那么组件内定义的 model 传入的参数, 但是会重新包装一下', () => { - const model = reactive>({ name: '张三' }); - - const wrapper = mount(AcroDynamicForm, { - props: { - model, - fields: defineAcroDynamicFormFields([ - { field: 'age', label: '年龄', type: 'input' }, - ]), - }, - }); - - expect(model).toStrictEqual(wrapper.vm.model); - expect(model).not.toBe(wrapper.vm.model); - - // 修改已有的值 - - model.name = '李四'; - expect(wrapper.vm.model.name).toBe('李四'); - - wrapper.vm.model.name = '王五'; - expect(model.name).toBe('王五'); - - // 新增值 - - wrapper.vm.model.age = '20'; - expect(model.age).toBe('20'); - - model.age = '22'; - expect(wrapper.vm.model.age).toBe('22'); - }); - }); -}); - -describe(' 事件', () => { - it('点击提交按钮, 会触发 submit 事件', async () => { - let submitData: Record = {}; - let submitCount = 0; - - function handleSubmit(model: Record) { - submitData = model; - submitCount++; - } - - const wrapper = mount(AcroDynamicForm, { - props: { - fields: [ - { field: 'name', label: '姓名', type: 'input' }, - { field: 'age', label: '年龄', type: 'input' }, - ], - onSubmit: handleSubmit, - }, - }); - - const [nameInput, ageInput] = wrapper.findAll('.arco-input') as [DOMWrapper, DOMWrapper]; - const submitBtn = wrapper.findAll('.arco-btn').find(btn => btn.text() === '提交')!; - - expect(submitCount).toBe(0); - expect(submitData).toStrictEqual({}); - - submitBtn.trigger('click'); - expect(submitCount).toBe(1); - expect(submitData).toStrictEqual({}); - - await nameInput.setValue('张三'); - await ageInput.setValue('18'); - - submitBtn.trigger('click'); - expect(submitCount).toBe(2); - expect(submitData).toStrictEqual({ name: '张三', age: '18' }); - }); - - it('点击重置按钮, 会触发 reset 事件', async () => { - let resetCount = 0; - - function handleReset() { - resetCount++; - } - - const wrapper = mount(AcroDynamicForm, { - props: { - onReset: handleReset, - }, - }); - - const resetBtn = wrapper.findAll('.arco-btn').find(btn => btn.text() === '重置')!; - - expect(resetCount).toBe(0); - - resetBtn.trigger('click'); - - expect(resetCount).toBe(1); - - resetBtn.trigger('click'); - - expect(resetCount).toBe(2); - }); -}); - -describe(' 操作按钮', () => { - it('默认情况下, 提交按钮和重置按钮都会显示', () => { - const wrapper = mount(AcroDynamicForm); - - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(btns.length).toBe(2); - expect(btns[0].text()).toBe('提交'); - expect(btns[1].text()).toBe('重置'); - }); - - it('配置不显示操作按钮区域, a-form-item 及提交按钮、重置按钮不会渲染', () => { - // boolean 传值 - { - const wrapper = mount(AcroDynamicForm, { - props: { - actionButtonArea: false, - }, - }); - - const formItems = wrapper.findAll('.arco-form-item'); - const btns = wrapper.findAll('.arco-btn'); - - expect(formItems.length).toBe(0); - expect(btns.length).toBe(0); - } - - // options 传值 - { - const wrapper = mount(AcroDynamicForm, { - props: { - actionButtonArea: { show: false }, - }, - }); - - const formItems = wrapper.findAll('.arco-form-item'); - const btns = wrapper.findAll('.arco-btn'); - - expect(formItems.length).toBe(0); - expect(btns.length).toBe(0); - } - }); - - it('配置不显示提交按钮', () => { - // boolean 传值 - { - const wrapper = mount(AcroDynamicForm, { - props: { - submitButton: false, - }, - }); - - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(btns.length).toBe(1); - expect(btns[0].text()).toBe('重置'); - } - - // options 传值 - { - const wrapper = mount(AcroDynamicForm, { - props: { - submitButton: { show: false }, - }, - }); - - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(btns.length).toBe(1); - expect(btns[0].text()).toBe('重置'); - } - }); - - it('配置不显示重置按钮', () => { - // boolean 传值 - { - const wrapper = mount(AcroDynamicForm, { - props: { - resetButton: false, - }, - }); - - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(btns.length).toBe(1); - expect(btns[0].text()).toBe('提交'); - } - - // options 传值 - { - const wrapper = mount(AcroDynamicForm, { - props: { - resetButton: { show: false }, - }, - }); - - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(btns.length).toBe(1); - expect(btns[0].text()).toBe('提交'); - } - }); - - it('配置提交按钮和重置按钮都不显示时, a-form-item 也不会渲染', () => { - // boolean 传值 - { - const wrapper = mount(AcroDynamicForm, { - props: { - submitButton: false, - resetButton: false, - }, - }); - - expect(wrapper.findAll('.arco-form-item').length).toBe(0); - } - - // options 传值 - { - const wrapper = mount(AcroDynamicForm, { - props: { - submitButton: { show: false }, - resetButton: { show: false }, - }, - }); - - expect(wrapper.findAll('.arco-form-item').length).toBe(0); - } - }); - - it('配置提交按钮和重置按钮的文字', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - submitButton: { text: '自定义提交' }, - resetButton: { text: '自定义重置' }, - }, - }); - - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(btns.length).toBe(2); - expect(btns[0].text()).toBe('自定义提交'); - expect(btns[1].text()).toBe('自定义重置'); - }); - - it('配置操作按钮区域 FormItem 组件的参数', () => { - // 不传值情况下 - { - const wrapper = mount(AcroDynamicForm); - const formItem = wrapper.find('.arco-form-item'); - - expect(formItem.exists()).toBe(true); - expect(formItem.classes()).not.includes('arco-form-item-status-success'); - } - - // 传值情况下 - { - const wrapper = mount(AcroDynamicForm, { - props: { - actionButtonArea: { - props: { validateStatus: 'success' }, - }, - }, - }); - const formItem = wrapper.find('.arco-form-item'); - - expect(formItem.exists()).toBe(true); - expect(formItem.classes()).includes('arco-form-item-status-success'); - } - }); - - it('传递给操作按钮区域 Space 组件的参数', () => { - // 不传值情况下 - { - const wrapper = mount(AcroDynamicForm); - const space = wrapper.find('.arco-space'); - - expect(space.exists()).toBe(true); - expect(space.classes()).includes('arco-space-horizontal'); - expect(space.classes()).not.includes('arco-space-vertical'); - } - - // 传值情况下 - { - const wrapper = mount(AcroDynamicForm, { - props: { - actionButtonArea: { - spaceProps: { direction: 'vertical' }, - }, - }, - }); - - const space = wrapper.find('.arco-space'); - - expect(space.exists()).toBe(true); - expect(space.classes()).not.includes('arco-space-horizontal'); - expect(space.classes()).includes('arco-space-vertical'); - } - }); - - it('配置提交按钮组件的参数', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - submitButton: { - props: { size: 'mini' }, - }, - }, - }); - - const submitBtn = wrapper.find('.arco-btn.arco-btn-size-mini'); - - expect(submitBtn.exists()).toBe(true); - expect(submitBtn.text()).toBe('提交'); - }); - - it('配置重置按钮组件的参数', () => { - const wrapper = mount(AcroDynamicForm, { - props: { - resetButton: { - props: { size: 'mini' }, - }, - }, - }); - - const resetBtn = wrapper.find('.arco-btn.arco-btn-size-mini'); - - expect(resetBtn.exists()).toBe(true); - expect(resetBtn.text()).toBe('重置'); - }); - - describe('操作按钮区域插槽, 及按钮前后置插槽', () => { - it('支持传入 actionButtonArea 插槽, 可使用该插槽代替操作按钮区域的渲染', () => { - const wrapper = mount(AcroDynamicForm, { - slots: { - actionButtonArea: () => h('div', { class: 'slot-666' }), - }, - }); - - expect(wrapper.findAll('.arco-form-item').length).toBe(0); - expect(wrapper.find('.slot-666').exists()).toBe(true); - }); - - it('支持传入 actionButtonPrepend 插槽, 可插入内容到提交按钮前面', () => { - const wrapper = mount(AcroDynamicForm, { - slots: { - actionButtonPrepend: () => h(Button, {}, '前置按钮'), - }, - }); - - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(btns.length).toBe(3); - expect(btns[0].text()).toBe('前置按钮'); - expect(btns[1].text()).toBe('提交'); - expect(btns[2].text()).toBe('重置'); - }); - - it('支持传入 actionButtonAppend 插槽, 可插入内容到重置按钮后面', () => { - const wrapper = mount(AcroDynamicForm, { - slots: { - actionButtonAppend: () => h(Button, {}, '后置按钮'), - }, - }); - - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(btns.length).toBe(3); - expect(btns[0].text()).toBe('提交'); - expect(btns[1].text()).toBe('重置'); - expect(btns[2].text()).toBe('后置按钮'); - }); - - it('配置提交按钮和重置按钮都不显示时, 只传入 actionButtonPrepend 和 actionButtonAppend 插槽, a-form-item 也会渲染', () => { - // actionButtonPrepend - { - const wrapper = mount(AcroDynamicForm, { - props: { - submitButton: false, - resetButton: false, - }, - slots: { - actionButtonPrepend: () => h(Button, {}, '前置按钮'), - }, - }); - - const formItems = wrapper.findAll('.arco-form-item'); - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(formItems.length).toBe(1); - expect(btns.length).toBe(1); - expect(btns[0].text()).toBe('前置按钮'); - } - - // actionButtonAppend - { - const wrapper = mount(AcroDynamicForm, { - props: { - submitButton: false, - resetButton: false, - }, - slots: { - actionButtonAppend: () => h(Button, {}, '后置按钮'), - }, - }); - - const formItems = wrapper.findAll('.arco-form-item'); - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(formItems.length).toBe(1); - expect(btns.length).toBe(1); - expect(btns[0].text()).toBe('后置按钮'); - } - - // actionButtonPrepend & actionButtonAppend - { - const wrapper = mount(AcroDynamicForm, { - props: { - submitButton: false, - resetButton: false, - }, - slots: { - actionButtonPrepend: () => h(Button, {}, '前置按钮'), - actionButtonAppend: () => h(Button, {}, '后置按钮'), - }, - }); - - const formItems = wrapper.findAll('.arco-form-item'); - const btns = wrapper.findAll('.arco-form-item .arco-btn'); - - expect(formItems.length).toBe(1); - expect(btns.length).toBe(2); - expect(btns[0].text()).toBe('前置按钮'); - expect(btns[1].text()).toBe('后置按钮'); - } - }); - - it('当使用 actionButtonArea 插槽时, actionButtonPrepend 和 actionButtonAppend 插槽不生效', () => { - const wrapper = mount(AcroDynamicForm, { - slots: { - actionButtonArea: () => h('div', { class: 'slot-666' }), - actionButtonPrepend: () => h(Button, {}, '前置按钮'), - actionButtonAppend: () => h(Button, {}, '后置按钮'), - }, - }); - - expect(wrapper.findAll('.arco-form-item').length).toBe(0); - expect(wrapper.find('.slot-666').exists()).toBe(true); - }); - }); -}); - -describe('导出的工具方法', () => { - describe('defineAcroDynamicFormField', () => { - it('方法原样返回传入的字段配置', () => { - const field = { field: 'name', label: '姓名', type: 'input' }; - const fieldClone = deepClone(field); - - expect(defineAcroDynamicFormField(field)).toBe(field); - expect(defineAcroDynamicFormField(field)).toStrictEqual(fieldClone); - }); - - it('类型测试: 用于定义单个字段配置', () => { - expectTypeOf>().toEqualTypeOf<[AcroDynamicFormField]>(); - expectTypeOf>().toEqualTypeOf(); - }); - }); - - describe('defineAcroDynamicFormFields', () => { - it('方法原样返回传入的字段配置数组', () => { - const fields = [ - { field: 'name', label: '姓名', type: 'input' }, - { field: 'age', label: '年龄', type: 'input-number' }, - ]; - const fieldsClone = deepClone(fields); - - expect(defineAcroDynamicFormFields(fields)).toBe(fields); - expect(defineAcroDynamicFormFields(fields)).toStrictEqual(fieldsClone); - }); - - it('类型测试: 用于定义多个字段配置', () => { - expectTypeOf>().toEqualTypeOf<[AcroDynamicFormField[]]>(); - expectTypeOf>().toEqualTypeOf(); - }); - }); - - describe('defineAcroDynamicFormPreset', () => { - it('方法传入一个预设 Map, 返回原本预设名称和对应预设的 Symbol', () => { - const presetKeyMap = defineAcroDynamicFormPreset({ - customInput: { type: 'input', defaultValue: '张三' }, - customInputNumber: { type: 'input-number', defaultValue: 18 }, - }); - - expect(Object.keys(presetKeyMap)).toStrictEqual(['customInput', 'customInputNumber']); - - Object.values(presetKeyMap).forEach((key) => { - expect(typeof key).toBe('symbol'); - }); - }); - - it('通过方法定义的预设, 都会存在预设映射缓存中', { skip: __TEST_BUILD__ }, () => { - const presets = { - customInput: { type: 'input', defaultValue: '张三' }, - customInputNumber: { type: 'input-number', defaultValue: 18 }, - }; - - const presetKeyMap = defineAcroDynamicFormPreset(presets); - - expect(presetMap.get(presetKeyMap.customInput)).toBe(presets.customInput); - expect(presetMap.get(presetKeyMap.customInputNumber)).toBe(presets.customInputNumber); - }); - - it('类型测试: 传值和返回值', () => { - type DefineAcroDynamicFormPreset = typeof defineAcroDynamicFormPreset; - type PresetAcroDynamicFormField = Omit; - - expectTypeOf>().toEqualTypeOf<[ - Record, - ]>(); - expectTypeOf>().toEqualTypeOf< - Record - >(); - }); - }); -}); diff --git a/packages/snippets/src/acro-dynamic-form/index.ts b/packages/snippets/src/acro-dynamic-form/index.ts deleted file mode 100644 index e320e090..00000000 --- a/packages/snippets/src/acro-dynamic-form/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import AcroDynamicForm from './src/index.vue'; -import { defineAcroDynamicFormField, defineAcroDynamicFormFields } from './src/utils/defineAcroDynamicFormFields'; -import { defineAcroDynamicFormPreset } from './src/utils/defineAcroDynamicFormPreset'; -import type { AcroDynamicFormField, AcroDynamicFormProps } from './src/types'; - -type AcroDynamicFormInstance = InstanceType; - -export type { - AcroDynamicFormInstance, - AcroDynamicFormProps, - AcroDynamicFormField, -}; - -export { - AcroDynamicForm, - - defineAcroDynamicFormField, - defineAcroDynamicFormFields, - - defineAcroDynamicFormPreset, -}; diff --git a/packages/snippets/src/acro-dynamic-form/info.ts b/packages/snippets/src/acro-dynamic-form/info.ts deleted file mode 100644 index 564bc9de..00000000 --- a/packages/snippets/src/acro-dynamic-form/info.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { Info } from '@/.vitepress/types/info'; - -export const title: Info['title'] = 'AcroDynamicForm'; -export const name: Info['name'] = 'Acro 动态表单'; -export const sidebarTitle: Info['sidebarTitle'] = 'AcroDynamicForm'; diff --git a/packages/snippets/src/acro-dynamic-form/src/components/RenderComponent.vue b/packages/snippets/src/acro-dynamic-form/src/components/RenderComponent.vue deleted file mode 100644 index 2a50e695..00000000 --- a/packages/snippets/src/acro-dynamic-form/src/components/RenderComponent.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - diff --git a/packages/snippets/src/acro-dynamic-form/src/components/RenderFormItem.vue b/packages/snippets/src/acro-dynamic-form/src/components/RenderFormItem.vue deleted file mode 100644 index 62f2387d..00000000 --- a/packages/snippets/src/acro-dynamic-form/src/components/RenderFormItem.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/packages/snippets/src/acro-dynamic-form/src/composables/useActionButtonArea.ts b/packages/snippets/src/acro-dynamic-form/src/composables/useActionButtonArea.ts deleted file mode 100644 index 09912751..00000000 --- a/packages/snippets/src/acro-dynamic-form/src/composables/useActionButtonArea.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { SetRequired } from 'type-fest'; -import { computed } from 'vue'; -import { deepMerge, isBoolean } from 'mixte'; -import { reactiveComputed } from '@vueuse/core'; -import type { AcroDynamicFormProps, AcroDynamicFormSlots, ActionButtonAreaOptions, SubmitButtonOptions } from '../types'; - -export function useActionButtonArea(props: AcroDynamicFormProps, slots: AcroDynamicFormSlots) { - /** 操作按钮区域配置 */ - const actionButtonArea = reactiveComputed(() => { - const actionButtonArea = props.actionButtonArea; - const options: SetRequired = { show: true }; - - if (isBoolean(actionButtonArea)) options.show = actionButtonArea; - else deepMerge(options, actionButtonArea); - - return options; - }); - - /** 提交按钮配置 */ - const submitButton = reactiveComputed(() => { - const submitButton = props.submitButton; - const options: SetRequired = { show: true, text: '提交' }; - - if (isBoolean(submitButton)) options.show = submitButton; - else deepMerge(options, submitButton); - - return options; - }); - - /** 重置按钮配置 */ - const resetButton = reactiveComputed(() => { - const resetButton = props.resetButton; - const options: SetRequired = { show: true, text: '重置' }; - - if (isBoolean(resetButton)) options.show = resetButton; - else deepMerge(options, resetButton); - - return options; - }); - - /** 是否显示操作按钮区域 */ - const showActionButtonArea = computed(() => { - return actionButtonArea.show && ( - submitButton.show || resetButton.show || slots.actionButtonPrepend || slots.actionButtonAppend - ); - }); - - return { - showActionButtonArea, - - actionButtonArea, - submitButton, - resetButton, - }; -} diff --git a/packages/snippets/src/acro-dynamic-form/src/index.vue b/packages/snippets/src/acro-dynamic-form/src/index.vue deleted file mode 100644 index efa55f14..00000000 --- a/packages/snippets/src/acro-dynamic-form/src/index.vue +++ /dev/null @@ -1,106 +0,0 @@ - - - diff --git a/packages/snippets/src/acro-dynamic-form/src/types.ts b/packages/snippets/src/acro-dynamic-form/src/types.ts deleted file mode 100644 index 20897396..00000000 --- a/packages/snippets/src/acro-dynamic-form/src/types.ts +++ /dev/null @@ -1,204 +0,0 @@ -import type { VNodeChild } from 'vue'; -import type { AutoCompleteInstance, ButtonInstance, CascaderInstance, CascaderPanelInstance, CheckboxGroupInstance, CheckboxInstance, ColorPickerInstance, DatePickerInstance, FieldRule, FormInstance, FormItemInstance, InputGroupInstance, InputInstance, InputNumberInstance, InputPasswordInstance, InputSearchInstance, InputTagInstance, MentionInstance, MonthPickerInstance, QuarterPickerInstance, RadioGroupInstance, RadioInstance, RangePickerInstance, RateInstance, SelectInstance, SliderInstance, SpaceInstance, SwitchInstance, TextareaInstance, TimePickerInstance, TransferInstance, TreeSelectInstance, UploadInstance, VerificationCodeInstance, WeekPickerInstance, YearPickerInstance } from '@arco-design/web-vue'; - -interface AcroDynamicFormProps extends /* @vue-ignore */ Omit { - /** 字段配置列表 */ - fields?: AcroDynamicFormField[]; - /** 表单数据 */ - model?: Record; - - /** - * 操作按钮区域配置 - * - 传入 boolean 值时, 表示是否显示操作按钮区域 - * @default true - */ - actionButtonArea?: ActionButtonAreaOptions | boolean; - /** - * 提交按钮配置 - * - 传入 boolean 值时, 表示是否显示提交按钮 - * @default true - */ - submitButton?: SubmitButtonOptions | boolean; - /** - * 重置按钮配置 - * - 传入 boolean 值时, 表示是否显示重置按钮 - * @default true - */ - resetButton?: ResetButtonOptions | boolean; -} - -interface AcroDynamicFormSlots { - /** 操作按钮区域插槽, 可使用该插槽代替操作按钮区域的渲染 */ - actionButtonArea?: () => void; - /** 操作按钮前置插槽, 可插入内容到提交按钮前面 */ - actionButtonPrepend?: () => void; - /** 操作按钮后置插槽, 可插入内容到重置按钮后面 */ - actionButtonAppend?: () => void; - /** 其他插槽 */ - [key: string]: ((options: RenderOptions) => void) | undefined; -} - -// #region ActionButtonAreaOptions -/** 操作按钮区域配置 */ -interface ActionButtonAreaOptions { - /** - * 是否显示操作按钮区域 - * @default true - */ - show?: boolean; - /** 传递给操作按钮区域 FormItem 组件的参数 */ - props?: FormItemInstance['$props']; - /** 传递给操作按钮区域 Space 组件的参数 */ - spaceProps?: SpaceInstance['$props']; -} -// #endregion ActionButtonAreaOptions - -// #region SubmitButtonOptions -/** 提交按钮配置 */ -interface SubmitButtonOptions { - /** - * 是否显示提交按钮 - * @default true - */ - show?: boolean; - /** - * 提交按钮文字 - * @default '提交' - */ - text?: string; - /** 传递给提交按钮组件的参数 */ - props?: ButtonInstance['$props']; -} -// #endregion SubmitButtonOptions - -// #region ResetButtonOptions -/** 重置按钮配置 */ -interface ResetButtonOptions { - /** - * 是否显示重置按钮 - * @default true - */ - show?: boolean; - /** - * 重置按钮文字 - * @default '重置' - */ - text?: string; - /** 传递给重置按钮组件的参数 */ - props?: ButtonInstance['$props']; -} -// #endregion ResetButtonOptions - -/** 组件渲染函数选项 */ -interface RenderOptions { - /** 表单数据 */ - model: Record; - /** 原组件渲染函数 */ - Component: () => VNodeChild; -} - -// #region AcroDynamicFormField -/** 字段配置 */ -type AcroDynamicFormField = AcroDynamicFormComponentField | AcroDynamicFormFieldBase; - -/** 字段通用配置 */ -interface AcroDynamicFormFieldBase { - /** 字段名 */ - field: string; - /** 标签 */ - label?: string; - /** 默认值 */ - defaultValue?: any; - /** 校验规则 */ - rules?: FieldRule | FieldRule[]; - /** - * 触发校验的事件 - * @default ['change', 'blur'] - */ - validateTrigger?: FormItemInstance['validateTrigger']; - /** 传递给 FormItem 组件的参数 */ - formItemProps?: Omit; - /** 传递给 FormItem 组件的插槽 */ - formItemSlots?: { - /** 标签 */ - label?: () => VNodeChild; - /** 帮助信息 */ - help?: () => VNodeChild; - /** 额外内容 */ - extra?: () => VNodeChild; - /** 其他插槽 ( 预留 ) */ - [key: string]: ((...args: any[]) => VNodeChild) | undefined; - }; - /** 字段类型 */ - type?: string; - /** 传递给组件的参数 */ - componentProps?: Record; - /** 传递给组件的插槽 */ - componentSlots?: Record VNodeChild>; - /** 组件渲染函数或插槽名称 */ - render?: ((options: RenderOptions) => VNodeChild) | string; - /** 预设 */ - preset?: symbol | symbol[]; -} - -/** 组件字段配置 */ -type AcroDynamicFormComponentField = { - [T in AcroDynamicFormFieldType]: Omit & { - /** 字段类型 */ - type: T; - /** 传递给组件的参数 */ - componentProps?: Omit; - }; -}[AcroDynamicFormFieldType]; - -/** 字段类型 */ -type AcroDynamicFormFieldType = keyof AcroDynamicFormFieldComponentPropsMap; - -/** 字段配置组件参数映射 */ -interface AcroDynamicFormFieldComponentPropsMap { - 'input': InputInstance['$props']; - 'input-group': InputGroupInstance['$props']; - 'input-number': InputNumberInstance['$props']; - 'input-password': InputPasswordInstance['$props']; - 'input-search': InputSearchInstance['$props']; - 'input-tag': InputTagInstance['$props']; - 'textarea': TextareaInstance['$props']; - 'select': SelectInstance['$props']; - 'cascader': CascaderInstance['$props']; - 'cascader-panel': CascaderPanelInstance['$props']; - 'tree-select': TreeSelectInstance['$props']; - 'date-picker': DatePickerInstance['$props']; - 'time-picker': TimePickerInstance['$props']; - 'year-picker': YearPickerInstance['$props']; - 'month-picker': MonthPickerInstance['$props']; - 'quarter-picker': QuarterPickerInstance['$props']; - 'week-picker': WeekPickerInstance['$props']; - 'range-picker': RangePickerInstance['$props']; - 'checkbox': CheckboxInstance['$props']; - 'checkbox-group': CheckboxGroupInstance['$props']; - 'radio': RadioInstance['$props']; - 'radio-group': RadioGroupInstance['$props']; - 'switch': SwitchInstance['$props']; - 'upload': UploadInstance['$props']; - 'transfer': TransferInstance['$props']; - 'slider': SliderInstance['$props']; - 'rate': RateInstance['$props']; - 'auto-complete': AutoCompleteInstance['$props']; - 'mention': MentionInstance['$props']; - 'verification-code': VerificationCodeInstance['$props']; - 'color-picker': ColorPickerInstance['$props']; -} -// #endregion AcroDynamicFormField - -export { - AcroDynamicFormProps, - - ActionButtonAreaOptions, - SubmitButtonOptions, - ResetButtonOptions, - - AcroDynamicFormSlots, - - AcroDynamicFormField, - AcroDynamicFormComponentField, -}; diff --git a/packages/snippets/src/acro-dynamic-form/src/utils/defineAcroDynamicFormFields.ts b/packages/snippets/src/acro-dynamic-form/src/utils/defineAcroDynamicFormFields.ts deleted file mode 100644 index 27b09842..00000000 --- a/packages/snippets/src/acro-dynamic-form/src/utils/defineAcroDynamicFormFields.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { AcroDynamicFormField } from '../types'; - -/** - * 定义 AcroDynamicForm 单个字段配置 - * @see https://mixte.moomfe.com/mixte/snippets/acro-dynamic-form - * @param field 字段配置 - * @example - * import { defineAcroDynamicFormField } from '@mixte/snippets/acro-dynamic-form'; - * - * const field = defineAcroDynamicFormField({ - * field: 'name', - * label: '姓名', - * defaultValue: '张三', - * // ... - * }); - */ -export function defineAcroDynamicFormField(field: AcroDynamicFormField) { - return field; -} - -/** - * 定义 AcroDynamicForm 字段配置列表 - * @see https://mixte.moomfe.com/mixte/snippets/acro-dynamic-form - * @param fields 字段配置列表 - * @example - * import { defineAcroDynamicFormFields } from '@mixte/snippets/acro-dynamic-form'; - * - * const fields = defineAcroDynamicFormFields([ - * { - * field: 'name', - * label: '姓名', - * defaultValue: '张三', - * // ... - * }, - * { - * field: 'age', - * label: '年龄', - * defaultValue: 18, - * // ... - * }, - * // ... - * ]); - */ -export function defineAcroDynamicFormFields(fields: AcroDynamicFormField[]) { - return fields; -} diff --git a/packages/snippets/src/acro-dynamic-form/src/utils/defineAcroDynamicFormPreset.ts b/packages/snippets/src/acro-dynamic-form/src/utils/defineAcroDynamicFormPreset.ts deleted file mode 100644 index f24ba3be..00000000 --- a/packages/snippets/src/acro-dynamic-form/src/utils/defineAcroDynamicFormPreset.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { toArray } from 'mixte'; -import { mergeProps } from 'vue'; -import type { AcroDynamicFormField } from '../types'; - -type PresetAcroDynamicFormField = Omit; - -/** - * 预设映射缓存 - * @private - */ -export const presetMap = new WeakMap(); - -/** - * 定义一组 AcroDynamicForm 预设 - * @see https://mixte.moomfe.com/mixte/snippets/acro-dynamic-form - * @param presets 预设字段配置 - * @example - * import { defineAcroDynamicFormPreset } from '@mixte/snippets/acro-dynamic-form'; - * - * const base = defineAcroDynamicFormPreset({ - * input: { - * type: 'input', - * componentProps: { - * placeholder: '预设的提示文字', allowClear: true, showWordLimit: true, maxLength: 25, - * // ... - * }, - * // ... - * }, - * xxx: { - * // ... - * }, - * // ... - * }); - * - * const field = defineAcroDynamicFormFields([ - * { - * field: 'name', - * label: '姓名', - * preset: base.input, - * // ... - * }, - * // ... - * ]); - */ -export function defineAcroDynamicFormPreset

>(presets: P) { - const presetKeyMap = {} as Record; - - Object.entries(presets).forEach(([name, preset]) => { - const key = Symbol(name); - presetMap.set(key, preset); - presetKeyMap[name as keyof P] = key; - }); - - return presetKeyMap; -} - -/** - * 解析字段配置, 合并预设 - * @private - */ -export function resolveAcroDynamicFormFieldConfig(field: AcroDynamicFormField) { - const preset = toArray(field.preset); - - if (!preset.length) - return field; - - const configs = preset.map(key => presetMap.get(key)!) as AcroDynamicFormField[]; - - return mergeAcroDynamicFormFieldConfig( - configs.reduce( - (presetFieldConfig, field) => mergeAcroDynamicFormFieldConfig(presetFieldConfig, field), - {} as AcroDynamicFormField, - ), - field, - ); -} - -/** - * 合并字段配置 - */ -export function mergeAcroDynamicFormFieldConfig(presetFieldConfig: AcroDynamicFormField, field: AcroDynamicFormField): AcroDynamicFormField { - return { - ...presetFieldConfig, - ...field, - - field: field.field, - - formItemProps: mergeProps(presetFieldConfig.formItemProps ?? {}, field.formItemProps ?? {}), - formItemSlots: { - ...presetFieldConfig.formItemSlots, - ...field.formItemSlots, - }, - - componentProps: mergeProps(presetFieldConfig.componentProps ?? {}, field.componentProps ?? {}), - componentSlots: { - ...presetFieldConfig.componentSlots, - ...field.componentSlots, - }, - }; -}