diff --git a/.changeset/config.json b/.changeset/config.json
index 7f769543644..f954fb4ba97 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -11,7 +11,7 @@
},
"privatePackages": { "version": true, "tag": true },
"linked": [],
- "access": "restricted",
+ "access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
diff --git a/.github/workflows/action-build.yml b/.github/workflows/action-build.yml
index 55fcadf71fb..b1820591ae0 100644
--- a/.github/workflows/action-build.yml
+++ b/.github/workflows/action-build.yml
@@ -45,5 +45,5 @@ jobs:
- name: Test and Build
run: |
- pnpm run test
+ pnpm run test:unit
pnpm run build
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index da7758a5eee..e308ec5768d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -68,7 +68,7 @@ jobs:
# run: git config --global user.email "you@example.com" && git config --global user.name "Your Name"
- name: Vitest tests
- run: pnpm run test
+ run: pnpm run test:unit
# - name: Upload coverage
# uses: codecov/codecov-action@v4
diff --git a/.ls-lint.yml b/.ls-lint.yml
index 63d6b761daa..7f8b7ce6e14 100644
--- a/.ls-lint.yml
+++ b/.ls-lint.yml
@@ -7,7 +7,7 @@ ls:
.css: kebab-case | pointcase
.d.ts: kebab-case | pointcase
# shadcn 自动生成文件为 PascalCase 格式
- packages/@vben-core/ui-kit/shadcn-ui/src/components/ui:
+ packages/@core/ui-kit/shadcn-ui/src/components/ui:
.vue: PascalCase
ignore:
@@ -25,3 +25,4 @@ ignore:
- .vscode
- .idea
- node_modules
+ - .cache
diff --git a/.vscode/settings.json b/.vscode/settings.json
index ffba678d555..9b97c9e26a1 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -71,7 +71,8 @@
"**/CVS": true,
"**/.stylelintcache": true,
"**/.DS_Store": true,
- "**/vite.config.mts.*": true
+ "**/vite.config.mts.*": true,
+ "**/tea.yaml": true
},
"files.watcherExclude": {
"**/.git/objects/**": true,
@@ -190,7 +191,7 @@
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,.ls-lint*,cspell.json",
"tailwind.config.mjs": "postcss.*"
},
- "commentTranslate.hover.enabled": true,
+ "commentTranslate.hover.enabled": false,
"i18n-ally.keystyle": "nested",
"commentTranslate.multiLineMerge": true
}
diff --git a/LICENSE b/LICENSE
index a4a2fe5cc31..cec5b42741c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024-present, Vben Admin
+Copyright (c) 2024-present, Vben
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
diff --git a/README.md b/README.md
index f2119b6a513..a33ae3e2045 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[](LICENSE)
-
Vue vben admin
+Vue Vben Admin
**English** | [中文](./README.zh-CN.md)
diff --git a/apps/backend-mock/middleware/1.api.ts b/apps/backend-mock/middleware/1.api.ts
new file mode 100644
index 00000000000..315d7e0cd2b
--- /dev/null
+++ b/apps/backend-mock/middleware/1.api.ts
@@ -0,0 +1,14 @@
+export default defineEventHandler((event) => {
+ // setResponseHeaders(event, {
+ // 'Access-Control-Allow-Credentials': 'true',
+ // 'Access-Control-Allow-Headers': '*',
+ // 'Access-Control-Allow-Methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
+ // 'Access-Control-Allow-Origin': '*',
+ // 'Access-Control-Expose-Headers': '*',
+ // });
+ if (event.method === 'OPTIONS') {
+ event.node.res.statusCode = 204;
+ event.node.res.statusMessage = 'No Content.';
+ return 'OK';
+ }
+});
diff --git a/apps/backend-mock/nitro.config.ts b/apps/backend-mock/nitro.config.ts
index 76b12dc1854..b3013298de4 100644
--- a/apps/backend-mock/nitro.config.ts
+++ b/apps/backend-mock/nitro.config.ts
@@ -3,4 +3,16 @@ import errorHandler from './error';
export default defineNitroConfig({
devErrorHandler: errorHandler,
errorHandler: '~/error',
+ routeRules: {
+ '/api/**': {
+ cors: true,
+ headers: {
+ 'Access-Control-Allow-Credentials': 'true',
+ 'Access-Control-Allow-Headers': '*',
+ 'Access-Control-Allow-Methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Expose-Headers': '*',
+ },
+ },
+ },
});
diff --git a/apps/backend-mock/utils/mock-data.ts b/apps/backend-mock/utils/mock-data.ts
index 10076ac9e9c..46c0e5a2bbb 100644
--- a/apps/backend-mock/utils/mock-data.ts
+++ b/apps/backend-mock/utils/mock-data.ts
@@ -80,8 +80,8 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
icon: 'mdi:button-cursor',
title: 'page.demos.access.adminVisible',
},
- name: 'AccessAdminVisible',
- path: 'admin-visible',
+ name: 'AccessAdminVisibleDemo',
+ path: '/demos/access/admin-visible',
},
super: {
component: '/demos/access/super-visible',
@@ -89,8 +89,8 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
icon: 'mdi:button-cursor',
title: 'page.demos.access.superVisible',
},
- name: 'AccessSuperVisible',
- path: 'super-visible',
+ name: 'AccessSuperVisibleDemo',
+ path: '/demos/access/super-visible',
},
user: {
component: '/demos/access/user-visible',
@@ -98,8 +98,8 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
icon: 'mdi:button-cursor',
title: 'page.demos.access.userVisible',
},
- name: 'AccessUserVisible',
- path: 'user-visible',
+ name: 'AccessUserVisibleDemo',
+ path: '/demos/access/user-visible',
},
};
@@ -117,8 +117,8 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
redirect: '/demos/access',
children: [
{
- name: 'Access',
- path: 'access',
+ name: 'AccessDemos',
+ path: '/demosaccess',
meta: {
icon: 'mdi:cloud-key-outline',
title: 'page.demos.access.backendPermissions',
@@ -126,8 +126,8 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
redirect: '/demos/access/page-control',
children: [
{
- name: 'AccessPageControl',
- path: 'page-control',
+ name: 'AccessPageControlDemo',
+ path: '/demos/access/page-control',
component: '/demos/access/index',
meta: {
icon: 'mdi:page-previous-outline',
@@ -135,8 +135,8 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
},
},
{
- name: 'AccessButtonControl',
- path: 'button-control',
+ name: 'AccessButtonControlDemo',
+ path: '/demos/access/button-control',
component: '/demos/access/button-control',
meta: {
icon: 'mdi:button-cursor',
@@ -144,8 +144,8 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
},
},
{
- name: 'AccessMenuVisible403',
- path: 'menu-visible-403',
+ name: 'AccessMenuVisible403Demo',
+ path: '/demos/access/menu-visible-403',
component: '/demos/access/menu-visible-403',
meta: {
authority: ['no-body'],
diff --git a/apps/web-antd/.env b/apps/web-antd/.env
index 5a7032d4759..57000a52ab5 100644
--- a/apps/web-antd/.env
+++ b/apps/web-antd/.env
@@ -1,6 +1,5 @@
-VITE_PORT = 5555
+# 应用标题
+VITE_GLOB_APP_TITLE=Vben Admin
-# spa-title
-VITE_GLOB_APP_TITLE = Vben Admin
-
-VITE_APP_NAMESPACE = web-antd
+# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
+VITE_APP_NAMESPACE=vben-web-antd
diff --git a/apps/web-antd/.env.analyze b/apps/web-antd/.env.analyze
index f367addb1e6..ffafa8dd529 100644
--- a/apps/web-antd/.env.analyze
+++ b/apps/web-antd/.env.analyze
@@ -1,8 +1,7 @@
-
# public path
-VITE_PUBLIC_PATH = /
+VITE_BASE=/
# Basic interface address SPA
VITE_GLOB_API_URL=/api
-VITE_VISUALIZER = true
+VITE_VISUALIZER=true
diff --git a/apps/web-antd/.env.development b/apps/web-antd/.env.development
index 447b41c4f26..dcf361e730e 100644
--- a/apps/web-antd/.env.development
+++ b/apps/web-antd/.env.development
@@ -1,5 +1,16 @@
-VITE_PUBLIC_PATH = /
+# 端口号
+VITE_PORT=5555
+VITE_BASE=/
+
+# 接口地址
VITE_GLOB_API_URL=/api
-VITE_NITRO_MOCK = true
+# 是否开启 Nitro Mock服务,true 为开启,false 为关闭
+VITE_NITRO_MOCK=true
+
+# 是否打开 devtools,true 为打开,false 为关闭
+VITE_DEVTOOLS=false
+
+# 是否注入全局loading
+VITE_INJECT_APP_LOADING=true
diff --git a/apps/web-antd/.env.production b/apps/web-antd/.env.production
index 209bd7a0fb8..ce8f55031d1 100644
--- a/apps/web-antd/.env.production
+++ b/apps/web-antd/.env.production
@@ -1,9 +1,16 @@
-# public path
-VITE_PUBLIC_PATH = /
+VITE_BASE=/
-# Basic interface address SPA
-VITE_GLOB_API_URL = https://mock-napi.vben.pro/api
+# 接口地址
+VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
-VITE_COMPRESS = gzip
+# 是否开启压缩,可以设置为 none, brotli, gzip
+VITE_COMPRESS=gzip
-VITE_PWA = false
+# 是否开启 PWA
+VITE_PWA=false
+
+# vue-router 的模式
+VITE_ROUTER_HISTORY=hash
+
+# 是否注入全局loading
+VITE_INJECT_APP_LOADING=true
diff --git a/apps/web-antd/index.html b/apps/web-antd/index.html
index 64c4ff88f52..01d77c1e398 100644
--- a/apps/web-antd/index.html
+++ b/apps/web-antd/index.html
@@ -14,6 +14,19 @@
<%= VITE_GLOB_APP_TITLE %>
+
diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json
index b970d8ca136..38a7cd8f82a 100644
--- a/apps/web-antd/package.json
+++ b/apps/web-antd/package.json
@@ -44,7 +44,7 @@
"ant-design-vue": "^4.2.3",
"dayjs": "^1.11.12",
"pinia": "2.1.7",
- "vue": "^3.4.33",
+ "vue": "^3.4.34",
"vue-router": "^4.4.0"
}
}
diff --git a/apps/web-antd/src/api/core/auth.ts b/apps/web-antd/src/api/core/auth.ts
new file mode 100644
index 00000000000..6950e3bf129
--- /dev/null
+++ b/apps/web-antd/src/api/core/auth.ts
@@ -0,0 +1,33 @@
+import { requestClient } from '#/api/request';
+
+export namespace AuthApi {
+ /** 登录接口参数 */
+ export interface LoginParams {
+ password: string;
+ username: string;
+ }
+
+ /** 登录接口返回值 */
+ export interface LoginResult {
+ accessToken: string;
+ desc: string;
+ realName: string;
+ refreshToken: string;
+ userId: string;
+ username: string;
+ }
+}
+
+/**
+ * 登录
+ */
+export async function login(data: AuthApi.LoginParams) {
+ return requestClient.post('/auth/login', data);
+}
+
+/**
+ * 获取用户权限码
+ */
+export async function getAccessCodes() {
+ return requestClient.get('/auth/codes');
+}
diff --git a/apps/web-antd/src/apis/core/index.ts b/apps/web-antd/src/api/core/index.ts
similarity index 100%
rename from apps/web-antd/src/apis/core/index.ts
rename to apps/web-antd/src/api/core/index.ts
diff --git a/apps/web-antd/src/apis/core/menu.ts b/apps/web-antd/src/api/core/menu.ts
similarity index 81%
rename from apps/web-antd/src/apis/core/menu.ts
rename to apps/web-antd/src/api/core/menu.ts
index 57088acafc5..62c40f17e25 100644
--- a/apps/web-antd/src/apis/core/menu.ts
+++ b/apps/web-antd/src/api/core/menu.ts
@@ -1,6 +1,6 @@
import type { RouteRecordStringComponent } from '@vben/types';
-import { requestClient } from '#/apis/request';
+import { requestClient } from '#/api/request';
/**
* 获取用户所有菜单
diff --git a/apps/web-antd/src/apis/core/user.ts b/apps/web-antd/src/api/core/user.ts
similarity index 77%
rename from apps/web-antd/src/apis/core/user.ts
rename to apps/web-antd/src/api/core/user.ts
index 5eecf0bdbb8..34c14ea9bc3 100644
--- a/apps/web-antd/src/apis/core/user.ts
+++ b/apps/web-antd/src/api/core/user.ts
@@ -1,6 +1,6 @@
import type { UserInfo } from '@vben/types';
-import { requestClient } from '#/apis/request';
+import { requestClient } from '#/api/request';
/**
* 获取用户信息
diff --git a/apps/web-antd/src/apis/demos/index.ts b/apps/web-antd/src/api/demos/index.ts
similarity index 100%
rename from apps/web-antd/src/apis/demos/index.ts
rename to apps/web-antd/src/api/demos/index.ts
diff --git a/apps/web-antd/src/apis/demos/status.ts b/apps/web-antd/src/api/demos/status.ts
similarity index 78%
rename from apps/web-antd/src/apis/demos/status.ts
rename to apps/web-antd/src/api/demos/status.ts
index 7da2d49ccf0..daa3d638376 100644
--- a/apps/web-antd/src/apis/demos/status.ts
+++ b/apps/web-antd/src/api/demos/status.ts
@@ -1,4 +1,4 @@
-import { requestClient } from '#/apis/request';
+import { requestClient } from '#/api/request';
/**
* 模拟任意状态码
diff --git a/apps/web-antd/src/apis/index.ts b/apps/web-antd/src/api/index.ts
similarity index 62%
rename from apps/web-antd/src/apis/index.ts
rename to apps/web-antd/src/api/index.ts
index 911672b77e6..2b42e898a12 100644
--- a/apps/web-antd/src/apis/index.ts
+++ b/apps/web-antd/src/api/index.ts
@@ -1,3 +1,2 @@
export * from './core';
export * from './demos';
-export type * from './types';
diff --git a/apps/web-antd/src/apis/request.ts b/apps/web-antd/src/api/request.ts
similarity index 78%
rename from apps/web-antd/src/apis/request.ts
rename to apps/web-antd/src/api/request.ts
index f56ba744c75..817d0d6dafb 100644
--- a/apps/web-antd/src/apis/request.ts
+++ b/apps/web-antd/src/api/request.ts
@@ -3,6 +3,7 @@
*/
import type { HttpResponse } from '@vben/request';
+import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences';
import { RequestClient } from '@vben/request';
@@ -10,13 +11,11 @@ import { message } from 'ant-design-vue';
import { useAccessStore } from '#/store';
-/**
- * 创建请求实例
- * Create a request instance
- */
-function createRequestClient() {
+const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
+
+function createRequestClient(baseURL: string) {
const client = new RequestClient({
- baseURL: import.meta.env.VITE_GLOB_API_URL,
+ baseURL,
// 为每个请求携带 Authorization
makeAuthorization: () => {
return {
@@ -43,6 +42,13 @@ function createRequestClient() {
};
},
makeErrorMessage: (msg) => message.error(msg),
+
+ makeRequestHeaders: () => {
+ return {
+ // 为每个请求携带 Accept-Language
+ 'Accept-Language': preferences.app.locale,
+ };
+ },
});
client.addResponseInterceptor((response) => {
const { data: responseData, status } = response;
@@ -56,9 +62,4 @@ function createRequestClient() {
return client;
}
-const requestClient = createRequestClient();
-
-// 其他配置的请求方法
-// const { request: xxxRequest } = createRequest();
-
-export { requestClient };
+export const requestClient = createRequestClient(apiURL);
diff --git a/apps/web-antd/src/apis/core/auth.ts b/apps/web-antd/src/apis/core/auth.ts
deleted file mode 100644
index 227d0f2a87b..00000000000
--- a/apps/web-antd/src/apis/core/auth.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import type { UserApi } from '../types';
-
-import { requestClient } from '#/apis/request';
-
-/**
- * 登录
- */
-export async function login(data: UserApi.LoginParams) {
- return requestClient.post('/auth/login', data);
-}
-
-/**
- * 获取用户权限码
- */
-export async function getAccessCodes() {
- return requestClient.get('/auth/codes');
-}
diff --git a/apps/web-antd/src/apis/types/index.ts b/apps/web-antd/src/apis/types/index.ts
deleted file mode 100644
index d20c6861aa1..00000000000
--- a/apps/web-antd/src/apis/types/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type * from './user';
diff --git a/apps/web-antd/src/apis/types/user.ts b/apps/web-antd/src/apis/types/user.ts
deleted file mode 100644
index 6c49df91c76..00000000000
--- a/apps/web-antd/src/apis/types/user.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace UserApi {
- /** 登录接口参数 */
- export interface LoginParams {
- password: string;
- username: string;
- }
-
- /** 登录接口返回值 */
- export interface LoginResult {
- accessToken: string;
- desc: string;
- realName: string;
- refreshToken: string;
- userId: string;
- username: string;
- }
-}
-
-export type { UserApi };
diff --git a/apps/web-antd/src/bootstrap.ts b/apps/web-antd/src/bootstrap.ts
index 607f7fa7d10..8141e95f3e6 100644
--- a/apps/web-antd/src/bootstrap.ts
+++ b/apps/web-antd/src/bootstrap.ts
@@ -1,10 +1,9 @@
import { createApp } from 'vue';
-import { preferences } from '@vben/preferences';
import '@vben/styles';
import '@vben/styles/antd';
-import { loadMessages, setupI18n } from '#/locales';
+import { setupI18n } from '#/locales';
import { setupStore } from '#/store';
import App from './app.vue';
@@ -14,11 +13,7 @@ async function bootstrap(namespace: string) {
const app = createApp(App);
// 国际化 i18n 配置
- await setupI18n(app, {
- defaultLocale: preferences.app.locale,
- loadMessages,
- missingWarn: !import.meta.env.PROD,
- });
+ await setupI18n(app);
// 配置 pinia-store
await setupStore(app, { namespace });
diff --git a/apps/web-antd/src/locales/index.ts b/apps/web-antd/src/locales/index.ts
index 11438544817..a3e23661e81 100644
--- a/apps/web-antd/src/locales/index.ts
+++ b/apps/web-antd/src/locales/index.ts
@@ -1,9 +1,11 @@
-import type { SupportedLanguagesType } from '@vben/types';
+import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
import type { Locale } from 'ant-design-vue/es/locale';
+import type { App } from 'vue';
import { ref } from 'vue';
-import { $t, loadLocalesMap, setupI18n } from '@vben/locales';
+import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
+import { preferences } from '@vben/preferences';
import antdEnLocale from 'ant-design-vue/es/locale/en_US';
import antdDefaultLocale from 'ant-design-vue/es/locale/zh_CN';
@@ -76,4 +78,13 @@ async function loadAntdLocale(lang: SupportedLanguagesType) {
}
}
+async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
+ await coreSetup(app, {
+ defaultLocale: preferences.app.locale,
+ loadMessages,
+ missingWarn: !import.meta.env.PROD,
+ ...options,
+ });
+}
+
export { $t, antdLocale, loadMessages, setupI18n };
diff --git a/apps/web-antd/src/locales/langs/en-US.json b/apps/web-antd/src/locales/langs/en-US.json
index 014aabaa897..6b1d636ed6d 100644
--- a/apps/web-antd/src/locales/langs/en-US.json
+++ b/apps/web-antd/src/locales/langs/en-US.json
@@ -33,11 +33,17 @@
"text": "Text Badge",
"color": "Badge Color"
},
+ "activeIcon": {
+ "title": "Active Menu Icon",
+ "children": "Children Active Icon"
+ },
"fallback": { "title": "Fallback Page" },
"features": {
"title": "Features",
"hideChildrenInMenu": "Hide Menu Children",
"loginExpired": "Login Expired",
+ "icons": "Icons",
+ "watermark": "Watermark",
"tabs": "Tabs",
"tabDetail": "Tab Detail Page"
},
diff --git a/apps/web-antd/src/locales/langs/zh-CN.json b/apps/web-antd/src/locales/langs/zh-CN.json
index 08b3fa1f946..0ce11b74e5f 100644
--- a/apps/web-antd/src/locales/langs/zh-CN.json
+++ b/apps/web-antd/src/locales/langs/zh-CN.json
@@ -33,6 +33,10 @@
"text": "文本徽标",
"color": "徽标颜色"
},
+ "activeIcon": {
+ "title": "菜单激活图标",
+ "children": "子级激活图标"
+ },
"fallback": {
"title": "缺省页"
},
@@ -40,6 +44,8 @@
"title": "功能",
"hideChildrenInMenu": "隐藏子菜单",
"loginExpired": "登录过期",
+ "icons": "图标",
+ "watermark": "水印",
"tabs": "标签页",
"tabDetail": "标签详情页"
},
diff --git a/apps/web-antd/src/main.ts b/apps/web-antd/src/main.ts
index 89d7b2cd6ba..dedabbcbf64 100644
--- a/apps/web-antd/src/main.ts
+++ b/apps/web-antd/src/main.ts
@@ -1,3 +1,4 @@
+import { unmountGlobalLoading } from '@vben/hooks';
import { initPreferences } from '@vben/preferences';
import { overridesPreferences } from './preferences';
@@ -9,7 +10,8 @@ async function initApplication() {
// name用于指定项目唯一标识
// 用于区分不同项目的偏好设置以及存储数据的key前缀以及其他一些需要隔离的数据
const env = import.meta.env.PROD ? 'prod' : 'dev';
- const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${env}`;
+ const appVersion = import.meta.env.VITE_APP_VERSION;
+ const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`;
// app偏好设置初始化
await initPreferences({
@@ -23,38 +25,7 @@ async function initApplication() {
await bootstrap(namespace);
// 移除并销毁loading
- destroyAppLoading();
-}
-
-/**
- * 移除并销毁loading
- * 放在这里是而不是放在 index.html 的app标签内,是因为这样比较不会生硬,渲染过快可能会有闪烁
- * 通过先添加css动画隐藏,在动画结束后在移除loading节点来改善体验
- * 不好的地方是会增加一些代码量
- */
-function destroyAppLoading() {
- // 查找全局 loading 元素
- const loadingElement = document.querySelector('#__app-loading__');
-
- if (loadingElement) {
- // 添加隐藏类,触发过渡动画
- loadingElement.classList.add('hidden');
-
- // 查找所有需要移除的注入 loading 元素
- const injectLoadingElements = document.querySelectorAll(
- '[data-app-loading^="inject"]',
- );
-
- // 当过渡动画结束时,移除 loading 元素和所有注入的 loading 元素
- loadingElement.addEventListener(
- 'transitionend',
- () => {
- loadingElement.remove(); // 移除 loading 元素
- injectLoadingElements.forEach((el) => el.remove()); // 移除所有注入的 loading 元素
- },
- { once: true },
- ); // 确保事件只触发一次
- }
+ unmountGlobalLoading();
}
initApplication();
diff --git a/apps/web-antd/src/preferences.ts b/apps/web-antd/src/preferences.ts
index 9683596a0cc..e8607fbdbeb 100644
--- a/apps/web-antd/src/preferences.ts
+++ b/apps/web-antd/src/preferences.ts
@@ -1,9 +1,15 @@
+import { useAppConfig } from '@vben/hooks';
import { defineOverridesPreferences } from '@vben/preferences';
+const { appTitle } = useAppConfig(import.meta.env, import.meta.env.PROD);
+
/**
* @description 项目配置文件
* 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置
*/
export const overridesPreferences = defineOverridesPreferences({
// overrides
+ app: {
+ name: appTitle,
+ },
});
diff --git a/apps/web-antd/src/router/access.ts b/apps/web-antd/src/router/access.ts
index b2a0a4f8802..251db72f549 100644
--- a/apps/web-antd/src/router/access.ts
+++ b/apps/web-antd/src/router/access.ts
@@ -8,7 +8,7 @@ import { preferences } from '@vben/preferences';
import { message } from 'ant-design-vue';
-import { getAllMenus } from '#/apis';
+import { getAllMenus } from '#/api';
import { BasicLayout, IFrameView } from '#/layouts';
import { $t } from '#/locales';
diff --git a/apps/web-antd/src/router/index.ts b/apps/web-antd/src/router/index.ts
index bddd05c3d3b..0c9d2c4a173 100644
--- a/apps/web-antd/src/router/index.ts
+++ b/apps/web-antd/src/router/index.ts
@@ -1,6 +1,10 @@
import type { RouteRecordName, RouteRecordRaw } from 'vue-router';
-import { createRouter, createWebHashHistory } from 'vue-router';
+import {
+ createRouter,
+ createWebHashHistory,
+ createWebHistory,
+} from 'vue-router';
import { traverseTreeValues } from '@vben/utils';
@@ -11,11 +15,14 @@ import { routes } from './routes';
* @zh_CN 创建vue-router实例
*/
const router = createRouter({
- history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH),
+ history:
+ import.meta.env.VITE_ROUTER_HISTORY === 'hash'
+ ? createWebHashHistory(import.meta.env.VITE_BASE)
+ : createWebHistory(import.meta.env.VITE_BASE),
// 应该添加到路由的初始路由列表。
routes,
scrollBehavior: () => ({ left: 0, top: 0 }),
- // 是否应该禁止尾部斜杠。默认为假
+ // 是否应该禁止尾部斜杠。
// strict: true,
});
diff --git a/apps/web-antd/src/router/routes/modules/dashboard.ts b/apps/web-antd/src/router/routes/modules/dashboard.ts
index 123bdbeb948..a8cdb05da08 100644
--- a/apps/web-antd/src/router/routes/modules/dashboard.ts
+++ b/apps/web-antd/src/router/routes/modules/dashboard.ts
@@ -13,11 +13,10 @@ const routes: RouteRecordRaw[] = [
},
name: 'Dashboard',
path: '/',
- redirect: '/analytics',
children: [
{
name: 'Analytics',
- path: 'analytics',
+ path: '/analytics',
component: () => import('#/views/dashboard/analytics/index.vue'),
meta: {
affixTab: true,
@@ -27,7 +26,7 @@ const routes: RouteRecordRaw[] = [
},
{
name: 'Workspace',
- path: 'workspace',
+ path: '/workspace',
component: () => import('#/views/dashboard/workspace/index.vue'),
meta: {
title: $t('page.dashboard.workspace'),
diff --git a/apps/web-antd/src/router/routes/modules/demos.ts b/apps/web-antd/src/router/routes/modules/demos.ts
index c0e369a4a6f..1c766832e6c 100644
--- a/apps/web-antd/src/router/routes/modules/demos.ts
+++ b/apps/web-antd/src/router/routes/modules/demos.ts
@@ -14,7 +14,6 @@ const routes: RouteRecordRaw[] = [
},
name: 'Demos',
path: '/demos',
- redirect: '/demos/access',
children: [
// 权限控制
{
@@ -22,13 +21,12 @@ const routes: RouteRecordRaw[] = [
icon: 'mdi:shield-key-outline',
title: $t('page.demos.access.frontendPermissions'),
},
- name: 'Access',
- path: 'access',
- redirect: '/demos/access/page-control',
+ name: 'AccessDemos',
+ path: '/demos/access',
children: [
{
- name: 'AccessPageControl',
- path: 'page-control',
+ name: 'AccessPageControlDemo',
+ path: '/demos/access/page-control',
component: () => import('#/views/demos/access/index.vue'),
meta: {
icon: 'mdi:page-previous-outline',
@@ -36,8 +34,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'AccessButtonControl',
- path: 'button-control',
+ name: 'AccessButtonControlDemo',
+ path: '/demos/access/button-control',
component: () => import('#/views/demos/access/button-control.vue'),
meta: {
icon: 'mdi:button-cursor',
@@ -45,8 +43,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'AccessMenuVisible403',
- path: 'menu-visible-403',
+ name: 'AccessMenuVisible403Demo',
+ path: '/demos/access/menu-visible-403',
component: () =>
import('#/views/demos/access/menu-visible-403.vue'),
meta: {
@@ -57,8 +55,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'AccessSuperVisible',
- path: 'super-visible',
+ name: 'AccessSuperVisibleDemo',
+ path: '/demos/access/super-visible',
component: () => import('#/views/demos/access/super-visible.vue'),
meta: {
authority: ['super'],
@@ -67,8 +65,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'AccessAdminVisible',
- path: 'admin-visible',
+ name: 'AccessAdminVisibleDemo',
+ path: '/demos/access/admin-visible',
component: () => import('#/views/demos/access/admin-visible.vue'),
meta: {
authority: ['admin'],
@@ -77,8 +75,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'AccessUserVisible',
- path: 'user-visible',
+ name: 'AccessUserVisibleDemo',
+ path: '/demos/access/user-visible',
component: () => import('#/views/demos/access/user-visible.vue'),
meta: {
authority: ['user'],
@@ -94,13 +92,39 @@ const routes: RouteRecordRaw[] = [
icon: 'mdi:feature-highlight',
title: $t('page.demos.features.title'),
},
- name: 'Features',
- path: 'features',
- redirect: '/demos/features/tabs',
+ name: 'FeaturesDemos',
+ path: '/demos/features',
children: [
+ {
+ name: 'LoginExpiredDemo',
+ path: '/demos/features/login-expired',
+ component: () =>
+ import('#/views/demos/features/login-expired/index.vue'),
+ meta: {
+ icon: 'mdi:encryption-expiration',
+ title: $t('page.demos.features.loginExpired'),
+ },
+ },
+ {
+ name: 'IconsDemo',
+ path: '/demos/features/icons',
+ component: () => import('#/views/demos/features/icons/index.vue'),
+ meta: {
+ title: $t('page.demos.features.icons'),
+ },
+ },
+ {
+ name: 'WatermarkDemo',
+ path: '/demos/features/watermark',
+ component: () =>
+ import('#/views/demos/features/watermark/index.vue'),
+ meta: {
+ title: $t('page.demos.features.watermark'),
+ },
+ },
{
name: 'FeatureTabsDemo',
- path: 'tabs',
+ path: '/demos/features/tabs',
component: () => import('#/views/demos/features/tabs/index.vue'),
meta: {
icon: 'lucide:app-window',
@@ -109,7 +133,7 @@ const routes: RouteRecordRaw[] = [
},
{
name: 'FeatureTabDetailDemo',
- path: 'tabs/detail/:id',
+ path: '/demos/features/tabs/detail/:id',
component: () =>
import('#/views/demos/features/tabs/tab-detail.vue'),
meta: {
@@ -120,19 +144,19 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'HideChildrenInMenuParent',
- path: 'hide-menu-children',
+ name: 'HideChildrenInMenuParentDemo',
+ path: '/demos/features/hide-menu-children',
component: () =>
import('#/views/demos/features/hide-menu-children/parent.vue'),
meta: {
hideChildrenInMenu: true,
icon: 'ic:round-menu',
- title: 'page.demos.features.hideChildrenInMenu',
+ title: $t('page.demos.features.hideChildrenInMenu'),
},
children: [
{
- name: 'HideChildrenInMenuChildren',
- path: 'hide-children-in-menu',
+ name: 'HideChildrenInMenuChildrenDemo',
+ path: '/demos/features/hide-menu-children/children',
component: () =>
import(
'#/views/demos/features/hide-menu-children/children.vue'
@@ -140,31 +164,20 @@ const routes: RouteRecordRaw[] = [
},
],
},
- {
- name: 'LoginExpired',
- path: 'login-expired',
- component: () =>
- import('#/views/demos/features/login-expired/index.vue'),
- meta: {
- icon: 'mdi:encryption-expiration',
- title: $t('page.demos.features.loginExpired'),
- },
- },
],
},
// 面包屑导航
{
name: 'BreadcrumbDemos',
- path: 'breadcrumb',
+ path: '/demos/breadcrumb',
meta: {
icon: 'lucide:navigation',
title: $t('page.demos.breadcrumb.navigation'),
},
- redirect: '/demos/breadcrumb/lateral',
children: [
{
- name: 'BreadcrumbLateral',
- path: 'lateral',
+ name: 'BreadcrumbLateralDemo',
+ path: '/demos/breadcrumb/lateral',
component: () => import('#/views/demos/breadcrumb/lateral.vue'),
meta: {
icon: 'lucide:navigation',
@@ -172,8 +185,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'BreadcrumbLateralDetail',
- path: 'lateral-detail',
+ name: 'BreadcrumbLateralDetailDemo',
+ path: '/demos/breadcrumb/lateral-detail',
component: () =>
import('#/views/demos/breadcrumb/lateral-detail.vue'),
meta: {
@@ -183,17 +196,16 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'BreadcrumbLevel',
- path: 'level',
+ name: 'BreadcrumbLevelDemo',
+ path: '/demos/breadcrumb/level',
meta: {
icon: 'lucide:navigation',
title: $t('page.demos.breadcrumb.level'),
},
- redirect: '/demos/breadcrumb/level/detail',
children: [
{
- name: 'BreadcrumbLevelDetail',
- path: 'detail',
+ name: 'BreadcrumbLevelDetailDemo',
+ path: '/demos/breadcrumb/level/detail',
component: () =>
import('#/views/demos/breadcrumb/level-detail.vue'),
meta: {
@@ -210,13 +222,12 @@ const routes: RouteRecordRaw[] = [
icon: 'mdi:lightbulb-error-outline',
title: $t('page.demos.fallback.title'),
},
- name: 'Fallback',
- path: 'fallback',
- redirect: '/demos/fallback/403',
+ name: 'FallbackDemos',
+ path: '/demos/fallback',
children: [
{
- name: 'Fallback403',
- path: '403',
+ name: 'Fallback403Demo',
+ path: '/demos/fallback/403',
component: () => import('#/views/_core/fallback/forbidden.vue'),
meta: {
icon: 'mdi:do-not-disturb-alt',
@@ -224,8 +235,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'Fallback404',
- path: '404',
+ name: 'Fallback404Demo',
+ path: '/demos/fallback/404',
component: () => import('#/views/_core/fallback/not-found.vue'),
meta: {
icon: 'mdi:table-off',
@@ -233,8 +244,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'Fallback500',
- path: '500',
+ name: 'Fallback500Demo',
+ path: '/demos/fallback/500',
component: () =>
import('#/views/_core/fallback/internal-error.vue'),
meta: {
@@ -243,8 +254,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'FallbackOffline',
- path: 'offline',
+ name: 'FallbackOfflineDemo',
+ path: '/demos/fallback/offline',
component: () => import('#/views/_core/fallback/offline.vue'),
meta: {
icon: 'mdi:offline',
@@ -261,14 +272,13 @@ const routes: RouteRecordRaw[] = [
icon: 'lucide:circle-dot',
title: $t('page.demos.badge.title'),
},
- name: 'BadgeDemo',
- path: 'badge',
- redirect: '/demos/badge/dot',
+ name: 'BadgeDemos',
+ path: '/demos/badge',
children: [
{
name: 'BadgeDotDemo',
component: () => import('#/views/demos/badge/index.vue'),
- path: 'dot',
+ path: '/demos/badge/dot',
meta: {
badgeType: 'dot',
icon: 'lucide:square-dot',
@@ -278,7 +288,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'BadgeTextDemo',
component: () => import('#/views/demos/badge/index.vue'),
- path: 'text',
+ path: '/demos/badge/text',
meta: {
badge: '10',
icon: 'lucide:square-dot',
@@ -288,7 +298,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'BadgeColorDemo',
component: () => import('#/views/demos/badge/index.vue'),
- path: 'color',
+ path: '/demos/badge/color',
meta: {
badge: 'Hot',
badgeVariants: 'destructive',
@@ -298,28 +308,48 @@ const routes: RouteRecordRaw[] = [
},
],
},
+ // 菜单激活图标
+ {
+ meta: {
+ activeIcon: 'fluent-emoji:radioactive',
+ icon: 'bi:radioactive',
+ title: $t('page.demos.activeIcon.title'),
+ },
+ name: 'ActiveIconDemos',
+ path: '/demos/active-icon',
+ children: [
+ {
+ name: 'ActiveIconDemo',
+ component: () => import('#/views/demos/active-icon/index.vue'),
+ path: '/demos/active-icon/children',
+ meta: {
+ activeIcon: 'fluent-emoji:radioactive',
+ icon: 'bi:radioactive',
+ title: $t('page.demos.activeIcon.children'),
+ },
+ },
+ ],
+ },
// 外部链接
{
meta: {
icon: 'ic:round-settings-input-composite',
title: $t('page.demos.outside.title'),
},
- name: 'Outside',
- path: 'outside',
- redirect: '/demos/outside/iframe',
+ name: 'OutsideDemos',
+ path: '/demos/outside',
children: [
{
- name: 'iframe',
- path: 'iframe',
+ name: 'IframeDemos',
+ path: '/demos/outside/iframe',
meta: {
icon: 'mdi:newspaper-variant-outline',
title: $t('page.demos.outside.embedded'),
},
- redirect: '/demos/outside/iframe/vue-document',
children: [
{
- name: 'VueDocument',
- path: 'vue-document',
+ name: 'VueDocumentDemo',
+ path: '/demos/outside/iframe/vue-document',
component: IFrameView,
meta: {
icon: 'logos:vue',
@@ -329,8 +359,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'Tailwindcss',
- path: 'tailwindcss',
+ name: 'TailwindcssDemo',
+ path: '/demos/outside/iframe/tailwindcss',
component: IFrameView,
meta: {
icon: 'devicon:tailwindcss',
@@ -342,17 +372,16 @@ const routes: RouteRecordRaw[] = [
],
},
{
- name: 'ExternalLink',
- path: 'external-link',
+ name: 'ExternalLinkDemos',
+ path: '/demos/outside/external-link',
meta: {
icon: 'mdi:newspaper-variant-multiple-outline',
title: $t('page.demos.outside.externalLink'),
},
- redirect: '/demos/outside/external-link/vite',
children: [
{
- name: 'Vite',
- path: 'vite',
+ name: 'ViteDemo',
+ path: '/demos/outside/external-link/vite',
component: IFrameView,
meta: {
icon: 'logos:vitejs',
@@ -361,8 +390,8 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'VueUse',
- path: 'vue-use',
+ name: 'VueUseDemo',
+ path: '/demos/outside/external-link/vue-use',
component: IFrameView,
meta: {
icon: 'logos:vueuse',
@@ -380,13 +409,12 @@ const routes: RouteRecordRaw[] = [
icon: 'ic:round-menu',
title: $t('page.demos.nested.title'),
},
- name: 'Nested',
- path: 'nested',
- redirect: '/demos/nested/menu1',
+ name: 'NestedDemos',
+ path: '/demos/nested',
children: [
{
- name: 'Menu1',
- path: 'menu1',
+ name: 'Menu1Demo',
+ path: '/demos/nested/menu1',
component: () => import('#/views/demos/nested/menu-1.vue'),
meta: {
icon: 'ic:round-menu',
@@ -395,18 +423,17 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'Menu2',
- path: 'menu2',
+ name: 'Menu2Demo',
+ path: '/demos/nested/menu2',
meta: {
icon: 'ic:round-menu',
keepAlive: true,
title: $t('page.demos.nested.menu2'),
},
- redirect: '/demos/nested/menu2/menu2-1',
children: [
{
- name: 'Menu21',
- path: 'menu2-1',
+ name: 'Menu21Demo',
+ path: '/demos/nested/menu2/menu2-1',
component: () => import('#/views/demos/nested/menu-2-1.vue'),
meta: {
icon: 'ic:round-menu',
@@ -417,16 +444,15 @@ const routes: RouteRecordRaw[] = [
],
},
{
- name: 'Menu3',
- path: 'menu3',
+ name: 'Menu3Demo',
+ path: '/demos/nested/menu3',
meta: {
icon: 'ic:round-menu',
title: $t('page.demos.nested.menu3'),
},
- redirect: '/demos/nested/menu3/menu3-1',
children: [
{
- name: 'Menu31',
+ name: 'Menu31Demo',
path: 'menu3-1',
component: () => import('#/views/demos/nested/menu-3-1.vue'),
meta: {
@@ -436,17 +462,16 @@ const routes: RouteRecordRaw[] = [
},
},
{
- name: 'Menu32',
+ name: 'Menu32Demo',
path: 'menu3-2',
meta: {
icon: 'ic:round-menu',
title: $t('page.demos.nested.menu3_2'),
},
- redirect: '/demos/nested/menu3/menu3-2/menu3-2-1',
children: [
{
- name: 'Menu321',
- path: 'menu3-2-1',
+ name: 'Menu321Demo',
+ path: '/demos/nested/menu3/menu3-2/menu3-2-1',
component: () =>
import('#/views/demos/nested/menu-3-2-1.vue'),
meta: {
diff --git a/apps/web-antd/src/router/routes/modules/vben.ts b/apps/web-antd/src/router/routes/modules/vben.ts
index 4df1475e5c0..2d1fdf24ead 100644
--- a/apps/web-antd/src/router/routes/modules/vben.ts
+++ b/apps/web-antd/src/router/routes/modules/vben.ts
@@ -17,11 +17,10 @@ const routes: RouteRecordRaw[] = [
},
name: 'VbenProject',
path: '/vben-admin',
- redirect: '/vben-admin/about',
children: [
{
name: 'VbenAbout',
- path: 'about',
+ path: '/vben-admin/about',
component: () => import('#/views/_core/vben/about/index.vue'),
meta: {
badgeType: 'dot',
@@ -32,7 +31,7 @@ const routes: RouteRecordRaw[] = [
},
{
name: 'VbenDocument',
- path: 'document',
+ path: '/vben-admin/document',
component: IFrameView,
meta: {
icon: 'lucide:book-open-text',
@@ -43,7 +42,7 @@ const routes: RouteRecordRaw[] = [
},
{
name: 'VbenGithub',
- path: 'github',
+ path: '/vben-admin/github',
component: IFrameView,
meta: {
icon: 'mdi:github',
diff --git a/apps/web-antd/src/store/modules/access.ts b/apps/web-antd/src/store/modules/access.ts
index c459931d860..b4afb54667d 100644
--- a/apps/web-antd/src/store/modules/access.ts
+++ b/apps/web-antd/src/store/modules/access.ts
@@ -11,7 +11,7 @@ import { resetAllStores, useCoreAccessStore } from '@vben/stores';
import { notification } from 'ant-design-vue';
import { defineStore } from 'pinia';
-import { getAccessCodes, getUserInfo, login } from '#/apis';
+import { getAccessCodes, getUserInfo, login } from '#/api';
import { $t } from '#/locales';
export const useAccessStore = defineStore('access', () => {
diff --git a/apps/web-antd/src/views/_core/fallback/forbidden.vue b/apps/web-antd/src/views/_core/fallback/forbidden.vue
index e6c47bae431..8ea65fedb52 100644
--- a/apps/web-antd/src/views/_core/fallback/forbidden.vue
+++ b/apps/web-antd/src/views/_core/fallback/forbidden.vue
@@ -1,5 +1,7 @@
diff --git a/apps/web-antd/src/views/_core/fallback/internal-error.vue b/apps/web-antd/src/views/_core/fallback/internal-error.vue
index 7154e725ebe..819a47d5ea8 100644
--- a/apps/web-antd/src/views/_core/fallback/internal-error.vue
+++ b/apps/web-antd/src/views/_core/fallback/internal-error.vue
@@ -1,5 +1,7 @@
diff --git a/apps/web-antd/src/views/_core/fallback/not-found.vue b/apps/web-antd/src/views/_core/fallback/not-found.vue
index cdc5a331d54..4d178e9cbd4 100644
--- a/apps/web-antd/src/views/_core/fallback/not-found.vue
+++ b/apps/web-antd/src/views/_core/fallback/not-found.vue
@@ -1,5 +1,7 @@
diff --git a/apps/web-antd/src/views/_core/fallback/offline.vue b/apps/web-antd/src/views/_core/fallback/offline.vue
index de5e6a82138..5de4a88de4e 100644
--- a/apps/web-antd/src/views/_core/fallback/offline.vue
+++ b/apps/web-antd/src/views/_core/fallback/offline.vue
@@ -1,5 +1,7 @@
diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue
index ab2147fe062..1bef4d4ec1b 100644
--- a/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue
+++ b/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue
@@ -3,8 +3,6 @@ import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
-defineOptions({ name: 'AnalyticsTrends' });
-
const chartRef = ref();
const { renderEcharts } = useEcharts(chartRef);
diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue
index 43674a842c6..f03907d0260 100644
--- a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue
+++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue
@@ -3,8 +3,6 @@ import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
-defineOptions({ name: 'AnalyticsVisitsData' });
-
const chartRef = ref();
const { renderEcharts } = useEcharts(chartRef);
diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue
index 9be875c863f..0529a3b0504 100644
--- a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue
+++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue
@@ -3,8 +3,6 @@ import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
-defineOptions({ name: 'AnalyticsVisitsSales' });
-
const chartRef = ref();
const { renderEcharts } = useEcharts(chartRef);
diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue
index 8cf14cc12c0..99292bf9de0 100644
--- a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue
+++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue
@@ -3,8 +3,6 @@ import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
-defineOptions({ name: 'AnalyticsVisitsSource' });
-
const chartRef = ref();
const { renderEcharts } = useEcharts(chartRef);
diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue
index 27d2024b100..4dfc163fd71 100644
--- a/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue
+++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue
@@ -3,8 +3,6 @@ import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
-defineOptions({ name: 'AnalyticsVisits' });
-
const chartRef = ref();
const { renderEcharts } = useEcharts(chartRef);
diff --git a/apps/web-antd/src/views/dashboard/analytics/index.vue b/apps/web-antd/src/views/dashboard/analytics/index.vue
index 28962302306..00b34df1e26 100644
--- a/apps/web-antd/src/views/dashboard/analytics/index.vue
+++ b/apps/web-antd/src/views/dashboard/analytics/index.vue
@@ -20,8 +20,6 @@ import AnalyticsVisitsData from './analytics-visits-data.vue';
import AnalyticsVisitsSales from './analytics-visits-sales.vue';
import AnalyticsVisitsSource from './analytics-visits-source.vue';
-defineOptions({ name: 'Analytics' });
-
const overviewItems: AnalysisOverviewItem[] = [
{
icon: SvgCardIcon,
diff --git a/apps/web-antd/src/views/dashboard/workspace/index.vue b/apps/web-antd/src/views/dashboard/workspace/index.vue
index 2ba195c6ef8..38dea1bb3ac 100644
--- a/apps/web-antd/src/views/dashboard/workspace/index.vue
+++ b/apps/web-antd/src/views/dashboard/workspace/index.vue
@@ -22,8 +22,6 @@ import { useAccessStore } from '#/store';
import AnalyticsVisitsSource from '../analytics/analytics-visits-source.vue';
-defineOptions({ name: 'Workspace' });
-
const accessStore = useAccessStore();
const projectItems: WorkbenchProjectItem[] = [
diff --git a/apps/web-antd/src/views/demos/access/admin-visible.vue b/apps/web-antd/src/views/demos/access/admin-visible.vue
index 59611f894dc..079b93092f4 100644
--- a/apps/web-antd/src/views/demos/access/admin-visible.vue
+++ b/apps/web-antd/src/views/demos/access/admin-visible.vue
@@ -1,7 +1,5 @@
diff --git a/apps/web-antd/src/views/demos/access/button-control.vue b/apps/web-antd/src/views/demos/access/button-control.vue
index 89f403c8353..2733241768f 100644
--- a/apps/web-antd/src/views/demos/access/button-control.vue
+++ b/apps/web-antd/src/views/demos/access/button-control.vue
@@ -9,8 +9,6 @@ import { Button } from 'ant-design-vue';
import { resetAllStores, useAccessStore } from '#/store';
-defineOptions({ name: 'AccessButtonControl' });
-
const accounts: Record = {
admin: {
password: '123456',
diff --git a/apps/web-antd/src/views/demos/access/index.vue b/apps/web-antd/src/views/demos/access/index.vue
index a902ce5d2c8..949500bbbf8 100644
--- a/apps/web-antd/src/views/demos/access/index.vue
+++ b/apps/web-antd/src/views/demos/access/index.vue
@@ -9,8 +9,6 @@ import { Button } from 'ant-design-vue';
import { resetAllStores, useAccessStore } from '#/store';
-defineOptions({ name: 'Access' });
-
const accounts: Record = {
admin: {
password: '123456',
@@ -51,7 +49,9 @@ async function handleToggleAccessMode() {
resetAllStores();
await accessStore.authLogin(accounts.super, async () => {
- router.go(0);
+ setTimeout(() => {
+ router.go(0);
+ }, 150);
});
}
diff --git a/apps/web-antd/src/views/demos/access/menu-visible-403.vue b/apps/web-antd/src/views/demos/access/menu-visible-403.vue
index a9969928b7f..7e3cde88b11 100644
--- a/apps/web-antd/src/views/demos/access/menu-visible-403.vue
+++ b/apps/web-antd/src/views/demos/access/menu-visible-403.vue
@@ -1,7 +1,5 @@
diff --git a/apps/web-antd/src/views/demos/access/super-visible.vue b/apps/web-antd/src/views/demos/access/super-visible.vue
index 9e909a1cc85..877fcd1156f 100644
--- a/apps/web-antd/src/views/demos/access/super-visible.vue
+++ b/apps/web-antd/src/views/demos/access/super-visible.vue
@@ -1,7 +1,5 @@
diff --git a/apps/web-antd/src/views/demos/access/user-visible.vue b/apps/web-antd/src/views/demos/access/user-visible.vue
index f6ae57431bf..f69255cad7a 100644
--- a/apps/web-antd/src/views/demos/access/user-visible.vue
+++ b/apps/web-antd/src/views/demos/access/user-visible.vue
@@ -1,7 +1,5 @@
diff --git a/apps/web-antd/src/views/demos/active-icon/index.vue b/apps/web-antd/src/views/demos/active-icon/index.vue
new file mode 100644
index 00000000000..af9f69aff03
--- /dev/null
+++ b/apps/web-antd/src/views/demos/active-icon/index.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/apps/web-antd/src/views/demos/badge/index.vue b/apps/web-antd/src/views/demos/badge/index.vue
index b5e19c24331..92d70c7e170 100644
--- a/apps/web-antd/src/views/demos/badge/index.vue
+++ b/apps/web-antd/src/views/demos/badge/index.vue
@@ -1,7 +1,5 @@
diff --git a/apps/web-antd/src/views/demos/breadcrumb/lateral-detail.vue b/apps/web-antd/src/views/demos/breadcrumb/lateral-detail.vue
index 47c3e0d03fc..8a4de64e25f 100644
--- a/apps/web-antd/src/views/demos/breadcrumb/lateral-detail.vue
+++ b/apps/web-antd/src/views/demos/breadcrumb/lateral-detail.vue
@@ -5,8 +5,6 @@ import { Fallback } from '@vben/common-ui';
import { Button } from 'ant-design-vue';
-defineOptions({ name: 'BreadcrumbLateralDetail' });
-
const router = useRouter();
diff --git a/apps/web-antd/src/views/demos/breadcrumb/lateral.vue b/apps/web-antd/src/views/demos/breadcrumb/lateral.vue
index 6d487431eb2..2c24abff2e0 100644
--- a/apps/web-antd/src/views/demos/breadcrumb/lateral.vue
+++ b/apps/web-antd/src/views/demos/breadcrumb/lateral.vue
@@ -5,12 +5,10 @@ import { Fallback } from '@vben/common-ui';
import { Button } from 'ant-design-vue';
-defineOptions({ name: 'BreadcrumbLateral' });
-
const router = useRouter();
function details() {
- router.push({ name: 'BreadcrumbLateralDetail' });
+ router.push({ name: 'BreadcrumbLateralDetailDemo' });
}
diff --git a/apps/web-antd/src/views/demos/breadcrumb/level-detail.vue b/apps/web-antd/src/views/demos/breadcrumb/level-detail.vue
index bcba562bfcf..7aa18d4b050 100644
--- a/apps/web-antd/src/views/demos/breadcrumb/level-detail.vue
+++ b/apps/web-antd/src/views/demos/breadcrumb/level-detail.vue
@@ -1,7 +1,5 @@
diff --git a/apps/web-antd/src/views/demos/features/hide-menu-children/parent.vue b/apps/web-antd/src/views/demos/features/hide-menu-children/parent.vue
index 255fe310bfd..9208775165d 100644
--- a/apps/web-antd/src/views/demos/features/hide-menu-children/parent.vue
+++ b/apps/web-antd/src/views/demos/features/hide-menu-children/parent.vue
@@ -1,7 +1,5 @@
diff --git a/apps/web-antd/src/views/demos/features/icons/index.vue b/apps/web-antd/src/views/demos/features/icons/index.vue
new file mode 100644
index 00000000000..065409ba6a0
--- /dev/null
+++ b/apps/web-antd/src/views/demos/features/icons/index.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
+
图标
+
+ 图标可在
+
+ Iconify
+
+ 中查找,支持多种图标库,如 Material Design, Font Awesome, Jam Icons 等。
+
+
+
+
+
Iconify
+
+
+
+
+
+
+
+
+
+
+
Svg Icons
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-antd/src/views/demos/features/login-expired/index.vue b/apps/web-antd/src/views/demos/features/login-expired/index.vue
index ffb0d1798b5..7f3814965d9 100644
--- a/apps/web-antd/src/views/demos/features/login-expired/index.vue
+++ b/apps/web-antd/src/views/demos/features/login-expired/index.vue
@@ -5,9 +5,7 @@ import { preferences, updatePreferences } from '@vben/preferences';
import { Button } from 'ant-design-vue';
-import { getMockStatus } from '#/apis';
-
-defineOptions({ name: 'LoginExpired' });
+import { getMockStatus } from '#/api';
async function handleClick(type: LoginExpiredModeType) {
const loginExpiredMode = preferences.app.loginExpiredMode;
diff --git a/apps/web-antd/src/views/demos/features/tabs/index.vue b/apps/web-antd/src/views/demos/features/tabs/index.vue
index 48d52ce3688..9415b668e74 100644
--- a/apps/web-antd/src/views/demos/features/tabs/index.vue
+++ b/apps/web-antd/src/views/demos/features/tabs/index.vue
@@ -6,8 +6,6 @@ import { useTabs } from '@vben/hooks';
import { Input as AInput, Button } from 'ant-design-vue';
-defineOptions({ name: 'FeatureTabsDemo' });
-
const router = useRouter();
const newTabTitle = ref('');
diff --git a/apps/web-antd/src/views/demos/features/tabs/tab-detail.vue b/apps/web-antd/src/views/demos/features/tabs/tab-detail.vue
index 809cf9107bd..c21080c0afe 100644
--- a/apps/web-antd/src/views/demos/features/tabs/tab-detail.vue
+++ b/apps/web-antd/src/views/demos/features/tabs/tab-detail.vue
@@ -4,8 +4,6 @@ import { useRoute } from 'vue-router';
import { useTabs } from '@vben/hooks';
-defineOptions({ name: 'FeatureTabDetailDemo' });
-
const route = useRoute();
const { setTabTitle } = useTabs();
diff --git a/apps/web-antd/src/views/demos/features/watermark/index.vue b/apps/web-antd/src/views/demos/features/watermark/index.vue
new file mode 100644
index 00000000000..493ab86a2d4
--- /dev/null
+++ b/apps/web-antd/src/views/demos/features/watermark/index.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
diff --git a/apps/web-antd/src/views/demos/nested/menu-1.vue b/apps/web-antd/src/views/demos/nested/menu-1.vue
index 707844c449e..d1b16748c3a 100644
--- a/apps/web-antd/src/views/demos/nested/menu-1.vue
+++ b/apps/web-antd/src/views/demos/nested/menu-1.vue
@@ -1,9 +1,10 @@
-
+
+
+
+
diff --git a/apps/web-antd/src/views/demos/nested/menu-2-1.vue b/apps/web-antd/src/views/demos/nested/menu-2-1.vue
index 057b54fc282..d1b16748c3a 100644
--- a/apps/web-antd/src/views/demos/nested/menu-2-1.vue
+++ b/apps/web-antd/src/views/demos/nested/menu-2-1.vue
@@ -1,9 +1,10 @@
-
+
+
+
+
diff --git a/apps/web-antd/src/views/demos/nested/menu-3-1.vue b/apps/web-antd/src/views/demos/nested/menu-3-1.vue
index 97d7f69902d..d1b16748c3a 100644
--- a/apps/web-antd/src/views/demos/nested/menu-3-1.vue
+++ b/apps/web-antd/src/views/demos/nested/menu-3-1.vue
@@ -1,9 +1,10 @@
-
+
+
+
+
diff --git a/apps/web-antd/src/views/demos/nested/menu-3-2-1.vue b/apps/web-antd/src/views/demos/nested/menu-3-2-1.vue
index be4009df7dc..d1b16748c3a 100644
--- a/apps/web-antd/src/views/demos/nested/menu-3-2-1.vue
+++ b/apps/web-antd/src/views/demos/nested/menu-3-2-1.vue
@@ -1,9 +1,10 @@
-
+
+
+
+
diff --git a/apps/web-antd/tsconfig.node.json b/apps/web-antd/tsconfig.node.json
index 9d98b252ec8..c2f0d86cc78 100644
--- a/apps/web-antd/tsconfig.node.json
+++ b/apps/web-antd/tsconfig.node.json
@@ -3,6 +3,7 @@
"extends": "@vben/tsconfig/node.json",
"compilerOptions": {
"composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"noEmit": false
},
"include": ["vite.config.mts"]
diff --git a/apps/web-antd/vite.config.mts b/apps/web-antd/vite.config.mts
index d856432f1a7..a3858f8aa64 100644
--- a/apps/web-antd/vite.config.mts
+++ b/apps/web-antd/vite.config.mts
@@ -6,7 +6,7 @@ import {
} from '@vben/vite-config';
export default defineConfig(async () => {
- const { appTitle, port, ...envConfig } = await loadAndConvertEnv();
+ const { appTitle, base, port, ...envConfig } = await loadAndConvertEnv();
return {
application: {
...envConfig,
@@ -16,13 +16,14 @@ export default defineConfig(async () => {
pwaOptions: getDefaultPwaOptions(appTitle),
},
vite: {
+ base,
server: {
port,
proxy: {
'/api': {
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
- // 代理目标地址 - backend-mock 项目
+ // mock代理目标地址
target: 'http://localhost:5320/api',
ws: true,
},
diff --git a/internal/lint-configs/eslint-config/package.json b/internal/lint-configs/eslint-config/package.json
index 3c812220f66..ea42b94a0cf 100644
--- a/internal/lint-configs/eslint-config/package.json
+++ b/internal/lint-configs/eslint-config/package.json
@@ -28,25 +28,25 @@
},
"dependencies": {
"eslint-config-turbo": "^2.0.9",
- "eslint-plugin-command": "^0.2.3"
+ "eslint-plugin-command": "^0.2.3",
+ "eslint-plugin-import-x": "^3.1.0"
},
"devDependencies": {
- "@eslint/js": "^9.7.0",
+ "@eslint/js": "^9.8.0",
"@types/eslint": "^9.6.0",
"@typescript-eslint/eslint-plugin": "^7.17.0",
"@typescript-eslint/parser": "^7.17.0",
- "eslint": "^8.57.0",
+ "eslint": "^9.8.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-eslint-comments": "^3.2.0",
- "eslint-plugin-i": "^2.29.1",
"eslint-plugin-jsdoc": "^48.8.3",
"eslint-plugin-jsonc": "^2.16.0",
- "eslint-plugin-n": "^17.9.0",
+ "eslint-plugin-n": "^17.10.1",
"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-perfectionist": "^3.0.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-regexp": "^2.6.0",
- "eslint-plugin-unicorn": "^54.0.0",
+ "eslint-plugin-unicorn": "^55.0.0",
"eslint-plugin-unused-imports": "^4.0.1",
"eslint-plugin-vitest": "^0.5.4",
"eslint-plugin-vue": "^9.27.0",
diff --git a/internal/lint-configs/eslint-config/src/configs/import.ts b/internal/lint-configs/eslint-config/src/configs/import.ts
index 85eff89e95f..67a08feaf24 100644
--- a/internal/lint-configs/eslint-config/src/configs/import.ts
+++ b/internal/lint-configs/eslint-config/src/configs/import.ts
@@ -1,14 +1,12 @@
import type { Linter } from 'eslint';
-export async function importPluginConfig(): Promise {
- const [pluginImport] = await Promise.all([
- // @ts-expect-error - no types
- import('eslint-plugin-i'),
- ] as const);
+import * as pluginImport from 'eslint-plugin-import-x';
+export async function importPluginConfig(): Promise {
return [
{
plugins: {
+ // @ts-expect-error - This is a dynamic import
import: pluginImport,
},
rules: {
diff --git a/internal/lint-configs/stylelint-config/package.json b/internal/lint-configs/stylelint-config/package.json
index eb8f3330048..15c310a34fc 100644
--- a/internal/lint-configs/stylelint-config/package.json
+++ b/internal/lint-configs/stylelint-config/package.json
@@ -33,7 +33,7 @@
"stylelint-scss": "^6.4.1"
},
"devDependencies": {
- "postcss": "^8.4.39",
+ "postcss": "^8.4.40",
"postcss-html": "^1.7.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.3.3",
diff --git a/internal/node-utils/package.json b/internal/node-utils/package.json
index 4625bab2f99..090b07e0fbe 100644
--- a/internal/node-utils/package.json
+++ b/internal/node-utils/package.json
@@ -37,6 +37,6 @@
"pkg-types": "^1.1.3",
"prettier": "^3.3.3",
"rimraf": "^6.0.1",
- "zx": "^7.2.3"
+ "zx": "^8.1.4"
}
}
diff --git a/internal/tailwind-config/package.json b/internal/tailwind-config/package.json
index 8206a246a9f..3ff27588cc2 100644
--- a/internal/tailwind-config/package.json
+++ b/internal/tailwind-config/package.json
@@ -46,17 +46,17 @@
"tailwindcss": "^3.4.3"
},
"dependencies": {
- "@iconify/json": "^2.2.230",
+ "@iconify/json": "^2.2.231",
"@iconify/tailwind": "^1.1.1",
"@tailwindcss/nesting": "0.0.0-insiders.565cd3e",
"@tailwindcss/typography": "^0.5.13",
"autoprefixer": "^10.4.19",
"cssnano": "^7.0.4",
- "postcss": "^8.4.39",
+ "postcss": "^8.4.40",
"postcss-antd-fixes": "^0.2.0",
"postcss-import": "^16.1.0",
"postcss-preset-env": "^9.6.0",
- "tailwindcss": "^3.4.6",
+ "tailwindcss": "^3.4.7",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
diff --git a/internal/tsconfig/package.json b/internal/tsconfig/package.json
index 40c423fc761..2213af9a2c3 100644
--- a/internal/tsconfig/package.json
+++ b/internal/tsconfig/package.json
@@ -20,6 +20,6 @@
],
"dependencies": {
"@vben/types": "workspace:*",
- "vite": "^5.3.4"
+ "vite": "^5.3.5"
}
}
diff --git a/internal/vite-config/package.json b/internal/vite-config/package.json
index c1f8e09ef4d..6b01b3f409d 100644
--- a/internal/vite-config/package.json
+++ b/internal/vite-config/package.json
@@ -34,20 +34,20 @@
"nitropack": "^2.9.7",
"resolve.exports": "^2.0.2",
"vite-plugin-lib-inject-css": "^2.1.1",
- "vite-plugin-pwa": "^0.20.0",
- "vite-plugin-vue-devtools": "^7.3.6"
+ "vite-plugin-pwa": "^0.20.1",
+ "vite-plugin-vue-devtools": "^7.3.7"
},
"devDependencies": {
"@types/html-minifier-terser": "^7.0.2",
"@vben/node-utils": "workspace:*",
- "@vitejs/plugin-vue": "^5.0.5",
+ "@vitejs/plugin-vue": "^5.1.1",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"dayjs": "^1.11.12",
"dotenv": "^16.4.5",
- "rollup": "^4.19.0",
+ "rollup": "^4.19.1",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.77.8",
- "vite": "^5.3.4",
+ "vite": "^5.3.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-dts": "4.0.0-beta.1",
"vite-plugin-html": "^3.2.2"
diff --git a/internal/vite-config/src/config/application.ts b/internal/vite-config/src/config/application.ts
index e058ddf0cb0..ea65c61db6e 100644
--- a/internal/vite-config/src/config/application.ts
+++ b/internal/vite-config/src/config/application.ts
@@ -94,7 +94,7 @@ function createCssOptions(injectGlobalScss = true) {
const relativePath = relative(root, filepath);
// apps下的包注入全局样式
if (relativePath.startsWith('apps/')) {
- return `@import "@vben/styles/global";\n${content}`;
+ return `@import (reference) "@vben/styles/global";\n${content}`;
}
return content;
},
diff --git a/internal/vite-config/src/options.ts b/internal/vite-config/src/options.ts
index 2841c5caeaf..876ca1ccb81 100644
--- a/internal/vite-config/src/options.ts
+++ b/internal/vite-config/src/options.ts
@@ -7,16 +7,16 @@ const isDevelopment = process.env.NODE_ENV === 'development';
const getDefaultPwaOptions = (name: string): Partial => ({
manifest: {
description:
- 'Vue Vben Admin is a modern admin dashboard template based on Vue 3. ',
+ 'Vben Admin is a modern admin dashboard template based on Vue 3. ',
icons: [
{
sizes: '192x192',
- src: 'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/pwa-icon-192.png',
+ src: 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/pwa-icon-192.png',
type: 'image/png',
},
{
sizes: '512x512',
- src: 'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/pwa-icon-512.png',
+ src: 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/pwa-icon-512.png',
type: 'image/png',
},
],
diff --git a/internal/vite-config/src/plugins/inject-app-loading/index.ts b/internal/vite-config/src/plugins/inject-app-loading/index.ts
index 8f881486453..3d7c20b354e 100644
--- a/internal/vite-config/src/plugins/inject-app-loading/index.ts
+++ b/internal/vite-config/src/plugins/inject-app-loading/index.ts
@@ -1,7 +1,7 @@
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
-import { fs } from '@vben/node-utils';
+import { fs, readPackageJSON } from '@vben/node-utils';
import { type PluginOption } from 'vite';
@@ -15,8 +15,9 @@ async function viteInjectAppLoadingPlugin(
loadingTemplate = 'loading.html',
): Promise {
const loadingHtml = await getLoadingRawByHtmlTemplate(loadingTemplate);
+ const { version } = await readPackageJSON(process.cwd());
const envRaw = isBuild ? 'prod' : 'dev';
- const cacheName = `'${env.VITE_APP_NAMESPACE}-${envRaw}-preferences-theme'`;
+ const cacheName = `'${env.VITE_APP_NAMESPACE}-${version}-${envRaw}-preferences-theme'`;
// 获取缓存的主题
// 保证黑暗主题下,刷新页面时,loading也是黑暗主题
diff --git a/internal/vite-config/src/plugins/inject-metadata.ts b/internal/vite-config/src/plugins/inject-metadata.ts
index a234b070497..80d1db5ab43 100644
--- a/internal/vite-config/src/plugins/inject-metadata.ts
+++ b/internal/vite-config/src/plugins/inject-metadata.ts
@@ -74,6 +74,7 @@ async function viteMetadataPlugin(
license,
version,
}),
+ 'import.meta.env.VITE_APP_VERSION': JSON.stringify(version),
},
};
},
diff --git a/internal/vite-config/src/plugins/license.ts b/internal/vite-config/src/plugins/license.ts
index 87544def585..81fc4ff07ea 100644
--- a/internal/vite-config/src/plugins/license.ts
+++ b/internal/vite-config/src/plugins/license.ts
@@ -30,7 +30,7 @@ async function viteLicensePlugin(
handler: (_options: NormalizedOutputOptions, bundle: OutputBundle) => {
const date = dateUtil().format('YYYY-MM-DD ');
const copyrightText = `/*!
- * Vue Vben Admin
+ * Vben Admin
* Version: ${version}
* Author: vben
* Copyright (C) 2024 Vben
diff --git a/internal/vite-config/src/utils/env.ts b/internal/vite-config/src/utils/env.ts
index 9cc3784392c..970973c5f7d 100644
--- a/internal/vite-config/src/utils/env.ts
+++ b/internal/vite-config/src/utils/env.ts
@@ -56,24 +56,40 @@ async function loadAndConvertEnv(
match = 'VITE_',
confFiles = getConfFiles(),
): Promise<
- { appTitle: string; port: number } & Partial
+ {
+ appTitle: string;
+ base: string;
+ port: number;
+ } & Partial
> {
const envConfig = await loadEnv(match, confFiles);
- const visualizer = envConfig.visualizer || '';
- const pwa = envConfig.pwa || '';
- const compress = envConfig.VITE_COMPRESS || '';
+ const {
+ VITE_BASE,
+ VITE_COMPRESS,
+ VITE_DEVTOOLS,
+ VITE_GLOB_APP_TITLE,
+ VITE_INJECT_APP_LOADING,
+ VITE_NITRO_MOCK,
+ VITE_PORT,
+ VITE_PWA,
+ VITE_VISUALIZER,
+ } = envConfig;
+ const compress = VITE_COMPRESS || '';
const compressTypes = compress
.split(',')
.filter((item) => item === 'brotli' || item === 'gzip');
return {
- appTitle: envConfig?.VITE_GLOB_APP_TITLE ?? 'Vben Admin',
+ appTitle: VITE_GLOB_APP_TITLE ?? 'Vben Admin',
+ base: VITE_BASE || '/',
compress: !!compress,
compressTypes: compressTypes as ('brotli' | 'gzip')[],
- nitroMock: !!envConfig.VITE_NITRO_MOCK,
- port: Number(envConfig.VITE_PORT) || 5173,
- pwa: !!pwa,
- visualizer: !!visualizer,
+ devtools: VITE_DEVTOOLS === 'true',
+ injectAppLoading: VITE_INJECT_APP_LOADING === 'true',
+ nitroMock: VITE_NITRO_MOCK === 'true',
+ port: Number(VITE_PORT) || 5173,
+ pwa: VITE_PWA === 'true',
+ visualizer: VITE_VISUALIZER === 'true',
};
}
diff --git a/package.json b/package.json
index 267adc4f365..8c68c32e55d 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"turbo",
"vben",
"vue vben admin",
- "vben admin pro",
+ "vue vben admin pro",
+ "vben admin",
"vben pro",
"vue",
"vue admin",
@@ -26,6 +27,8 @@
"scripts": {
"bootstrap": "pnpm install",
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
+ "preview": "turbo preview",
+ "build:analyze": "turbo build:analyze",
"build:docker": "./build-local-docker-image.sh",
"changeset": "pnpm exec changeset",
"check": "pnpm run check:dep && pnpm run check:circular && pnpm run check:type && pnpm run check:cspell",
@@ -35,9 +38,8 @@
"check:type": "turbo run typecheck",
"clean": "vsh clean",
"commit": "czg",
- "dev": "cross-env turbo run dev",
- "dev:ui": "cross-env TURBO_UI=1 turbo run dev",
- "docs:dev": "pnpm -F @vben/website run docs:dev",
+ "dev": "cross-env TURBO_UI=1 turbo run dev",
+ "dev:docs": "pnpm -F @vben/website run docs:dev",
"format": "vsh lint --format",
"lint": "vsh lint",
"postinstall": "turbo run stub",
@@ -45,7 +47,7 @@
"prepare": "is-ci || husky",
"publint": "vsh publint",
"reinstall": "pnpm clean --del-lock && pnpm bootstrap",
- "test": "vitest",
+ "test:unit": "vitest",
"update:deps": " pnpm update --latest --recursive",
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile"
},
@@ -54,7 +56,7 @@
"@changesets/cli": "^2.27.7",
"@ls-lint/ls-lint": "^2.2.3",
"@types/jsdom": "^21.1.7",
- "@types/node": "^20.14.11",
+ "@types/node": "^20.14.12",
"@vben/commitlint-config": "workspace:*",
"@vben/eslint-config": "workspace:*",
"@vben/lint-staged-config": "workspace:*",
@@ -67,16 +69,16 @@
"@vue/test-utils": "^2.4.6",
"cross-env": "^7.0.3",
"cspell": "^8.12.1",
- "husky": "^9.1.1",
+ "husky": "^9.1.3",
"is-ci": "^3.0.1",
"jsdom": "^24.1.1",
"rimraf": "^6.0.1",
"turbo": "^2.0.9",
"typescript": "^5.5.4",
"unbuild": "^2.0.0",
- "vite": "^5.3.4",
+ "vite": "^5.3.5",
"vitest": "^2.0.4",
- "vue-tsc": "^2.0.28"
+ "vue-tsc": "^2.0.29"
},
"engines": {
"node": ">=20",
@@ -87,9 +89,7 @@
"overrides": {
"@ctrl/tinycolor": "^4.1.0",
"clsx": "^2.1.1",
- "eslint": "^8.57.0",
- "vue": "^3.4.33",
- "zx": "^7.2.3"
+ "vue": "^3.4.34"
},
"neverBuiltDependencies": [
"canvas",
@@ -98,7 +98,6 @@
],
"updateConfig": {
"ignoreDependencies": [
- "eslint",
"zx"
]
}
diff --git a/packages/@core/hooks/package.json b/packages/@core/hooks/package.json
index d92327164d2..b9ab34dddb9 100644
--- a/packages/@core/hooks/package.json
+++ b/packages/@core/hooks/package.json
@@ -40,7 +40,7 @@
"@vueuse/core": "^10.11.0",
"radix-vue": "^1.9.2",
"sortablejs": "^1.15.2",
- "vue": "^3.4.33"
+ "vue": "^3.4.34"
},
"devDependencies": {
"@types/sortablejs": "^1.15.8"
diff --git a/packages/@core/preferences/package.json b/packages/@core/preferences/package.json
index cf3385681d6..488c07e45c3 100644
--- a/packages/@core/preferences/package.json
+++ b/packages/@core/preferences/package.json
@@ -31,6 +31,6 @@
"@vben-core/toolkit": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "^10.11.0",
- "vue": "^3.4.33"
+ "vue": "^3.4.34"
}
}
diff --git a/packages/@core/preferences/src/config.ts b/packages/@core/preferences/src/config.ts
index 8971df29ab0..90685eeab60 100644
--- a/packages/@core/preferences/src/config.ts
+++ b/packages/@core/preferences/src/config.ts
@@ -9,14 +9,15 @@ const defaultPreferences: Preferences = {
compact: false,
contentCompact: 'wide',
defaultAvatar:
- 'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/avatar-v1.webp',
+ 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/avatar-v1.webp',
dynamicTitle: true,
enablePreferences: true,
isMobile: false,
layout: 'sidebar-nav',
locale: 'zh-CN',
- loginExpiredMode: 'page',
+ loginExpiredMode: 'modal',
name: 'Vben Admin',
+ watermark: false,
},
breadcrumb: {
enable: true,
@@ -44,8 +45,7 @@ const defaultPreferences: Preferences = {
},
logo: {
enable: true,
- source:
- 'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/logo-v1.webp',
+ source: 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/logo-v1.webp',
},
navigation: {
accordion: true,
@@ -75,6 +75,9 @@ const defaultPreferences: Preferences = {
keepAlive: true,
persist: true,
showIcon: true,
+ showMaximize: true,
+ showMore: true,
+ showRefresh: true,
styleType: 'chrome',
},
theme: {
@@ -94,7 +97,6 @@ const defaultPreferences: Preferences = {
progress: true,
},
widget: {
- aiAssistant: true,
fullscreen: true,
globalSearch: true,
languageToggle: true,
diff --git a/packages/@core/preferences/src/constants.ts b/packages/@core/preferences/src/constants.ts
index 5a5100001f8..354a58968b5 100644
--- a/packages/@core/preferences/src/constants.ts
+++ b/packages/@core/preferences/src/constants.ts
@@ -1,12 +1,4 @@
-import type {
- BuiltinThemeType,
- SupportedLanguagesType,
-} from '@vben-core/typings';
-
-interface Language {
- key: SupportedLanguagesType;
- text: string;
-}
+import type { BuiltinThemeType } from '@vben-core/typings';
interface BuiltinThemePreset {
color: string;
@@ -15,25 +7,11 @@ interface BuiltinThemePreset {
type: BuiltinThemeType;
}
-/**
- * Supported languages
- */
-const SUPPORT_LANGUAGES: Language[] = [
- {
- key: 'zh-CN',
- text: '简体中文',
- },
- {
- key: 'en-US',
- text: 'English',
- },
-];
-
const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
- {
- color: 'hsl(231 98% 65%)',
- type: 'default',
- },
+ // {
+ // color: 'hsl(231 98% 65%)',
+ // type: 'default',
+ // },
{
color: 'hsl(245 82% 67%)',
type: 'violet',
@@ -102,6 +80,6 @@ const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
export const COLOR_PRESETS = [...BUILT_IN_THEME_PRESETS].slice(0, 7);
-export { BUILT_IN_THEME_PRESETS, SUPPORT_LANGUAGES };
+export { BUILT_IN_THEME_PRESETS };
export type { BuiltinThemePreset };
diff --git a/packages/@core/preferences/src/preferences.test.ts b/packages/@core/preferences/src/preferences.test.ts
index 1f26c321cd9..786de946ca5 100644
--- a/packages/@core/preferences/src/preferences.test.ts
+++ b/packages/@core/preferences/src/preferences.test.ts
@@ -24,17 +24,6 @@ describe('preferences', () => {
preferenceManager = new PreferenceManager();
});
- it('initPreferences should initialize preferences with overrides and namespace', async () => {
- const overrides = { theme: { colorPrimary: 'hsl(231 98% 65%)' } };
- const namespace = 'testNamespace';
-
- await preferenceManager.initPreferences({ namespace, overrides });
-
- expect(preferenceManager.getPreferences().theme.colorPrimary).toBe(
- overrides.theme.colorPrimary,
- );
- });
-
it('loads default preferences if no saved preferences found', () => {
const preferences = preferenceManager.getPreferences();
expect(preferences).toEqual(defaultPreferences);
diff --git a/packages/@core/preferences/src/preferences.ts b/packages/@core/preferences/src/preferences.ts
index 9e7f70e6398..42cf9df86a2 100644
--- a/packages/@core/preferences/src/preferences.ts
+++ b/packages/@core/preferences/src/preferences.ts
@@ -41,7 +41,7 @@ class PreferenceManager {
this.savePreferences = useDebounceFn(
(preference: Preferences) => this._savePreferences(preference),
- 100,
+ 150,
);
}
diff --git a/packages/@core/preferences/src/types.ts b/packages/@core/preferences/src/types.ts
index ee388808c2d..e71d49a56b7 100644
--- a/packages/@core/preferences/src/types.ts
+++ b/packages/@core/preferences/src/types.ts
@@ -10,11 +10,12 @@ import type {
LoginExpiredModeType,
NavigationStyleType,
PageTransitionType,
- SupportedLanguagesType,
TabsStyleType,
ThemeModeType,
} from '@vben-core/typings';
+type SupportedLanguagesType = 'en-US' | 'zh-CN';
+
interface AppPreferences {
/** 权限模式 */
accessMode: AccessModeType;
@@ -44,6 +45,10 @@ interface AppPreferences {
loginExpiredMode: LoginExpiredModeType;
/** 应用名 */
name: string;
+ /**
+ * @zh_CN 是否开启水印
+ */
+ watermark: boolean;
}
interface BreadcrumbPreferences {
@@ -149,6 +154,12 @@ interface TabbarPreferences {
persist: boolean;
/** 是否开启多标签页图标 */
showIcon: boolean;
+ /** 显示最大化按钮 */
+ showMaximize: boolean;
+ /** 显示更多按钮 */
+ showMore: boolean;
+ /** 显示刷新按钮 */
+ showRefresh: boolean;
/** 标签页风格 */
styleType: TabsStyleType;
}
@@ -184,8 +195,6 @@ interface TransitionPreferences {
}
interface WidgetPreferences {
- /** 是否开启vben助手部件 */
- aiAssistant: boolean;
/** 是否启用全屏部件 */
fullscreen: boolean;
/** 是否启用全局搜索部件 */
@@ -249,6 +258,7 @@ export type {
PreferencesKeys,
ShortcutKeyPreferences,
SidebarPreferences,
+ SupportedLanguagesType,
TabbarPreferences,
ThemePreferences,
TransitionPreferences,
diff --git a/packages/@core/preferences/src/update-css-variables.ts b/packages/@core/preferences/src/update-css-variables.ts
index 9f1823f818f..4b3c258dc36 100644
--- a/packages/@core/preferences/src/update-css-variables.ts
+++ b/packages/@core/preferences/src/update-css-variables.ts
@@ -5,7 +5,7 @@ import {
generatorColorVariables,
} from '@vben-core/toolkit';
-import { BUILT_IN_THEME_PRESETS } from './constants';
+import { BUILT_IN_THEME_PRESETS, type BuiltinThemePreset } from './constants';
/**
* 更新主题的 CSS 变量以及其他 CSS 变量
@@ -37,9 +37,13 @@ function updateCSSVariables(preferences: Preferences) {
}
// 获取当前的内置主题
- const currentBuiltType = BUILT_IN_THEME_PRESETS.find(
- (item) => item.type === builtinType,
- );
+ const currentBuiltType = [
+ {
+ color: preferences.theme.colorPrimary,
+ type: 'default',
+ } as BuiltinThemePreset,
+ ...BUILT_IN_THEME_PRESETS,
+ ].find((item) => item.type === builtinType);
let builtinTypeColorPrimary: string | undefined = '';
diff --git a/packages/@core/shared/constants/src/vben.ts b/packages/@core/shared/constants/src/vben.ts
index dfad89cb369..8143c92f796 100644
--- a/packages/@core/shared/constants/src/vben.ts
+++ b/packages/@core/shared/constants/src/vben.ts
@@ -12,7 +12,7 @@ const VBEN_DOC_URL = 'https://doc.vben.pro';
* @zh_CN Vben Logo
*/
const VBEN_LOGO_URL =
- 'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/logo-v1.webp';
+ 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/logo-v1.webp';
/**
* @zh_CN Vben Admin 首页地址
diff --git a/packages/@core/shared/design/src/css/global.css b/packages/@core/shared/design/src/css/global.css
index c6d95c98df4..5a154d24f51 100644
--- a/packages/@core/shared/design/src/css/global.css
+++ b/packages/@core/shared/design/src/css/global.css
@@ -85,17 +85,21 @@
/* 只有非mac下才进行调整,mac下使用默认滚动条 */
html:not([data-platform='macOs']) {
- *::-webkit-scrollbar {
+ ::-webkit-scrollbar {
@apply h-[1px] w-[10px];
}
- *::-webkit-scrollbar-thumb {
+ ::-webkit-scrollbar-thumb {
@apply bg-border rounded-sm border-none;
}
- *::-webkit-scrollbar-track {
+ ::-webkit-scrollbar-track {
@apply rounded-sm border-none bg-transparent shadow-none;
}
+
+ ::-webkit-scrollbar-button {
+ @apply hidden;
+ }
}
}
diff --git a/packages/@core/shared/icons/package.json b/packages/@core/shared/icons/package.json
index f22c4f0dc18..c3927882857 100644
--- a/packages/@core/shared/icons/package.json
+++ b/packages/@core/shared/icons/package.json
@@ -35,7 +35,7 @@
},
"dependencies": {
"@iconify/vue": "^4.1.2",
- "lucide-vue-next": "^0.414.0",
- "vue": "^3.4.33"
+ "lucide-vue-next": "^0.416.0",
+ "vue": "^3.4.34"
}
}
diff --git a/packages/@core/shared/toolkit/package.json b/packages/@core/shared/toolkit/package.json
index b6e50422b01..05f72e783cf 100644
--- a/packages/@core/shared/toolkit/package.json
+++ b/packages/@core/shared/toolkit/package.json
@@ -36,7 +36,7 @@
},
"dependencies": {
"@ctrl/tinycolor": "^4.1.0",
- "@vue/shared": "^3.4.33",
+ "@vue/shared": "^3.4.34",
"clsx": "^2.1.1",
"defu": "^6.1.4",
"lodash.clonedeep": "^4.5.0",
diff --git a/packages/@core/shared/typings/package.json b/packages/@core/shared/typings/package.json
index 3ee0ce48789..28e5e4cc898 100644
--- a/packages/@core/shared/typings/package.json
+++ b/packages/@core/shared/typings/package.json
@@ -38,7 +38,7 @@
}
},
"dependencies": {
- "vue": "^3.4.33",
+ "vue": "^3.4.34",
"vue-router": "^4.4.0"
}
}
diff --git a/packages/@core/shared/typings/src/app.d.ts b/packages/@core/shared/typings/src/app.d.ts
index 5b34e2fc0f2..0db306ddf78 100644
--- a/packages/@core/shared/typings/src/app.d.ts
+++ b/packages/@core/shared/typings/src/app.d.ts
@@ -1,5 +1,3 @@
-type SupportedLanguagesType = 'en-US' | 'zh-CN';
-
type LayoutType =
| 'full-content'
| 'header-nav'
@@ -26,7 +24,8 @@ type BuiltinThemeType =
| 'stone'
| 'violet'
| 'yellow'
- | 'zinc';
+ | 'zinc'
+ | (Record & string);
type ContentCompactType = 'compact' | 'wide';
@@ -34,20 +33,52 @@ type LayoutHeaderModeType = 'auto' | 'auto-scroll' | 'fixed' | 'static';
/**
* 登录过期模式
- * 'modal' 弹窗模式 | 'page' 页面模式
+ * modal 弹窗模式
+ * page 页面模式
*/
type LoginExpiredModeType = 'modal' | 'page';
+/**
+ * 面包屑样式
+ * background 背景
+ * normal 默认
+ */
type BreadcrumbStyleType = 'background' | 'normal';
-type AccessModeType = 'allow-all' | 'backend' | 'frontend';
+/**
+ * 权限模式
+ * backend 后端权限模式
+ * frontend 前端权限模式
+ */
+type AccessModeType = 'backend' | 'frontend';
+/**
+ * 导航风格
+ * plain 朴素
+ * rounded 圆润
+ */
type NavigationStyleType = 'plain' | 'rounded';
+/**
+ * 标签栏风格
+ * brisk 轻快
+ * card 卡片
+ * chrome 谷歌
+ * plain 朴素
+ */
type TabsStyleType = 'brisk' | 'card' | 'chrome' | 'plain';
+/**
+ * 页面切换动画
+ */
type PageTransitionType = 'fade' | 'fade-down' | 'fade-slide' | 'fade-up';
+/**
+ * 页面切换动画
+ * panel-center 居中布局
+ * panel-left 居左布局
+ * panel-right 居右布局
+ */
type AuthPageLayoutType = 'panel-center' | 'panel-left' | 'panel-right';
export type {
@@ -61,7 +92,6 @@ export type {
LoginExpiredModeType,
NavigationStyleType,
PageTransitionType,
- SupportedLanguagesType,
TabsStyleType,
ThemeModeType,
};
diff --git a/packages/@core/shared/typings/src/menu-record.ts b/packages/@core/shared/typings/src/menu-record.ts
index c15d3bcfbda..0ea0a0b8ce7 100644
--- a/packages/@core/shared/typings/src/menu-record.ts
+++ b/packages/@core/shared/typings/src/menu-record.ts
@@ -28,6 +28,10 @@ interface MenuRecordBadgeRaw {
* 菜单原始对象
*/
interface MenuRecordRaw extends MenuRecordBadgeRaw {
+ /**
+ * 激活时的图标名
+ */
+ activeIcon?: string;
/**
* 子菜单
*/
diff --git a/packages/@core/shared/typings/src/vue-router.d.ts b/packages/@core/shared/typings/src/vue-router.d.ts
index 23c0274fa3a..65a3d503dc6 100644
--- a/packages/@core/shared/typings/src/vue-router.d.ts
+++ b/packages/@core/shared/typings/src/vue-router.d.ts
@@ -3,6 +3,10 @@ import type { Router, RouteRecordRaw } from 'vue-router';
import type { Component } from 'vue';
interface RouteMeta {
+ /**
+ * 激活图标(菜单/tab)
+ */
+ activeIcon?: string;
/**
* 当前激活的菜单,有时候不想激活现有菜单,需要激活父级菜单时使用
* @default false
@@ -13,6 +17,11 @@ interface RouteMeta {
* @default false
*/
affixTab?: boolean;
+ /**
+ * 固定标签页的顺序
+ * @default 0
+ */
+ affixTabOrder?: number;
/**
* 需要特定的角色标识才可以访问
* @default []
@@ -56,10 +65,6 @@ interface RouteMeta {
* @default false
*/
hideInTab?: boolean;
- /**
- * 路由跳转地址
- */
- href?: string;
/**
* 图标(菜单/tab)
*/
@@ -87,7 +92,7 @@ interface RouteMeta {
loaded?: boolean;
/**
* 标签页最大打开数量
- * @default false
+ * @default -1
*/
maxNumOfOpenTab?: number;
/**
@@ -126,5 +131,6 @@ export type {
ComponentRecordType,
GenerateMenuAndRoutesOptions,
RouteMeta,
+ RouteRecordRaw,
RouteRecordStringComponent,
};
diff --git a/packages/@core/ui-kit/layout-ui/package.json b/packages/@core/ui-kit/layout-ui/package.json
index adea0b76896..41a8050067c 100644
--- a/packages/@core/ui-kit/layout-ui/package.json
+++ b/packages/@core/ui-kit/layout-ui/package.json
@@ -42,6 +42,6 @@
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "^10.11.0",
- "vue": "^3.4.33"
+ "vue": "^3.4.34"
}
}
diff --git a/packages/@core/ui-kit/menu-ui/package.json b/packages/@core/ui-kit/menu-ui/package.json
index d0b1b8c2ac3..68e15dde317 100644
--- a/packages/@core/ui-kit/menu-ui/package.json
+++ b/packages/@core/ui-kit/menu-ui/package.json
@@ -43,6 +43,6 @@
"@vben-core/toolkit": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "^10.11.0",
- "vue": "^3.4.33"
+ "vue": "^3.4.34"
}
}
diff --git a/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue b/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue
index cf017a8d3c1..76805bc3cc3 100644
--- a/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue
+++ b/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue
@@ -26,6 +26,10 @@ const subMenu = useSubMenuContext();
const { parentMenu, parentPaths } = useMenu();
const active = computed(() => props.path === rootMenu?.activePath);
+const menuIcon = computed(() =>
+ active.value ? props.activeIcon || props.icon : props.icon,
+);
+
const isTopLevelMenuItem = computed(
() => parentMenu.value?.type.name === 'Menu',
);
@@ -94,7 +98,7 @@ onBeforeUnmount(() => {
>
-
+
@@ -109,7 +113,7 @@ onBeforeUnmount(() => {
class="right-2"
v-bind="props"
/>
-
+
diff --git a/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue b/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue
index 8efb018df79..fbd9986c2e2 100644
--- a/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue
+++ b/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue
@@ -12,7 +12,7 @@ defineOptions({
name: 'NormalMenu',
});
-withDefaults(defineProps(), {
+const props = withDefaults(defineProps(), {
activePath: '',
collapse: false,
menus: () => [],
@@ -25,6 +25,12 @@ const emit = defineEmits<{
}>();
const { b, e, is } = useNamespace('normal-menu');
+
+function menuIcon(menu: MenuRecordRaw) {
+ return props.activePath === menu.path
+ ? menu.activeIcon || menu.icon
+ : menu.icon;
+}
@@ -44,7 +50,8 @@ const { b, e, is } = useNamespace('normal-menu');
@click="() => emit('select', menu)"
@mouseenter="() => emit('enter', menu)"
>
-
+
+
{{ menu.name }}
diff --git a/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue b/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue
index 547456d0256..334e9c3f716 100644
--- a/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue
+++ b/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue
@@ -172,6 +172,10 @@ function handleMouseleave(deepDispatch = false) {
}
}
+const menuIcon = computed(() =>
+ active.value ? props.activeIcon || props.icon : props.icon,
+);
+
const item = reactive({
active,
parentPaths,
@@ -215,7 +219,7 @@ onBeforeUnmount(() => {
{
{
Reflect.has(menu, 'children') && !!menu.children && menu.children.length > 0
);
});
+
+// function menuIcon(menu: MenuRecordRaw) {
+// return props.activePath === menu.path
+// ? menu.activeIcon || menu.icon
+// : menu.icon;
+// }