diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index 5b80005e..b73d6644 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-antd", - "version": "5.3.0", + "version": "5.3.1", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/apps/web-ele/package.json b/apps/web-ele/package.json index 4cc83eb1..4a1286c1 100644 --- a/apps/web-ele/package.json +++ b/apps/web-ele/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-ele", - "version": "5.3.0", + "version": "5.3.1", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/apps/web-naive/package.json b/apps/web-naive/package.json index 42a2face..b6ead495 100644 --- a/apps/web-naive/package.json +++ b/apps/web-naive/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-naive", - "version": "5.3.0", + "version": "5.3.1", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/docs/package.json b/docs/package.json index 347eafbd..2b9f1c2f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@vben/docs", - "version": "5.3.0", + "version": "5.3.1", "private": true, "scripts": { "build": "vitepress build", diff --git a/internal/lint-configs/commitlint-config/package.json b/internal/lint-configs/commitlint-config/package.json index a96c6fd9..f2dd4715 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.3.0", + "version": "5.3.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/eslint-config/src/custom-config.ts b/internal/lint-configs/eslint-config/src/custom-config.ts index 3118044d..24b31dff 100644 --- a/internal/lint-configs/eslint-config/src/custom-config.ts +++ b/internal/lint-configs/eslint-config/src/custom-config.ts @@ -15,10 +15,17 @@ const customConfig: Linter.Config[] = [ }, }, { - files: ['packages/effects/**/**', 'packages/types/**/**'], + files: [ + 'apps/**/**', + 'packages/effects/**/**', + 'packages/utils/**/**', + 'packages/types/**/**', + 'packages/locales/**/**', + ], ignores: restrictedImportIgnores, rules: { 'perfectionist/sort-interfaces': 'off', + 'perfectionist/sort-objects': 'off', }, }, { diff --git a/internal/lint-configs/stylelint-config/package.json b/internal/lint-configs/stylelint-config/package.json index 9b3ec2e8..4f4c3d19 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.3.0", + "version": "5.3.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 4428dbc9..680835ec 100644 --- a/internal/node-utils/package.json +++ b/internal/node-utils/package.json @@ -1,6 +1,6 @@ { "name": "@vben/node-utils", - "version": "5.3.0", + "version": "5.3.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 27742dfb..e40645ed 100644 --- a/internal/tailwind-config/package.json +++ b/internal/tailwind-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/tailwind-config", - "version": "5.3.0", + "version": "5.3.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 57b871c0..e7bcef34 100644 --- a/internal/tsconfig/package.json +++ b/internal/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@vben/tsconfig", - "version": "5.3.0", + "version": "5.3.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 0b661a42..52cda1a2 100644 --- a/internal/vite-config/package.json +++ b/internal/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/vite-config", - "version": "5.3.0", + "version": "5.3.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 10200bf1..cef174e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vben-admin-pro", - "version": "5.3.0", + "version": "5.3.1", "private": true, "keywords": [ "monorepo", diff --git a/packages/@core/base/design/package.json b/packages/@core/base/design/package.json index ea38b20d..3b7eaa3b 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.3.0", + "version": "5.3.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 be0c2665..ba2f2f9d 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.3.0", + "version": "5.3.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 fbf77125..57d593fc 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.3.0", + "version": "5.3.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/src/utils/__tests__/util.test.ts b/packages/@core/base/shared/src/utils/__tests__/util.test.ts index 992145ca..0d87b318 100644 --- a/packages/@core/base/shared/src/utils/__tests__/util.test.ts +++ b/packages/@core/base/shared/src/utils/__tests__/util.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { bindMethods } from '../util'; +import { bindMethods, getNestedValue } from '../util'; class TestClass { public value: string; @@ -78,3 +78,79 @@ describe('bindMethods', () => { expect(value).toBe('test'); }); }); + +describe('getNestedValue', () => { + interface UserProfile { + age: number; + name: string; + } + + interface UserSettings { + theme: string; + } + + interface Data { + user: { + profile: UserProfile; + settings: UserSettings; + }; + } + + const data: Data = { + user: { + profile: { + age: 25, + name: 'Alice', + }, + settings: { + theme: 'dark', + }, + }, + }; + + it('should get a nested value when the path is valid', () => { + const result = getNestedValue(data, 'user.profile.name'); + expect(result).toBe('Alice'); + }); + + it('should return undefined for non-existent property', () => { + const result = getNestedValue(data, 'user.profile.gender'); + expect(result).toBeUndefined(); + }); + + it('should return undefined when accessing a non-existent deep path', () => { + const result = getNestedValue(data, 'user.nonexistent.field'); + expect(result).toBeUndefined(); + }); + + it('should return undefined if a middle level is undefined', () => { + const result = getNestedValue({ user: undefined }, 'user.profile.name'); + expect(result).toBeUndefined(); + }); + + it('should return the correct value for a nested setting', () => { + const result = getNestedValue(data, 'user.settings.theme'); + expect(result).toBe('dark'); + }); + + it('should work for a single-level path', () => { + const result = getNestedValue({ a: 1, b: 2 }, 'b'); + expect(result).toBe(2); + }); + + it('should return the entire object if path is empty', () => { + expect(() => getNestedValue(data, '')()).toThrow(); + }); + + it('should handle paths with array indexes', () => { + const complexData = { list: [{ name: 'Item1' }, { name: 'Item2' }] }; + const result = getNestedValue(complexData, 'list.1.name'); + expect(result).toBe('Item2'); + }); + + it('should return undefined when accessing an out-of-bounds array index', () => { + const complexData = { list: [{ name: 'Item1' }] }; + const result = getNestedValue(complexData, 'list.2.name'); + expect(result).toBeUndefined(); + }); +}); diff --git a/packages/@core/base/shared/src/utils/merge.ts b/packages/@core/base/shared/src/utils/merge.ts index c63733ab..4bf79eb5 100644 --- a/packages/@core/base/shared/src/utils/merge.ts +++ b/packages/@core/base/shared/src/utils/merge.ts @@ -1 +1,10 @@ +import { createDefu } from 'defu'; + export { createDefu as createMerge, defu as merge } from 'defu'; + +export const mergeWithArrayOverride = createDefu((originObj, key, updates) => { + if (Array.isArray(originObj[key]) && Array.isArray(updates)) { + originObj[key] = updates; + return true; + } +}); diff --git a/packages/@core/base/shared/src/utils/util.ts b/packages/@core/base/shared/src/utils/util.ts index 7f9a620c..885eeaa6 100644 --- a/packages/@core/base/shared/src/utils/util.ts +++ b/packages/@core/base/shared/src/utils/util.ts @@ -17,3 +17,28 @@ export function bindMethods(instance: T): void { } }); } + +/** + * 获取嵌套对象的字段值 + * @param obj - 要查找的对象 + * @param path - 用于查找字段的路径,使用小数点分隔 + * @returns 字段值,或者未找到时返回 undefined + */ +export function getNestedValue(obj: T, path: string): any { + if (typeof path !== 'string' || path.length === 0) { + throw new Error('Path must be a non-empty string'); + } + // 把路径字符串按 "." 分割成数组 + const keys = path.split('.') as (number | string)[]; + + let current: any = obj; + + for (const key of keys) { + if (current === null || current === undefined) { + return undefined; + } + current = current[key as keyof typeof current]; + } + + return current; +} diff --git a/packages/@core/base/typings/package.json b/packages/@core/base/typings/package.json index 609ec958..3c39e5ad 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.3.0", + "version": "5.3.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 2d2eac9d..450c4660 100644 --- a/packages/@core/composables/package.json +++ b/packages/@core/composables/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/composables", - "version": "5.3.0", + "version": "5.3.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/src/use-scroll-lock.ts b/packages/@core/composables/src/use-scroll-lock.ts index 3703d222..d1c14975 100644 --- a/packages/@core/composables/src/use-scroll-lock.ts +++ b/packages/@core/composables/src/use-scroll-lock.ts @@ -2,8 +2,8 @@ import { getScrollbarWidth, needsScrollbar } from '@vben-core/shared/utils'; import { useScrollLock as _useScrollLock, - tryOnBeforeMount, tryOnBeforeUnmount, + tryOnMounted, } from '@vueuse/core'; export const SCROLL_FIXED_CLASS = `_scroll__fixed_`; @@ -12,7 +12,7 @@ export function useScrollLock() { const isLocked = _useScrollLock(document.body); const scrollbarWidth = getScrollbarWidth(); - tryOnBeforeMount(() => { + tryOnMounted(() => { if (!needsScrollbar()) { return; } diff --git a/packages/@core/preferences/package.json b/packages/@core/preferences/package.json index d26e43b8..66b262e1 100644 --- a/packages/@core/preferences/package.json +++ b/packages/@core/preferences/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/preferences", - "version": "5.3.0", + "version": "5.3.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/__tests__/form-api.test.ts b/packages/@core/ui-kit/form-ui/__tests__/form-api.test.ts index d61627d9..69126420 100644 --- a/packages/@core/ui-kit/form-ui/__tests__/form-api.test.ts +++ b/packages/@core/ui-kit/form-ui/__tests__/form-api.test.ts @@ -1,24 +1,7 @@ -// 假设这个文件为 FormApi.ts import { beforeEach, describe, expect, it, vi } from 'vitest'; import { FormApi } from '../src/form-api'; -vi.mock('@vben-core/shared/utils', () => ({ - bindMethods: vi.fn(), - createMerge: vi.fn((mergeFn) => { - return (stateOrFn: any, prev: any) => { - mergeFn(prev, 'key', stateOrFn); - return { ...prev, ...stateOrFn }; - }; - }), - isFunction: (fn: any) => typeof fn === 'function', - StateHandler: vi.fn().mockImplementation(() => ({ - reset: vi.fn(), - setConditionTrue: vi.fn(), - waitForCondition: vi.fn().mockResolvedValue(true), - })), -})); - describe('formApi', () => { let formApi: FormApi; @@ -128,7 +111,6 @@ describe('formApi', () => { it('should unmount form and reset state', () => { formApi.unmounted(); expect(formApi.isMounted).toBe(false); - expect(formApi.stateHandler.reset).toHaveBeenCalled(); }); it('should validate form', async () => { 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 3b78e039..df8bdf63 100644 --- a/packages/@core/ui-kit/form-ui/src/form-api.ts +++ b/packages/@core/ui-kit/form-ui/src/form-api.ts @@ -12,20 +12,13 @@ import { toRaw } from 'vue'; import { Store } from '@vben-core/shared/store'; import { bindMethods, - createMerge, isFunction, + mergeWithArrayOverride, StateHandler, } from '@vben-core/shared/utils'; import { objectPick } from '@vueuse/core'; -const merge = createMerge((originObj, key, updates) => { - if (Array.isArray(originObj[key]) && Array.isArray(updates)) { - originObj[key] = updates; - return true; - } -}); - function getDefaultState(): VbenFormProps { return { actionWrapperClass: '', @@ -218,10 +211,10 @@ export class FormApi { ) { if (isFunction(stateOrFn)) { this.store.setState((prev) => { - return merge(stateOrFn(prev), prev); + return mergeWithArrayOverride(stateOrFn(prev), prev); }); } else { - this.store.setState((prev) => merge(stateOrFn, prev)); + this.store.setState((prev) => mergeWithArrayOverride(stateOrFn, prev)); } } @@ -287,7 +280,10 @@ export class FormApi { currentSchema.forEach((schema, index) => { const updatedData = updatedMap[schema.fieldName]; if (updatedData) { - currentSchema[index] = merge(updatedData, schema) as FormSchema; + currentSchema[index] = mergeWithArrayOverride( + updatedData, + schema, + ) as FormSchema; } }); this.setState({ schema: currentSchema }); diff --git a/packages/@core/ui-kit/layout-ui/package.json b/packages/@core/ui-kit/layout-ui/package.json index 689a8461..239717a6 100644 --- a/packages/@core/ui-kit/layout-ui/package.json +++ b/packages/@core/ui-kit/layout-ui/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/layout-ui", - "version": "5.3.0", + "version": "5.3.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/menu-ui/package.json b/packages/@core/ui-kit/menu-ui/package.json index 58f834ce..78d77ce4 100644 --- a/packages/@core/ui-kit/menu-ui/package.json +++ b/packages/@core/ui-kit/menu-ui/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/menu-ui", - "version": "5.3.0", + "version": "5.3.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/popup-ui/src/drawer/drawer.vue b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue index c15cdb9d..4f80f9e5 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue @@ -1,7 +1,7 @@ diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/ui/sheet/SheetOverlay.vue b/packages/@core/ui-kit/shadcn-ui/src/components/ui/sheet/SheetOverlay.vue index 1be8b011..0a153c7a 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/ui/sheet/SheetOverlay.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/ui/sheet/SheetOverlay.vue @@ -1,11 +1,14 @@ diff --git a/packages/@core/ui-kit/tabs-ui/package.json b/packages/@core/ui-kit/tabs-ui/package.json index fb04d2cd..a51af8a3 100644 --- a/packages/@core/ui-kit/tabs-ui/package.json +++ b/packages/@core/ui-kit/tabs-ui/package.json @@ -1,6 +1,6 @@ { "name": "@vben-core/tabs-ui", - "version": "5.3.0", + "version": "5.3.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/constants/package.json b/packages/constants/package.json index 7e8840dc..85225203 100644 --- a/packages/constants/package.json +++ b/packages/constants/package.json @@ -1,6 +1,6 @@ { "name": "@vben/constants", - "version": "5.3.0", + "version": "5.3.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/effects/access/package.json b/packages/effects/access/package.json index 235e16c4..22f1113f 100644 --- a/packages/effects/access/package.json +++ b/packages/effects/access/package.json @@ -1,6 +1,6 @@ { "name": "@vben/access", - "version": "5.3.0", + "version": "5.3.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/effects/common-ui/package.json b/packages/effects/common-ui/package.json index c2150da2..9959a345 100644 --- a/packages/effects/common-ui/package.json +++ b/packages/effects/common-ui/package.json @@ -1,6 +1,6 @@ { "name": "@vben/common-ui", - "version": "5.3.0", + "version": "5.3.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/effects/hooks/package.json b/packages/effects/hooks/package.json index cd5d9d4d..f0f7cf05 100644 --- a/packages/effects/hooks/package.json +++ b/packages/effects/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@vben/hooks", - "version": "5.3.0", + "version": "5.3.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/packages/effects/layouts/package.json b/packages/effects/layouts/package.json index 1f052604..d301ce17 100644 --- a/packages/effects/layouts/package.json +++ b/packages/effects/layouts/package.json @@ -1,6 +1,6 @@ { "name": "@vben/layouts", - "version": "5.3.0", + "version": "5.3.1", "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { @@ -20,6 +20,7 @@ } }, "dependencies": { + "@vben-core/composables": "workspace:*", "@vben-core/form-ui": "workspace:*", "@vben-core/layout-ui": "workspace:*", "@vben-core/menu-ui": "workspace:*", diff --git a/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue b/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue index 2b7e2e9d..8153de7c 100644 --- a/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue +++ b/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue @@ -4,6 +4,7 @@ import { computed, reactive, ref } from 'vue'; import { LockKeyhole } from '@vben/icons'; import { $t, useI18n } from '@vben/locales'; import { storeToRefs, useLockStore } from '@vben/stores'; +import { useScrollLock } from '@vben-core/composables'; import { useVbenForm, z } from '@vben-core/form-ui'; import { VbenAvatar, VbenButton } from '@vben-core/shadcn-ui'; @@ -74,6 +75,8 @@ async function handleSubmit() { function toggleUnlockForm() { showUnlockForm.value = !showUnlockForm.value; } + +useScrollLock();