diff --git a/.vscode/launch.json b/.vscode/launch.json index 7740e810..e9673304 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "url": "http://localhost:5555", "env": { "NODE_ENV": "development" }, "sourceMaps": true, - "webRoot": "${workspaceFolder}" + "webRoot": "${workspaceFolder}/playground" }, { "type": "chrome", @@ -18,7 +18,7 @@ "url": "http://localhost:5666", "env": { "NODE_ENV": "development" }, "sourceMaps": true, - "webRoot": "${workspaceFolder}" + "webRoot": "${workspaceFolder}/apps/web-antd" }, { "type": "chrome", @@ -27,7 +27,7 @@ "url": "http://localhost:5777", "env": { "NODE_ENV": "development" }, "sourceMaps": true, - "webRoot": "${workspaceFolder}" + "webRoot": "${workspaceFolder}/apps/web-ele" }, { "type": "chrome", @@ -36,7 +36,7 @@ "url": "http://localhost:5888", "env": { "NODE_ENV": "development" }, "sourceMaps": true, - "webRoot": "${workspaceFolder}" + "webRoot": "${workspaceFolder}/apps/web-naive" } ] } diff --git a/apps/backend-mock/api/table/list.ts b/apps/backend-mock/api/table/list.ts index 4a0db94e..55b88eaa 100644 --- a/apps/backend-mock/api/table/list.ts +++ b/apps/backend-mock/api/table/list.ts @@ -43,6 +43,31 @@ export default eventHandler(async (event) => { await sleep(600); - const { page, pageSize } = getQuery(event); - return usePageResponseSuccess(page as string, pageSize as string, mockData); + const { page, pageSize, sortBy, sortOrder } = getQuery(event); + const listData = structuredClone(mockData); + if (sortBy && Reflect.has(listData[0], sortBy as string)) { + listData.sort((a, b) => { + if (sortOrder === 'asc') { + if (sortBy === 'price') { + return ( + Number.parseFloat(a[sortBy as string]) - + Number.parseFloat(b[sortBy as string]) + ); + } else { + return a[sortBy as string] > b[sortBy as string] ? 1 : -1; + } + } else { + if (sortBy === 'price') { + return ( + Number.parseFloat(b[sortBy as string]) - + Number.parseFloat(a[sortBy as string]) + ); + } else { + return a[sortBy as string] < b[sortBy as string] ? 1 : -1; + } + } + }); + } + + return usePageResponseSuccess(page as string, pageSize as string, listData); }); diff --git a/apps/web-antd/src/views/_core/authentication/code-login.vue b/apps/web-antd/src/views/_core/authentication/code-login.vue index dfada340..fc8a2329 100644 --- a/apps/web-antd/src/views/_core/authentication/code-login.vue +++ b/apps/web-antd/src/views/_core/authentication/code-login.vue @@ -15,6 +15,7 @@ import { useAuthStore } from '#/store'; defineOptions({ name: 'CodeLogin' }); const loading = ref(false); +const CODE_LENGTH = 6; const tenantInfo = ref({ tenantEnabled: false, @@ -98,7 +99,9 @@ const formSchema = computed((): VbenFormSchema[] => { }, fieldName: 'code', label: $t('authentication.code'), - rules: z.string().min(1, { message: $t('authentication.codeTip') }), + rules: z.string().length(CODE_LENGTH, { + message: $t('authentication.codeTip', [CODE_LENGTH]), + }), }, ]; }); diff --git a/apps/web-ele/package.json b/apps/web-ele/package.json index 9828c52f..a02376ee 100644 --- a/apps/web-ele/package.json +++ b/apps/web-ele/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-ele", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/apps/web-ele/src/views/_core/authentication/code-login.vue b/apps/web-ele/src/views/_core/authentication/code-login.vue index 1fc15e14..b65f9623 100644 --- a/apps/web-ele/src/views/_core/authentication/code-login.vue +++ b/apps/web-ele/src/views/_core/authentication/code-login.vue @@ -10,6 +10,7 @@ import { $t } from '@vben/locales'; defineOptions({ name: 'CodeLogin' }); const loading = ref(false); +const CODE_LENGTH = 6; const formSchema = computed((): VbenFormSchema[] => { return [ @@ -30,6 +31,7 @@ const formSchema = computed((): VbenFormSchema[] => { { component: 'VbenPinInput', componentProps: { + codeLength: CODE_LENGTH, createText: (countdown: number) => { const text = countdown > 0 @@ -41,7 +43,9 @@ const formSchema = computed((): VbenFormSchema[] => { }, fieldName: 'code', label: $t('authentication.code'), - rules: z.string().min(1, { message: $t('authentication.codeTip') }), + rules: z.string().length(CODE_LENGTH, { + message: $t('authentication.codeTip', [CODE_LENGTH]), + }), }, ]; }); diff --git a/apps/web-ele/src/views/demos/form/basic.vue b/apps/web-ele/src/views/demos/form/basic.vue index 90aaccef..771665a6 100644 --- a/apps/web-ele/src/views/demos/form/basic.vue +++ b/apps/web-ele/src/views/demos/form/basic.vue @@ -139,6 +139,7 @@ const [Form, formApi] = useVbenForm({ fieldName: 'select', label: 'Select', componentProps: { + filterable: true, options: [ { value: 'A', label: '选项A' }, { value: 'B', label: '选项B' }, diff --git a/apps/web-naive/package.json b/apps/web-naive/package.json index 180b5692..5bd3b1a7 100644 --- a/apps/web-naive/package.json +++ b/apps/web-naive/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-naive", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/apps/web-naive/src/adapter/form.ts b/apps/web-naive/src/adapter/form.ts index 2c3cee87..2f2ed2ab 100644 --- a/apps/web-naive/src/adapter/form.ts +++ b/apps/web-naive/src/adapter/form.ts @@ -10,8 +10,6 @@ import { $t } from '@vben/locales'; setupVbenForm({ config: { - // naive-ui组件不接受onChang事件,所以需要禁用 - disabledOnChangeListener: true, // naive-ui组件的空值为null,不能是undefined,否则重置表单时不生效 emptyStateValue: null, baseModelPropName: 'value', diff --git a/apps/web-naive/src/views/_core/authentication/code-login.vue b/apps/web-naive/src/views/_core/authentication/code-login.vue index 1fc15e14..b65f9623 100644 --- a/apps/web-naive/src/views/_core/authentication/code-login.vue +++ b/apps/web-naive/src/views/_core/authentication/code-login.vue @@ -10,6 +10,7 @@ import { $t } from '@vben/locales'; defineOptions({ name: 'CodeLogin' }); const loading = ref(false); +const CODE_LENGTH = 6; const formSchema = computed((): VbenFormSchema[] => { return [ @@ -30,6 +31,7 @@ const formSchema = computed((): VbenFormSchema[] => { { component: 'VbenPinInput', componentProps: { + codeLength: CODE_LENGTH, createText: (countdown: number) => { const text = countdown > 0 @@ -41,7 +43,9 @@ const formSchema = computed((): VbenFormSchema[] => { }, fieldName: 'code', label: $t('authentication.code'), - rules: z.string().min(1, { message: $t('authentication.codeTip') }), + rules: z.string().length(CODE_LENGTH, { + message: $t('authentication.codeTip', [CODE_LENGTH]), + }), }, ]; }); diff --git a/docs/package.json b/docs/package.json index e1435068..2d56a8ba 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@vben/docs", - "version": "5.5.0", + "version": "5.5.1", "private": true, "scripts": { "build": "vitepress build", diff --git a/docs/src/_env/adapter/form.ts b/docs/src/_env/adapter/form.ts index 67e2483e..d8b51c25 100644 --- a/docs/src/_env/adapter/form.ts +++ b/docs/src/_env/adapter/form.ts @@ -14,8 +14,6 @@ initComponentAdapter(); setupVbenForm({ config: { baseModelPropName: 'value', - // naive-ui组件不接受onChang事件,所以需要禁用 - disabledOnChangeListener: true, // naive-ui组件的空值为null,不能是undefined,否则重置表单时不生效 emptyStateValue: null, modelPropNameMap: { diff --git a/docs/src/components/common-ui/vben-form.md b/docs/src/components/common-ui/vben-form.md index 618e3d3a..80636963 100644 --- a/docs/src/components/common-ui/vben-form.md +++ b/docs/src/components/common-ui/vben-form.md @@ -287,6 +287,8 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单 | setValues | 设置表单值, 默认会过滤不在schema中定义的field, 可通过filterFields形参关闭过滤 | `(fields: Record, filterFields?: boolean, shouldValidate?: boolean) => Promise` | | getValues | 获取表单值 | `(fields:Record,shouldValidate: boolean = false)=>Promise` | | validate | 表单校验 | `()=>Promise` | +| validateField | 校验指定字段 | `(fieldName: string)=>Promise>` | +| isFieldValid | 检查某个字段是否已通过校验 | `(fieldName: string)=>Promise` | | resetValidate | 重置表单校验 | `()=>Promise` | | updateSchema | 更新formSchema | `(schema:FormSchema[])=>void` | | setFieldValue | 设置字段值 | `(field: string, value: any, shouldValidate?: boolean)=>Promise` | @@ -311,14 +313,14 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单 | resetButtonOptions | 重置按钮组件参数 | `ActionButtonOptions` | - | | submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - | | showDefaultActions | 是否显示默认操作按钮 | `boolean` | `true` | -| collapsed | 是否折叠,在`是否展开,在showCollapseButton=true`时生效 | `boolean` | `false` | +| collapsed | 是否折叠,在`showCollapseButton`为`true`时生效 | `boolean` | `false` | | collapseTriggerResize | 折叠时,触发`resize`事件 | `boolean` | `false` | | collapsedRows | 折叠时保持的行数 | `number` | `1` | -| fieldMappingTime | 用于将表单内时间区域的应设成 2 个字段 | `[string, [string, string], string?][]` | - | +| fieldMappingTime | 用于将表单内时间区域组件的数组值映射成 2 个字段 | `[string, [string, string], string?][]` | - | | commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - | -| schema | 表单项的每一项配置 | `FormSchema` | - | +| schema | 表单项的每一项配置 | `FormSchema[]` | - | | submitOnEnter | 按下回车健时提交表单 | `boolean` | false | -| submitOnChange | 字段值改变时提交表单 | `boolean` | false | +| submitOnChange | 字段值改变时提交表单(内部防抖,这个属性一般用于表格的搜索表单) | `boolean` | false | ### TS 类型说明 @@ -359,6 +361,10 @@ export interface FormCommonConfig { * 所有表单项的控件样式 */ controlClass?: string; + /** + * 在表单项的Label后显示一个冒号 + */ + colon?: boolean; /** * 所有表单项的禁用状态 * @default false @@ -418,7 +424,7 @@ export interface FormSchema< dependencies?: FormItemDependencies; /** 描述 */ description?: string; - /** 字段名 */ + /** 字段名,也作为自定义插槽的名称 */ fieldName: string; /** 帮助信息 */ help?: string; @@ -441,7 +447,7 @@ export interface FormSchema< ```ts dependencies: { - // 只有当 name 字段的值变化时,才会触发联动 + // 触发字段。只有这些字段值变动时,联动才会触发 triggerFields: ['name'], // 动态判断当前字段是否需要显示,不显示则直接销毁 if(values,formApi){}, @@ -462,11 +468,11 @@ dependencies: { ### 表单校验 -表单联动需要通过 schema 内的 `rules` 属性进行配置。 +表单校验需要通过 schema 内的 `rules` 属性进行配置。 -rules的值可以是一个字符串,也可以是一个zod的schema。 +rules的值可以是字符串(预定义的校验规则名称),也可以是一个zod的schema。 -#### 字符串 +#### 预定义的校验规则 ```ts // 表示字段必填,默认会根据适配器的required进行国际化 @@ -492,11 +498,16 @@ import { z } from '#/adapter/form'; rules: z.string().min(1, { message: '请输入字符串' }); } -// 可选,并且携带默认值 +// 可选(可以是undefined),并且携带默认值。注意zod的optional不包括空字符串'' { rules: z.string().default('默认值').optional(), } +// 可以是空字符串、undefined或者一个邮箱地址 +{ + rules: z.union(z.string().email().optional(), z.literal("")) +} + // 复杂校验 { z.string().min(1, { message: "请输入" }) diff --git a/docs/src/components/common-ui/vben-vxe-table.md b/docs/src/components/common-ui/vben-vxe-table.md index 29f679f6..24d911da 100644 --- a/docs/src/components/common-ui/vben-vxe-table.md +++ b/docs/src/components/common-ui/vben-vxe-table.md @@ -165,6 +165,8 @@ vxeUI.renderer.add('CellLink', { **表单搜索** 部分采用了`Vben Form 表单`,参考 [Vben Form 表单文档](/components/common-ui/vben-form)。 +当启用了表单搜索时,可以在toolbarConfig中配置`search`为`true`来让表格在工具栏区域显示一个搜索表单控制按钮。 + ## 单元格编辑 @@ -215,14 +217,15 @@ const [Grid, gridApi] = useVbenVxeGrid({ useVbenVxeGrid 返回的第二个参数,是一个对象,包含了一些表单的方法。 -| 方法名 | 描述 | 类型 | -| --- | --- | --- | -| setLoading | 设置loading状态 | `(loading)=>void` | -| setGridOptions | 设置vxe-table grid组件参数 | `(options: Partialvoid` | -| reload | 重载表格,会进行初始化 | `(params:any)=>void` | -| query | 重载表格,会保留当前分页 | `(params:any)=>void` | -| grid | vxe-table grid实例 | `VxeGridInstance` | -| formApi | vbenForm api实例 | `FormApi` | +| 方法名 | 描述 | 类型 | 说明 | +| --- | --- | --- | --- | +| setLoading | 设置loading状态 | `(loading)=>void` | - | +| setGridOptions | 设置vxe-table grid组件参数 | `(options: Partialvoid` | - | +| reload | 重载表格,会进行初始化 | `(params:any)=>void` | - | +| query | 重载表格,会保留当前分页 | `(params:any)=>void` | - | +| grid | vxe-table grid实例 | `VxeGridInstance` | - | +| formApi | vbenForm api实例 | `FormApi` | - | +| toggleSearchForm | 设置搜索表单显示状态 | `(show?: boolean)=>boolean` | 当省略参数时,则将表单在显示和隐藏两种状态之间切换 | ## Props @@ -236,3 +239,4 @@ useVbenVxeGrid 返回的第二个参数,是一个对象,包含了一些表 | gridOptions | grid组件的参数 | `VxeTableGridProps` | | gridEvents | grid组件的触发的⌚️ | `VxeGridListeners` | | formOptions | 表单参数 | `VbenFormProps` | +| showSearchForm | 是否显示搜索表单 | `boolean` | diff --git a/docs/src/demos/vben-vxe-table/form/index.vue b/docs/src/demos/vben-vxe-table/form/index.vue index b5be6c65..bcf3f5a5 100644 --- a/docs/src/demos/vben-vxe-table/form/index.vue +++ b/docs/src/demos/vben-vxe-table/form/index.vue @@ -110,6 +110,11 @@ const gridOptions: VxeGridProps = { }, }, }, + toolbarConfig: { + // 是否显示搜索表单控制按钮 + // @ts-ignore 正式环境时有完整的类型声明 + search: true, + }, }; const [Grid] = useVbenVxeGrid({ formOptions, gridOptions }); diff --git a/docs/src/en/guide/essentials/settings.md b/docs/src/en/guide/essentials/settings.md index 68fef3e7..a3cd579e 100644 --- a/docs/src/en/guide/essentials/settings.md +++ b/docs/src/en/guide/essentials/settings.md @@ -217,6 +217,7 @@ const defaultPreferences: Preferences = { globalSearch: true, }, sidebar: { + autoActivateChild: false, collapsed: false, collapsedShowTitle: false, enable: true, diff --git a/docs/src/guide/essentials/settings.md b/docs/src/guide/essentials/settings.md index e3357206..3669a771 100644 --- a/docs/src/guide/essentials/settings.md +++ b/docs/src/guide/essentials/settings.md @@ -240,6 +240,7 @@ const defaultPreferences: Preferences = { globalSearch: true, }, sidebar: { + autoActivateChild: false, collapsed: false, collapsedShowTitle: false, enable: true, diff --git a/internal/lint-configs/commitlint-config/package.json b/internal/lint-configs/commitlint-config/package.json index fecabea5..01b43088 100644 --- a/internal/lint-configs/commitlint-config/package.json +++ b/internal/lint-configs/commitlint-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/commitlint-config", - "version": "5.5.0", + "version": "5.5.1", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", diff --git a/internal/lint-configs/stylelint-config/package.json b/internal/lint-configs/stylelint-config/package.json index 514cbf77..64815741 100644 --- a/internal/lint-configs/stylelint-config/package.json +++ b/internal/lint-configs/stylelint-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/stylelint-config", - "version": "5.5.0", + "version": "5.5.1", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", diff --git a/internal/node-utils/package.json b/internal/node-utils/package.json index 7bbf40d9..9412656a 100644 --- a/internal/node-utils/package.json +++ b/internal/node-utils/package.json @@ -1,6 +1,6 @@ { "name": "@vben/node-utils", - "version": "5.5.0", + "version": "5.5.1", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", diff --git a/internal/tailwind-config/package.json b/internal/tailwind-config/package.json index 946141dd..26da63b4 100644 --- a/internal/tailwind-config/package.json +++ b/internal/tailwind-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/tailwind-config", - "version": "5.5.0", + "version": "5.5.1", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", diff --git a/internal/tsconfig/package.json b/internal/tsconfig/package.json index e39ec49b..d935b671 100644 --- a/internal/tsconfig/package.json +++ b/internal/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@vben/tsconfig", - "version": "5.5.0", + "version": "5.5.1", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", diff --git a/internal/vite-config/package.json b/internal/vite-config/package.json index 5abdcf45..60955a1d 100644 --- a/internal/vite-config/package.json +++ b/internal/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/vite-config", - "version": "5.5.0", + "version": "5.5.1", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", diff --git a/package.json b/package.json index a5501e58..5e449593 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vben-admin-monorepo", - "version": "5.5.0", + "version": "5.5.1", "private": true, "keywords": [ "monorepo", diff --git a/packages/@core/base/design/package.json b/packages/@core/base/design/package.json index 9e564de2..7b46ddd7 100644 --- a/packages/@core/base/design/package.json +++ b/packages/@core/base/design/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/design", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/@core/base/icons/package.json b/packages/@core/base/icons/package.json index bb971c0c..754b7450 100644 --- a/packages/@core/base/icons/package.json +++ b/packages/@core/base/icons/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/icons", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/@core/base/shared/package.json b/packages/@core/base/shared/package.json index 17fa08c7..20eeea45 100644 --- a/packages/@core/base/shared/package.json +++ b/packages/@core/base/shared/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/shared", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/@core/base/typings/package.json b/packages/@core/base/typings/package.json index 42c5db8b..504e53ed 100644 --- a/packages/@core/base/typings/package.json +++ b/packages/@core/base/typings/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/typings", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/@core/composables/package.json b/packages/@core/composables/package.json index 9af4f1f1..736feefa 100644 --- a/packages/@core/composables/package.json +++ b/packages/@core/composables/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/composables", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap b/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap index def5ff60..f05a96dc 100644 --- a/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap +++ b/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap @@ -65,6 +65,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj "globalSearch": true, }, "sidebar": { + "autoActivateChild": false, "collapsed": false, "collapsedShowTitle": false, "enable": true, @@ -83,6 +84,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj "showMaximize": true, "showMore": true, "styleType": "chrome", + "wheelable": true, }, "theme": { "builtinType": "default", diff --git a/packages/@core/preferences/package.json b/packages/@core/preferences/package.json index 1cff0998..24b522e2 100644 --- a/packages/@core/preferences/package.json +++ b/packages/@core/preferences/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/preferences", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/@core/preferences/src/config.ts b/packages/@core/preferences/src/config.ts index 72b087e4..5974797f 100644 --- a/packages/@core/preferences/src/config.ts +++ b/packages/@core/preferences/src/config.ts @@ -65,6 +65,7 @@ const defaultPreferences: Preferences = { globalSearch: true, }, sidebar: { + autoActivateChild: false, collapsed: false, collapsedShowTitle: false, enable: true, @@ -83,6 +84,7 @@ const defaultPreferences: Preferences = { showMaximize: true, showMore: true, styleType: 'chrome', + wheelable: true, }, theme: { builtinType: 'default', diff --git a/packages/@core/preferences/src/types.ts b/packages/@core/preferences/src/types.ts index 2b536b85..59dce2e2 100644 --- a/packages/@core/preferences/src/types.ts +++ b/packages/@core/preferences/src/types.ts @@ -125,6 +125,8 @@ interface NavigationPreferences { } interface SidebarPreferences { + /** 点击目录时自动激活子菜单 */ + autoActivateChild: boolean; /** 侧边栏是否折叠 */ collapsed: boolean; /** 侧边栏折叠时,是否显示title */ @@ -173,6 +175,8 @@ interface TabbarPreferences { showMore: boolean; /** 标签页风格 */ styleType: TabsStyleType; + /** 是否开启鼠标滚轮响应 */ + wheelable: boolean; } interface ThemePreferences { diff --git a/packages/@core/ui-kit/form-ui/package.json b/packages/@core/ui-kit/form-ui/package.json index 3b5f86e5..944fd49e 100644 --- a/packages/@core/ui-kit/form-ui/package.json +++ b/packages/@core/ui-kit/form-ui/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/form-ui", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/@core/ui-kit/form-ui/src/config.ts b/packages/@core/ui-kit/form-ui/src/config.ts index 95f5f3ac..685646e4 100644 --- a/packages/@core/ui-kit/form-ui/src/config.ts +++ b/packages/@core/ui-kit/form-ui/src/config.ts @@ -46,11 +46,15 @@ export function setupVbenForm< >(options: VbenFormAdapterOptions) { const { config, defineRules } = options; - const { disabledOnChangeListener = false, emptyStateValue = undefined } = - (config || {}) as FormCommonConfig; + const { + disabledOnChangeListener = true, + disabledOnInputListener = true, + emptyStateValue = undefined, + } = (config || {}) as FormCommonConfig; Object.assign(DEFAULT_FORM_COMMON_CONFIG, { disabledOnChangeListener, + disabledOnInputListener, emptyStateValue, }); diff --git a/packages/@core/ui-kit/form-ui/src/form-api.ts b/packages/@core/ui-kit/form-ui/src/form-api.ts index 585afaff..8de5c405 100644 --- a/packages/@core/ui-kit/form-ui/src/form-api.ts +++ b/packages/@core/ui-kit/form-ui/src/form-api.ts @@ -130,6 +130,11 @@ export class FormApi { return form.values; } + async isFieldValid(fieldName: string) { + const form = await this.getForm(); + return form.isFieldValid(fieldName); + } + merge(formApi: FormApi) { const chain = [this, formApi]; const proxy = new Proxy(formApi, { @@ -348,4 +353,14 @@ export class FormApi { } return await this.submitForm(); } + + async validateField(fieldName: string, opts?: Partial) { + const form = await this.getForm(); + const validateResult = await form.validateField(fieldName, opts); + + if (Object.keys(validateResult?.errors ?? {}).length > 0) { + console.error('validate error', validateResult?.errors); + } + return validateResult; + } } diff --git a/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue b/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue index e9ee8f87..8b2f3923 100644 --- a/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue +++ b/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue @@ -26,6 +26,7 @@ import { isEventObjectLike } from './helper'; interface Props extends FormSchema {} const { + colon, commonComponentProps, component, componentProps, @@ -33,6 +34,7 @@ const { description, disabled, disabledOnChangeListener, + disabledOnInputListener, emptyStateValue, fieldName, formFieldProps, @@ -227,10 +229,13 @@ function fieldBindEvent(slotProps: Record) { return onChange?.(e?.target?.[bindEventField] ?? e); }, - onInput: () => {}, + ...(disabledOnInputListener ? { onInput: undefined } : {}), }; } - return {}; + return { + ...(disabledOnInputListener ? { onInput: undefined } : {}), + ...(disabledOnChangeListener ? { onChange: undefined } : {}), + }; } function createComponentProps(slotProps: Record) { @@ -296,7 +301,10 @@ function autofocus() { :required="shouldRequired && !hideRequiredMark" :style="labelStyle" > - {{ label }} +
diff --git a/packages/@core/ui-kit/form-ui/src/form-render/form.vue b/packages/@core/ui-kit/form-ui/src/form-render/form.vue index 348f03d8..7c04540d 100644 --- a/packages/@core/ui-kit/form-ui/src/form-render/form.vue +++ b/packages/@core/ui-kit/form-ui/src/form-render/form.vue @@ -86,10 +86,12 @@ const computedSchema = computed( formFieldProps: Record; } & Omit)[] => { const { + colon = false, componentProps = {}, controlClass = '', disabled, - disabledOnChangeListener = false, + disabledOnChangeListener = true, + disabledOnInputListener = true, emptyStateValue = undefined, formFieldProps = {}, formItemClass = '', @@ -109,8 +111,10 @@ const computedSchema = computed( : false; return { + colon, disabled, disabledOnChangeListener, + disabledOnInputListener, emptyStateValue, hideLabel, hideRequiredMark, diff --git a/packages/@core/ui-kit/form-ui/src/types.ts b/packages/@core/ui-kit/form-ui/src/types.ts index 2f8a7be7..999f5737 100644 --- a/packages/@core/ui-kit/form-ui/src/types.ts +++ b/packages/@core/ui-kit/form-ui/src/types.ts @@ -136,6 +136,10 @@ type ComponentProps = | MaybeComponentProps; export interface FormCommonConfig { + /** + * 在Label后显示一个冒号 + */ + colon?: boolean; /** * 所有表单项的props */ @@ -151,9 +155,14 @@ export interface FormCommonConfig { disabled?: boolean; /** * 是否禁用所有表单项的change事件监听 - * @default false + * @default true */ disabledOnChangeListener?: boolean; + /** + * 是否禁用所有表单项的input事件监听 + * @default true + */ + disabledOnInputListener?: boolean; /** * 所有表单项的空状态值,默认都是undefined,naive-ui的空状态值是null */ @@ -371,6 +380,7 @@ export interface VbenFormAdapterOptions< config?: { baseModelPropName?: string; disabledOnChangeListener?: boolean; + disabledOnInputListener?: boolean; emptyStateValue?: null | undefined; modelPropNameMap?: Partial>; }; diff --git a/packages/@core/ui-kit/form-ui/src/vben-use-form.vue b/packages/@core/ui-kit/form-ui/src/vben-use-form.vue index 855472d9..27a8351c 100644 --- a/packages/@core/ui-kit/form-ui/src/vben-use-form.vue +++ b/packages/@core/ui-kit/form-ui/src/vben-use-form.vue @@ -6,7 +6,9 @@ import type { ExtendedFormApi, VbenFormProps } from './types'; import { useForwardPriorityValues } from '@vben-core/composables'; // import { isFunction } from '@vben-core/shared/utils'; -import { toRaw, useTemplateRef, watch } from 'vue'; +import { nextTick, onMounted, useTemplateRef, watch } from 'vue'; + +import { cloneDeep } from '@vben-core/shared/utils'; import { useDebounceFn } from '@vueuse/core'; @@ -59,14 +61,16 @@ function handleKeyDownEnter(event: KeyboardEvent) { formActionsRef.value?.handleSubmit?.(); } -watch( - () => form.values, - useDebounceFn(() => { - forward.value.handleValuesChange?.(toRaw(form.values)); - state.value.submitOnChange && props.formApi?.submitForm(); - }, 300), - { deep: true }, -); +const handleValuesChangeDebounced = useDebounceFn((newVal) => { + forward.value.handleValuesChange?.(cloneDeep(newVal)); + state.value.submitOnChange && formActionsRef.value?.handleSubmit?.(); +}, 300); + +onMounted(async () => { + // 只在挂载后开始监听,form.values会有一个初始化的过程 + await nextTick(); + watch(() => form.values, handleValuesChangeDebounced, { deep: true }); +});