diff --git a/apps/backend-mock/README.md b/apps/backend-mock/README.md index 8b194f9b..8cf085a8 100644 --- a/apps/backend-mock/README.md +++ b/apps/backend-mock/README.md @@ -2,7 +2,7 @@ ## Description -Vben Admin Pro 数据mock服务 +Vben Admin Pro 数据 mock 服务,没有对接任何的数据库,所有数据都是模拟的,用于前端开发时提供数据支持。由于 sqlite 安装需要在本地进行编译,所以这里接口是直接返回的。线上环境不再提供mock集成,可自行部署服务或者对接真实数据,同步 mock.js等工具有一些限制,比如上传文件不行、无法模拟复杂的逻辑等,所以这里使用了 真是的后端服务来实现。唯一麻烦的是本地需要同时启动后端服务和前端服务,但是这样可以更好的模拟真实环境。 ## Running the app diff --git a/apps/web-antd/index.html b/apps/web-antd/index.html index fd3a08a5..aa35c9b8 100644 --- a/apps/web-antd/index.html +++ b/apps/web-antd/index.html @@ -11,7 +11,7 @@ name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" /> - + <%= VITE_GLOB_APP_TITLE %> diff --git a/apps/web-antd/src/forward/access.ts b/apps/web-antd/src/forward/access.ts index 03cbf659..8339a895 100644 --- a/apps/web-antd/src/forward/access.ts +++ b/apps/web-antd/src/forward/access.ts @@ -10,7 +10,8 @@ import { getAllMenus } from '#/apis'; import { BasicLayout, IFrameView } from '#/layouts'; import { $t } from '#/locales'; -const forbiddenPage = () => import('#/views/_essential/fallback/forbidden.vue'); +const forbiddenComponent = () => + import('#/views/_essential/fallback/forbidden.vue'); async function generateAccess(options: GeneratorMenuAndRoutesOptions) { const pageMap: ComponentRecordType = import.meta.glob('../views/**/*.vue'); @@ -30,7 +31,7 @@ async function generateAccess(options: GeneratorMenuAndRoutesOptions) { return await getAllMenus(); }, // 可以指定没有权限跳转403页面 - forbiddenComponent: forbiddenPage, + forbiddenComponent, // 如果 route.meta.menuVisibleWithForbidden = true layoutMap, pageMap, diff --git a/apps/web-antd/src/locales/index.ts b/apps/web-antd/src/locales/index.ts index 2e26a220..aa1dc256 100644 --- a/apps/web-antd/src/locales/index.ts +++ b/apps/web-antd/src/locales/index.ts @@ -10,7 +10,7 @@ import dayjs from 'dayjs'; const antdLocale = ref(defaultLocale); -const modules = import.meta.glob('./langs/*.y(a)?ml'); +const modules = import.meta.glob('./langs/*.json'); const localesMap = loadLocalesMap(modules); diff --git a/apps/web-antd/src/locales/langs/en-US.json b/apps/web-antd/src/locales/langs/en-US.json new file mode 100644 index 00000000..9e154deb --- /dev/null +++ b/apps/web-antd/src/locales/langs/en-US.json @@ -0,0 +1,34 @@ +{ + "page": { + "demos": { + "title": "Demos", + "access": { + "title": "Access Control", + "frontend-control": "Front-end Control", + "backend-control": "Backend Control", + "page": "Page visit", + "button": "Button control", + "loading-menu": "In the loading menu", + "access-test-1": "Super visit", + "access-test-2": "Admin visit", + "access-test-3": "User visit" + }, + "nested": { + "title": "Nested Menu", + "menu1": "Menu 1", + "menu2": "Menu 2", + "menu21": "Menu 2-1", + "menu3": "Menu 3", + "menu31": "Menu 3-1", + "menu32": "Menu 3-2", + "menu321": "Menu 3-2-1" + }, + "outside": { + "title": "External Page", + "embedded": "embedded Page", + "external-link": "External Link" + }, + "fallback": { "title": "Fallback Page" } + } + } +} diff --git a/apps/web-antd/src/locales/langs/en-US.yaml b/apps/web-antd/src/locales/langs/en-US.yaml deleted file mode 100644 index 4b59e7f1..00000000 --- a/apps/web-antd/src/locales/langs/en-US.yaml +++ /dev/null @@ -1,28 +0,0 @@ -page: - demos: - title: Demos - access: - title: Access Control - frontend-control: Front-end Control - backend-control: Backend Control - page: Page visit - button: Button control - loading-menu: In the loading menu - access-test-1: Super visit - access-test-2: Admin visit - access-test-3: User visit - nested: - title: Nested Menu - menu1: Menu 1 - menu2: Menu 2 - menu21: Menu 2-1 - menu3: Menu 3 - menu31: Menu 3-1 - menu32: Menu 3-2 - menu321: Menu 3-2-1 - outside: - title: External Page - embedded: embedded Page - external-link: External Link - fallback: - title: Fallback Page diff --git a/apps/web-antd/src/locales/langs/zh-CN.json b/apps/web-antd/src/locales/langs/zh-CN.json new file mode 100644 index 00000000..a3a15c0e --- /dev/null +++ b/apps/web-antd/src/locales/langs/zh-CN.json @@ -0,0 +1,35 @@ +{ + "page": { + "demos": { + "title": "演示", + "access": { + "title": "访问控制", + "frontend-control": "前端控制", + "backend-control": "后端控制", + "page": "页面访问", + "button": "按钮控制", + "access-test-1": "Super 可见", + "access-test-2": "Admin 可见", + "access-test-3": "User 可见" + }, + "nested": { + "title": "嵌套菜单", + "menu1": "菜单 1", + "menu2": "菜单 2", + "menu21": "菜单 2-1", + "menu3": "菜单 3", + "menu31": "菜单 3-1", + "menu32": "菜单 3-2", + "menu321": "菜单 3-2-1" + }, + "outside": { + "title": "外部页面", + "embedded": "内嵌", + "external-link": "外链" + }, + "fallback": { + "title": "缺省页" + } + } + } +} diff --git a/apps/web-antd/src/locales/langs/zh-CN.yaml b/apps/web-antd/src/locales/langs/zh-CN.yaml deleted file mode 100644 index d3be1f98..00000000 --- a/apps/web-antd/src/locales/langs/zh-CN.yaml +++ /dev/null @@ -1,27 +0,0 @@ -page: - demos: - title: 演示 - access: - title: 访问控制 - frontend-control: 前端控制 - backend-control: 后端控制 - page: 页面访问 - button: 按钮控制 - access-test-1: Super 可见 - access-test-2: Admin 可见 - access-test-3: User 可见 - nested: - title: 嵌套菜单 - menu1: 菜单 1 - menu2: 菜单 2 - menu21: 菜单 2-1 - menu3: 菜单 3 - menu31: 菜单 3-1 - menu32: 菜单 3-2 - menu321: 菜单 3-2-1 - outside: - title: 外部页面 - embedded: 内嵌 - external-link: 外链 - fallback: - title: 缺省页 diff --git a/apps/web-antd/src/preferences.ts b/apps/web-antd/src/preferences.ts index 2b667198..5b542173 100644 --- a/apps/web-antd/src/preferences.ts +++ b/apps/web-antd/src/preferences.ts @@ -1,8 +1,9 @@ -import type { DeepPartial } from '@vben/types'; -import type { Preferences } from '@vben-core/preferences'; +import { defineOverridesPreferences } from '@vben-core/preferences'; /** * @description 项目配置文件 * 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置 */ -export const overridesPreferences: DeepPartial = {}; +export const overridesPreferences = defineOverridesPreferences({ + // overrides +}); diff --git a/apps/web-antd/src/views/demos/access/backend/button-control.vue b/apps/web-antd/src/views/demos/access/backend/button-control.vue index 664b913e..ba8f7cd8 100644 --- a/apps/web-antd/src/views/demos/access/backend/button-control.vue +++ b/apps/web-antd/src/views/demos/access/backend/button-control.vue @@ -3,7 +3,7 @@ import type { LoginAndRegisterParams } from '@vben/universal-ui'; import { useRouter } from 'vue-router'; -import { CodeAuthority, useAccess } from '@vben/access'; +import { CodeAccess, useAccess } from '@vben/access'; import { Button } from 'ant-design-vue'; @@ -82,20 +82,20 @@ async function changeAccount(role: string) {
组件形式控制
- + - - + + - - + + - - + + - +
diff --git a/apps/web-antd/src/views/demos/access/frontend/button-control.vue b/apps/web-antd/src/views/demos/access/frontend/button-control.vue index 00179717..4d3d9605 100644 --- a/apps/web-antd/src/views/demos/access/frontend/button-control.vue +++ b/apps/web-antd/src/views/demos/access/frontend/button-control.vue @@ -3,7 +3,7 @@ import type { LoginAndRegisterParams } from '@vben/universal-ui'; import { useRouter } from 'vue-router'; -import { CodeAuthority, RoleAuthority, useAccess } from '@vben/access'; +import { CodeAccess, RoleAccess, useAccess } from '@vben/access'; import { Button } from 'ant-design-vue'; @@ -81,18 +81,18 @@ async function changeAccount(role: string) {
角色 - 组件形式控制
- + - - + + - - + + - - + + - +
@@ -113,20 +113,20 @@ async function changeAccount(role: string) {
权限码 - 组件形式控制
- + - - + + - - + + - - + + - +
diff --git a/internal/vite-config/src/plugins/index.ts b/internal/vite-config/src/plugins/index.ts index 8c80e796..3ee53795 100644 --- a/internal/vite-config/src/plugins/index.ts +++ b/internal/vite-config/src/plugins/index.ts @@ -7,10 +7,6 @@ import type { LibraryPluginOptions, } from '../typing'; -import { join } from 'node:path'; - -import { getPackages } from '@vben/node-utils'; - import viteVueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'; import viteVue from '@vitejs/plugin-vue'; import viteVueJsx from '@vitejs/plugin-vue-jsx'; @@ -117,28 +113,10 @@ async function loadApplicationPlugins( { condition: i18n, plugins: async () => { - const { packages } = await getPackages(); - - const include: string[] = []; - - // 加载所有应用的国际化文件 - for (const { dir, relativeDir } of packages) { - if ( - // 排除非应用目录 - !relativeDir.startsWith('apps') || - // 排除mock目录 - relativeDir.includes('backend-mock') - ) { - continue; - } - include.push(`${join(dir, 'src', 'locales', 'langs')}/*.yaml`); - } - return [ viteVueI18nPlugin({ compositionOnly: true, fullInstall: true, - include, runtimeOnly: true, }), ]; diff --git a/packages/@core/forward/helpers/src/flatten-object.test.ts b/packages/@core/forward/helpers/src/flatten-object.test.ts deleted file mode 100644 index f86c194d..00000000 --- a/packages/@core/forward/helpers/src/flatten-object.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { flattenObject } from './flatten-object'; - -describe('flattenObject', () => { - it('should flatten a nested object correctly', () => { - const nestedObject = { - language: 'en', - notifications: { - email: true, - push: { - sound: true, - vibration: false, - }, - }, - theme: 'light', - }; - - const expected = { - language: 'en', - notificationsEmail: true, - notificationsPushSound: true, - notificationsPushVibration: false, - theme: 'light', - }; - - const result = flattenObject(nestedObject); - expect(result).toEqual(expected); - }); - - it('should handle empty objects', () => { - const nestedObject = {}; - const expected = {}; - - const result = flattenObject(nestedObject); - expect(result).toEqual(expected); - }); - - it('should handle objects with primitive values', () => { - const nestedObject = { - active: true, - age: 30, - name: 'Alice', - }; - - const expected = { - active: true, - age: 30, - name: 'Alice', - }; - - const result = flattenObject(nestedObject); - expect(result).toEqual(expected); - }); - - it('should handle objects with null values', () => { - const nestedObject = { - user: { - age: null, - name: null, - }, - }; - - const expected = { - userAge: null, - userName: null, - }; - - const result = flattenObject(nestedObject); - expect(result).toEqual(expected); - }); - - it('should handle nested empty objects', () => { - const nestedObject = { - a: {}, - b: { c: {} }, - }; - - const expected = {}; - - const result = flattenObject(nestedObject); - expect(result).toEqual(expected); - }); - - it('should handle arrays within objects', () => { - const nestedObject = { - hobbies: ['reading', 'gaming'], - name: 'Alice', - }; - - const expected = { - hobbies: ['reading', 'gaming'], - name: 'Alice', - }; - - const result = flattenObject(nestedObject); - expect(result).toEqual(expected); - }); - it('should flatten objects with nested arrays correctly', () => { - const nestedObject = { - person: { - hobbies: ['reading', 'gaming'], - name: 'Alice', - }, - }; - - const expected = { - personHobbies: ['reading', 'gaming'], - personName: 'Alice', - }; - - const result = flattenObject(nestedObject); - expect(result).toEqual(expected); - }); - - it('should handle objects with undefined values', () => { - const nestedObject = { - user: { - age: undefined, - name: 'Alice', - }, - }; - - const expected = { - userAge: undefined, - userName: 'Alice', - }; - - const result = flattenObject(nestedObject); - expect(result).toEqual(expected); - }); -}); diff --git a/packages/@core/forward/helpers/src/flatten-object.ts b/packages/@core/forward/helpers/src/flatten-object.ts deleted file mode 100644 index c357cf2f..00000000 --- a/packages/@core/forward/helpers/src/flatten-object.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type { Flatten } from '@vben-core/typings'; - -import { capitalizeFirstLetter } from '@vben-core/toolkit'; - -/** - * 将嵌套对象扁平化 - * @param obj - 需要扁平化的对象 - * @param parentKey - 父键名,用于递归时拼接键名 - * @param result - 存储结果的对象 - * @returns 扁平化后的对象 - * - * 示例: - * const nestedObj = { - * user: { - * name: 'Alice', - * address: { - * city: 'Wonderland', - * zip: '12345' - * } - * }, - * items: [ - * { id: 1, name: 'Item 1' }, - * { id: 2, name: 'Item 2' } - * ], - * active: true - * }; - * const flatObj = flattenObject(nestedObj); - * console.log(flatObj); - * 输出: - * { - * userName: 'Alice', - * userAddressCity: 'Wonderland', - * userAddressZip: '12345', - * items: [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' } ], - * active: true - * } - */ -function flattenObject>( - obj: T, - parentKey: string = '', - result: Record = {}, -): Flatten { - Object.keys(obj).forEach((key) => { - const newKey = parentKey - ? `${parentKey}${capitalizeFirstLetter(key)}` - : key; - const value = obj[key]; - - if (value && typeof value === 'object' && !Array.isArray(value)) { - flattenObject(value, newKey, result); - } else { - result[newKey] = value; - } - }); - return result as Flatten; -} - -export { flattenObject }; - -// 定义递归类型,用于推断扁平化后的对象类型 -// 限制递归深度的辅助类型 -// type FlattenDepth< -// T, -// Depth extends number, -// CurrentDepth extends number[] = [], -// > = { -// [K in keyof T as CurrentDepth['length'] extends Depth -// ? K -// : T[K] extends object -// ? `${CurrentDepth['length'] extends 0 ? UnCapitalize : Capitalize}${keyof FlattenDepth extends string ? Capitalize> : ''}` -// : `${CurrentDepth['length'] extends 0 ? UnCapitalize : Capitalize}`]: CurrentDepth['length'] extends Depth -// ? T[K] -// : T[K] extends object -// ? FlattenDepth[keyof FlattenDepth< -// T[K], -// Depth, -// [...CurrentDepth, 1] -// >] -// : T[K]; -// }; - -// type Flatten = FlattenDepth; diff --git a/packages/@core/forward/helpers/src/index.ts b/packages/@core/forward/helpers/src/index.ts index b26d3b4b..c6e0e532 100644 --- a/packages/@core/forward/helpers/src/index.ts +++ b/packages/@core/forward/helpers/src/index.ts @@ -1,4 +1,2 @@ export * from './find-menu-by-path'; -export * from './flatten-object'; export * from './merge-route-modules'; -export * from './nested-object'; diff --git a/packages/@core/forward/helpers/src/nested-object.test.ts b/packages/@core/forward/helpers/src/nested-object.test.ts deleted file mode 100644 index 85acb391..00000000 --- a/packages/@core/forward/helpers/src/nested-object.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { nestedObject } from './nested-object'; - -describe('nestedObject', () => { - it('should convert flat object to nested object with level 1', () => { - const flatObject = { - anotherKeyExample: 2, - commonAppName: 1, - someOtherKey: 3, - }; - - const expectedNestedObject = { - anotherKeyExample: 2, - commonAppName: 1, - someOtherKey: 3, - }; - - expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject); - }); - - it('should convert flat object to nested object with level 2', () => { - const flatObject = { - appAnotherKeyExample: 2, - appCommonName: 1, - appSomeOtherKey: 3, - }; - - const expectedNestedObject = { - app: { - anotherKeyExample: 2, - commonName: 1, - someOtherKey: 3, - }, - }; - - expect(nestedObject(flatObject, 2)).toEqual(expectedNestedObject); - }); - - it('should convert flat object to nested object with level 3', () => { - const flatObject = { - appAnotherKeyExampleValue: 2, - appCommonNameKey: 1, - appSomeOtherKeyItem: 3, - }; - - const expectedNestedObject = { - app: { - another: { - keyExampleValue: 2, - }, - common: { - nameKey: 1, - }, - some: { - otherKeyItem: 3, - }, - }, - }; - - expect(nestedObject(flatObject, 3)).toEqual(expectedNestedObject); - }); - - it('should handle empty object', () => { - const flatObject = {}; - - const expectedNestedObject = {}; - - expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject); - }); - - it('should handle single key object', () => { - const flatObject = { - singleKey: 1, - }; - - const expectedNestedObject = { - singleKey: 1, - }; - - expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject); - }); - - it('should handle complex keys', () => { - const flatObject = { - anotherComplexKeyWithParts: 2, - complexKeyWithMultipleParts: 1, - }; - - const expectedNestedObject = { - anotherComplexKeyWithParts: 2, - complexKeyWithMultipleParts: 1, - }; - - expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject); - }); - - it('should correctly nest an object based on the specified level', () => { - const obj = { - oneFiveSix: 'Value156', - oneTwoFour: 'Value124', - oneTwoThree: 'Value123', - }; - - const nested = nestedObject(obj, 2); - - expect(nested).toEqual({ - one: { - fiveSix: 'Value156', - twoFour: 'Value124', - twoThree: 'Value123', - }, - }); - }); -}); diff --git a/packages/@core/forward/helpers/src/nested-object.ts b/packages/@core/forward/helpers/src/nested-object.ts deleted file mode 100644 index 471ef149..00000000 --- a/packages/@core/forward/helpers/src/nested-object.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { toLowerCaseFirstLetter } from '@vben-core/toolkit'; - -/** - * 将扁平对象转换为嵌套对象。 - * - * @template T - 输入对象值的类型 - * @param {Record} obj - 要转换的扁平对象 - * @param {number} level - 嵌套的层级 - * @returns {T} 嵌套对象 - * - * @example - * 将扁平对象转换为嵌套对象,嵌套层级为 1 - * const flatObject = { - * 'commonAppName': 1, - * 'anotherKeyExample': 2, - * 'someOtherKey': 3 - * }; - * const nestedObject = nestedObject(flatObject, 1); - * console.log(nestedObject); - * 输出: - * { - * commonAppName: 1, - * anotherKeyExample: 2, - * someOtherKey: 3 - * } - * - * @example - * 将扁平对象转换为嵌套对象,嵌套层级为 2 - * const flatObject = { - * 'appCommonName': 1, - * 'appAnotherKeyExample': 2, - * 'appSomeOtherKey': 3 - * }; - * const nestedObject = nestedObject(flatObject, 2); - * console.log(nestedObject); - * 输出: - * { - * app: { - * commonName: 1, - * anotherKeyExample: 2, - * someOtherKey: 3 - * } - * } - */ - -function nestedObject(obj: Record, level: number): T { - const result: any = {}; - - for (const key in obj) { - const keys = key.split(/(?=[A-Z])/); - // 将驼峰式分割为数组; - let current = result; - - for (let i = 0; i < keys.length; i++) { - const lowerKey = keys[i].toLowerCase(); - if (i === level - 1) { - const remainingKeys = keys.slice(i).join(''); // 保留后续部分作为键的一部分 - current[toLowerCaseFirstLetter(remainingKeys)] = obj[key]; - break; - } else { - current[lowerKey] = current[lowerKey] || {}; - current = current[lowerKey]; - } - } - } - - return result as T; -} - -export { nestedObject }; diff --git a/packages/@core/forward/preferences/src/config.ts b/packages/@core/forward/preferences/src/config.ts index 82519d9f..f992d885 100644 --- a/packages/@core/forward/preferences/src/config.ts +++ b/packages/@core/forward/preferences/src/config.ts @@ -17,7 +17,6 @@ const defaultPreferences: Preferences = { layout: 'sidebar-nav', locale: 'zh-CN', name: 'Vben Admin Pro', - semiDarkMenu: true, }, breadcrumb: { enable: true, @@ -82,6 +81,7 @@ const defaultPreferences: Preferences = { colorWarning: 'hsl(42 84% 61%)', mode: 'dark', radius: '0.5', + semiDarkMenu: true, }, transition: { enable: true, @@ -89,6 +89,15 @@ const defaultPreferences: Preferences = { name: 'fade-slide', progress: true, }, + widget: { + aiAssistant: true, + fullscreen: true, + globalSearch: true, + languageToggle: true, + notification: true, + sidebarToggle: true, + themeToggle: true, + }, }; export { defaultPreferences }; diff --git a/packages/@core/forward/preferences/src/index.ts b/packages/@core/forward/preferences/src/index.ts index 3f9cdbd6..35fe4ad5 100644 --- a/packages/@core/forward/preferences/src/index.ts +++ b/packages/@core/forward/preferences/src/index.ts @@ -1,3 +1,5 @@ +import type { DeepPartial } from '@vben-core/typings'; + import type { Preferences } from './types'; import { preferencesManager } from './preferences'; @@ -5,10 +7,6 @@ import { preferencesManager } from './preferences'; // 偏好设置(带有层级关系) const preferences: Preferences = preferencesManager.getPreferences(); -// 扁平化后的偏好设置 -// const flatPreferences: Flatten = -// preferencesManager.getFlatPreferences(); - // 更新偏好设置 const updatePreferences = preferencesManager.updatePreferences.bind(preferencesManager); @@ -20,9 +18,13 @@ const resetPreferences = const clearPreferencesCache = preferencesManager.clearCache.bind(preferencesManager); +function defineOverridesPreferences(preferences: DeepPartial) { + return preferences; +} + export { clearPreferencesCache, - // flatPreferences, + defineOverridesPreferences, preferences, preferencesManager, resetPreferences, diff --git a/packages/@core/forward/preferences/src/types.ts b/packages/@core/forward/preferences/src/types.ts index 0c9d04b6..553c5ec7 100644 --- a/packages/@core/forward/preferences/src/types.ts +++ b/packages/@core/forward/preferences/src/types.ts @@ -46,8 +46,6 @@ interface AppPreferences { locale: SupportedLanguagesType; /** 应用名 */ name: string; - /** 是否开启半深色菜单(只在theme='light'时生效) */ - semiDarkMenu: boolean; } interface BreadcrumbPreferences { @@ -164,6 +162,8 @@ interface ThemePreferences { mode: ThemeModeType; /** 圆角 */ radius: string; + /** 是否开启半深色菜单(只在theme='light'时生效) */ + semiDarkMenu: boolean; } interface TransitionPreferences { @@ -177,6 +177,23 @@ interface TransitionPreferences { progress: boolean; } +interface WidgetPreferences { + /** 是否开启vben助手部件 */ + aiAssistant: boolean; + /** 是否启用全屏部件 */ + fullscreen: boolean; + /** 是否启用全局搜索部件 */ + globalSearch: boolean; + /** 是否启用语言切换部件 */ + languageToggle: boolean; + /** 是否显示通知部件 */ + notification: boolean; + /** 是否显示侧边栏显示/隐藏部件 */ + sidebarToggle: boolean; + /** 是否显示主题切换部件 */ + themeToggle: boolean; +} + interface Preferences { /** 全局配置 */ app: AppPreferences; @@ -202,6 +219,8 @@ interface Preferences { theme: ThemePreferences; /** 动画配置 */ transition: TransitionPreferences; + /** 功能配置 */ + widget: WidgetPreferences; } type PreferencesKeys = keyof Preferences; @@ -230,4 +249,5 @@ export type { ThemeModeType, ThemePreferences, TransitionPreferences, + WidgetPreferences, }; diff --git a/packages/@core/locales/src/langs/en-US.json b/packages/@core/locales/src/langs/en-US.json index 354c0160..bda0e237 100644 --- a/packages/@core/locales/src/langs/en-US.json +++ b/packages/@core/locales/src/langs/en-US.json @@ -147,7 +147,6 @@ "general": "General", "language": "Language", "dynamic-title": "Dynamic Title", - "ai-assistant": "Ai Assistant", "sidebar": { "title": "Sidebar", "width": "Width", @@ -248,6 +247,16 @@ "search": "Global Search", "logout": "Logout", "preferences": "Preferences" + }, + "widget": { + "title": "Widget", + "global-search": "Enable Global Search", + "fullscreen": "Enable Fullscreen", + "theme-toggle": "Enable Theme Toggle", + "language-toggle": "Enable Language Toggle", + "notification": "Enable Notification", + "sidebar-toggle": "Enable Sidebar Toggle", + "ai-assistant": "Enable AI Assistant" } } } diff --git a/packages/@core/locales/src/langs/zh-CN.json b/packages/@core/locales/src/langs/zh-CN.json index c06b6d98..775de102 100644 --- a/packages/@core/locales/src/langs/zh-CN.json +++ b/packages/@core/locales/src/langs/zh-CN.json @@ -146,7 +146,6 @@ "general": "通用", "language": "语言", "dynamic-title": "动态标题", - "ai-assistant": "Ai 助手", "sidebar": { "title": "侧边栏", "width": "宽度", @@ -247,6 +246,16 @@ "search": "全局搜索", "logout": "退出登录", "preferences": "偏好设置" + }, + "widget": { + "title": "小部件", + "global-search": "启用全局搜索", + "fullscreen": "启用全屏", + "theme-toggle": "启用主题切换", + "language-toggle": "启用语言切换", + "notification": "启用通知", + "sidebar-toggle": "启用侧边栏切换", + "ai-assistant": "启用 AI 助手" } } } diff --git a/packages/@core/shared/typings/src/flatten.d.ts b/packages/@core/shared/typings/src/flatten.d.ts deleted file mode 100644 index d6546580..00000000 --- a/packages/@core/shared/typings/src/flatten.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -// `Prev` 类型用于表示递归深度的递减。它是一个元组,其索引代表了递归的层数,通过索引访问可以得到减少后的层数。 -// 例如,Prev[3] 等于 2,表示递归深度从 3 减少到 2。 -type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...0[]]; - -// `FlattenDepth` 类型用于将一个嵌套的对象类型“展平”,同时考虑到了递归的深度。 -// 它接受三个泛型参数:T(要处理的类型),Prefix(属性名前缀,默认为空字符串),Depth(递归深度,默认为3)。 -// 如果当前深度(Depth)为 0,则停止递归并返回 `never`。否则,如果属性值是对象类型,则递归调用 `FlattenDepth` 并递减深度。 -// 对于非对象类型的属性,将其直接映射到结果类型中,并根据前缀构造属性名。 - -type FlattenDepth = { - [K in keyof T]: T[K] extends object - ? Depth extends 0 - ? never - : FlattenDepth< - T[K], - `${Prefix}${K extends string ? (Prefix extends '' ? K : Capitalize) : ''}`, - Prev[Depth] - > - : { - [P in `${Prefix}${K extends string ? (Prefix extends '' ? K : Capitalize) : ''}`]: T[K]; - }; -}[keyof T] extends infer O - ? { [P in keyof O]: O[P] } - : never; - -// `UnionToIntersection` 类型用于将一个联合类型转换为交叉类型。 -// 这个类型通过条件类型和类型推断的方式来实现。它先尝试将输入类型(U)映射为一个函数类型, -// 然后通过推断这个函数类型的返回类型(infer I),最终得到一个交叉类型。 -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( - k: infer I, -) => void - ? I - : never; - -type Flatten = UnionToIntersection>; - -type FlattenObject = FlattenDepth; -type FlattenObjectKeys = keyof FlattenObject; - -export type { Flatten, FlattenObject, FlattenObjectKeys, UnionToIntersection }; diff --git a/packages/@core/shared/typings/src/index.ts b/packages/@core/shared/typings/src/index.ts index e8f8d09f..c46d868f 100644 --- a/packages/@core/shared/typings/src/index.ts +++ b/packages/@core/shared/typings/src/index.ts @@ -1,5 +1,4 @@ export type * from './app'; -export type * from './flatten'; export type * from './helper'; export type * from './menu-record'; export type * from './tabs'; diff --git a/packages/@core/ui-kit/layout-ui/src/vben-layout.ts b/packages/@core/ui-kit/layout-ui/src/vben-layout.ts index 0d96a4b2..94cf38ea 100644 --- a/packages/@core/ui-kit/layout-ui/src/vben-layout.ts +++ b/packages/@core/ui-kit/layout-ui/src/vben-layout.ts @@ -87,6 +87,11 @@ interface VbenLayoutProps { * @default 'fixed' */ headerMode?: LayoutHeaderModeType; + /** + * 是否显示header切换侧边栏按钮 + * @default + */ + headerToggleSidebarButton?: boolean; /** * header是否显示 * @default true @@ -152,21 +157,21 @@ interface VbenLayoutProps { * @default 210 */ sidebarWidth?: number; + /** + * footer背景颜色 + * @default #fff + */ + tabbarBackgroundColor?: string; /** * tab是否可见 * @default true */ tabbarEnable?: boolean; - /** - * footer背景颜色 - * @default #fff - */ - tabsBackgroundColor?: string; /** * tab高度 * @default 30 */ - tabsHeight?: number; + tabbarHeight?: number; /** * zIndex * @default 100 diff --git a/packages/@core/ui-kit/layout-ui/src/vben-layout.vue b/packages/@core/ui-kit/layout-ui/src/vben-layout.vue index 5038bcd4..6177fe00 100644 --- a/packages/@core/ui-kit/layout-ui/src/vben-layout.vue +++ b/packages/@core/ui-kit/layout-ui/src/vben-layout.vue @@ -32,8 +32,8 @@ const props = withDefaults(defineProps(), { headerHeight: 50, headerHeightOffset: 10, headerHidden: false, - headerMode: 'fixed', + headerToggleSidebarButton: true, headerVisible: true, isMobile: false, layout: 'sidebar-nav', @@ -45,7 +45,7 @@ const props = withDefaults(defineProps(), { sidebarTheme: 'dark', sidebarWidth: 180, tabbarEnable: true, - tabsHeight: 36, + tabbarHeight: 36, zIndex: 200, }); @@ -122,7 +122,7 @@ const headerWrapperHeight = computed(() => { height += getHeaderHeight.value; } if (props.tabbarEnable) { - height += props.tabsHeight; + height += props.tabbarHeight; } return height; }); @@ -364,6 +364,7 @@ const maskStyle = computed((): CSSProperties => { const showHeaderToggleButton = computed(() => { return ( + props.headerToggleSidebarButton && isSideMode.value && !isSidebarMixedNav.value && !isMixedNav.value && @@ -528,7 +529,7 @@ function handleOpenMenu() { diff --git a/packages/business/access/src/code-authority.vue b/packages/business/access/src/code-access.vue similarity index 94% rename from packages/business/access/src/code-authority.vue rename to packages/business/access/src/code-access.vue index 36d9a5f1..1163a42a 100644 --- a/packages/business/access/src/code-authority.vue +++ b/packages/business/access/src/code-access.vue @@ -13,7 +13,7 @@ interface Props { } defineOptions({ - name: 'CodeAuthority', + name: 'CodeAccess', }); withDefaults(defineProps(), { diff --git a/packages/business/access/src/index.ts b/packages/business/access/src/index.ts index 3f7e0f14..7301aeb5 100644 --- a/packages/business/access/src/index.ts +++ b/packages/business/access/src/index.ts @@ -1,5 +1,5 @@ -export { default as CodeAuthority } from './code-authority.vue'; +export { default as CodeAccess } from './code-access.vue'; export * from './generate-menu-and-routes'; -export { default as RoleAuthority } from './role-authority.vue'; +export { default as RoleAccess } from './role-access.vue'; export type * from './types'; export * from './use-access'; diff --git a/packages/business/access/src/role-authority.vue b/packages/business/access/src/role-access.vue similarity index 94% rename from packages/business/access/src/role-authority.vue rename to packages/business/access/src/role-access.vue index ebcbfaaf..4b833242 100644 --- a/packages/business/access/src/role-authority.vue +++ b/packages/business/access/src/role-access.vue @@ -13,7 +13,7 @@ interface Props { } defineOptions({ - name: 'RoleAuthority', + name: 'RoleAccess', }); withDefaults(defineProps(), { diff --git a/packages/business/layouts/src/basic/header/header.vue b/packages/business/layouts/src/basic/header/header.vue index b425e954..a9149ccf 100644 --- a/packages/business/layouts/src/basic/header/header.vue +++ b/packages/business/layouts/src/basic/header/header.vue @@ -1,5 +1,5 @@ - - diff --git a/packages/business/layouts/src/widgets/preferences/blocks/layout/widget.vue b/packages/business/layouts/src/widgets/preferences/blocks/layout/widget.vue new file mode 100644 index 00000000..e7957fd0 --- /dev/null +++ b/packages/business/layouts/src/widgets/preferences/blocks/layout/widget.vue @@ -0,0 +1,41 @@ + + + diff --git a/packages/business/layouts/src/widgets/preferences/preferences-sheet.vue b/packages/business/layouts/src/widgets/preferences/preferences-sheet.vue index 7891181a..373816f4 100644 --- a/packages/business/layouts/src/widgets/preferences/preferences-sheet.vue +++ b/packages/business/layouts/src/widgets/preferences/preferences-sheet.vue @@ -51,6 +51,7 @@ import { Sidebar, Tabbar, Theme, + Widget, } from './blocks'; import IconSetting from './icons/setting.vue'; import { useOpenPreferences } from './use-open-preferences'; @@ -59,7 +60,6 @@ const emit = defineEmits<{ clearPreferencesAndLogout: [] }>(); const { toast } = useToast(); const appLocale = defineModel('appLocale'); const appDynamicTitle = defineModel('appDynamicTitle'); -const appAiAssistant = defineModel('appAiAssistant'); const appLayout = defineModel('appLayout'); const appColorGrayMode = defineModel('appColorGrayMode'); const appColorWeakMode = defineModel('appColorWeakMode'); @@ -129,6 +129,14 @@ const shortcutKeysGlobalPreferences = defineModel( 'shortcutKeysGlobalPreferences', ); +const widgetGlobalSearch = defineModel('widgetGlobalSearch'); +const widgetFullscreen = defineModel('widgetFullscreen'); +const widgetLanguageToggle = defineModel('widgetLanguageToggle'); +const widgetNotification = defineModel('widgetNotification'); +const widgetThemeToggle = defineModel('widgetThemeToggle'); +const widgetAiAssistant = defineModel('widgetAiAssistant'); +const widgetSidebarToggle = defineModel('widgetSidebarToggle'); + const { diffPreference, isDark, @@ -245,7 +253,6 @@ async function handleReset() {