diff --git a/apps/web-antd/src/apis/modules/user.ts b/apps/web-antd/src/apis/modules/user.ts index c39090d1..75d515b4 100644 --- a/apps/web-antd/src/apis/modules/user.ts +++ b/apps/web-antd/src/apis/modules/user.ts @@ -1,6 +1,7 @@ -import type { UserApiType } from '@/apis/types'; import type { UserInfo } from '@vben/types'; +import type { UserApiType } from '@/apis/types'; + import { request } from '@/forward'; /** diff --git a/apps/web-antd/src/app.vue b/apps/web-antd/src/app.vue index 692545e0..bce3c2aa 100644 --- a/apps/web-antd/src/app.vue +++ b/apps/web-antd/src/app.vue @@ -1,13 +1,14 @@ + + diff --git a/internal/lint-configs/eslint-config/package.json b/internal/lint-configs/eslint-config/package.json index 8706d4c5..877c7f75 100644 --- a/internal/lint-configs/eslint-config/package.json +++ b/internal/lint-configs/eslint-config/package.json @@ -41,9 +41,9 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-i": "^2.29.1", - "eslint-plugin-jsdoc": "^48.2.7", + "eslint-plugin-jsdoc": "^48.2.8", "eslint-plugin-jsonc": "^2.16.0", - "eslint-plugin-n": "^17.7.0", + "eslint-plugin-n": "^17.8.0", "eslint-plugin-no-only-tests": "^3.1.0", "eslint-plugin-perfectionist": "^2.10.0", "eslint-plugin-prettier": "^5.1.3", diff --git a/internal/lint-configs/eslint-config/src/configs/perfectionist.ts b/internal/lint-configs/eslint-config/src/configs/perfectionist.ts index 1c45d329..86814fa4 100644 --- a/internal/lint-configs/eslint-config/src/configs/perfectionist.ts +++ b/internal/lint-configs/eslint-config/src/configs/perfectionist.ts @@ -22,31 +22,38 @@ export async function perfectionist(): Promise { { 'custom-groups': { type: { - vben: 'vue', - vue: ['vue', 'vue-*', '@vue*'], + vben: 'vben', + vue: 'vue', }, value: { - vben: 'vben', - vue: ['@vben-*', '@vben-core/*'], + vben: ['@vben*', '@vben/*', '@vben-core/*'], + vue: ['vue', 'vue-*', '@vue*'], }, }, groups: [ - 'side-effect', - 'type', - 'vue', + ['external-type', 'builtin-type', 'type'], + ['parent-type', 'sibling-type', 'index-type'], + ['internal-type'], 'builtin', + 'vue', 'vben', 'external', - 'internal-type', 'internal', ['parent', 'sibling', 'index'], + 'side-effect', + 'side-effect-style', 'style', 'object', 'unknown', - 'type', - ['parent-type', 'sibling-type', 'index-type'], ], - 'internal-pattern': ['@/layouts/**', '@/router/**', '@/views/**'], + 'internal-pattern': [ + '@/layouts/**', + '@/apis/**', + '@/forward/**', + '@/router/**', + '@/views/**', + '#/**', + ], 'newlines-between': 'always', order: 'asc', type: 'natural', diff --git a/internal/tailwind-config/package.json b/internal/tailwind-config/package.json index 61fdb67c..9c21f576 100644 --- a/internal/tailwind-config/package.json +++ b/internal/tailwind-config/package.json @@ -54,12 +54,12 @@ "@tailwindcss/nesting": "0.0.0-insiders.565cd3e", "@tailwindcss/typography": "^0.5.13", "autoprefixer": "^10.4.19", - "cssnano": "^7.0.1", + "cssnano": "^7.0.2", "postcss": "^8.4.38", "postcss-antd-fixes": "^0.2.0", "postcss-import": "^16.1.0", "postcss-preset-env": "^9.5.14", - "tailwindcss": "^3.4.3", + "tailwindcss": "^3.4.4", "tailwindcss-animate": "^1.0.7" }, "devDependencies": { diff --git a/internal/tailwind-config/src/index.ts b/internal/tailwind-config/src/index.ts index 0c2e85dd..ef2ee70d 100644 --- a/internal/tailwind-config/src/index.ts +++ b/internal/tailwind-config/src/index.ts @@ -2,10 +2,11 @@ import type { Config } from 'tailwindcss'; import path from 'node:path'; +import { fs, getPackagesSync } from '@vben/node-utils'; + import { addDynamicIconSelectors } from '@iconify/tailwind'; import formsPlugin from '@tailwindcss/forms'; import typographyPlugin from '@tailwindcss/typography'; -import { fs, getPackagesSync } from '@vben/node-utils'; import animate from 'tailwindcss-animate'; import { enterAnimationPlugin } from './plugins/entry'; diff --git a/internal/vite-config/src/config/application.ts b/internal/vite-config/src/config/application.ts index 770a8370..52254196 100644 --- a/internal/vite-config/src/config/application.ts +++ b/internal/vite-config/src/config/application.ts @@ -1,5 +1,7 @@ import type { UserConfig } from 'vite'; +import type { DefineApplicationOptions } from '../typing'; + import { resolve } from 'node:path'; import { defineConfig, loadEnv, mergeConfig } from 'vite'; @@ -7,8 +9,6 @@ import { defineConfig, loadEnv, mergeConfig } from 'vite'; import { getApplicationConditionPlugins } from '../plugins'; import { getCommonConfig } from './common'; -import type { DefineApplicationOptions } from '../typing'; - function defineApplicationConfig(options: DefineApplicationOptions = {}) { return defineConfig(async ({ command, mode }) => { const { application = {}, vite = {} } = options; diff --git a/internal/vite-config/src/config/index.ts b/internal/vite-config/src/config/index.ts index 547ceffa..6cded559 100644 --- a/internal/vite-config/src/config/index.ts +++ b/internal/vite-config/src/config/index.ts @@ -1,11 +1,11 @@ +import type { DefineConfig } from '../typing'; + import { existsSync } from 'node:fs'; import { join } from 'node:path'; import { defineApplicationConfig } from './application'; import { defineLibraryConfig } from './library'; -import type { DefineConfig } from '../typing'; - export * from './application'; export * from './library'; diff --git a/internal/vite-config/src/config/library.ts b/internal/vite-config/src/config/library.ts index 18e74d01..fca61d14 100644 --- a/internal/vite-config/src/config/library.ts +++ b/internal/vite-config/src/config/library.ts @@ -1,13 +1,14 @@ import type { UserConfig } from 'vite'; +import type { DefineLibraryOptions } from '../typing'; + import { readPackageJSON } from '@vben/node-utils'; + import { defineConfig, mergeConfig } from 'vite'; import { getLibraryConditionPlugins } from '../plugins'; import { getCommonConfig } from './common'; -import type { DefineLibraryOptions } from '../typing'; - function defineLibraryConfig(options: DefineLibraryOptions = {}) { return defineConfig(async ({ command, mode }) => { const root = process.cwd(); diff --git a/internal/vite-config/src/plugins/extra-app-config.ts b/internal/vite-config/src/plugins/extra-app-config.ts index acea1464..13d98ec9 100644 --- a/internal/vite-config/src/plugins/extra-app-config.ts +++ b/internal/vite-config/src/plugins/extra-app-config.ts @@ -3,6 +3,7 @@ import { generatorContentHash, readPackageJSON, } from '@vben/node-utils'; + import { type PluginOption } from 'vite'; import { getEnvConfig } from '../utils/env'; diff --git a/internal/vite-config/src/plugins/index.ts b/internal/vite-config/src/plugins/index.ts index 553f7262..49495ae0 100644 --- a/internal/vite-config/src/plugins/index.ts +++ b/internal/vite-config/src/plugins/index.ts @@ -1,9 +1,17 @@ import type { PluginOption } from 'vite'; +import type { + ApplicationPluginOptions, + CommonPluginOptions, + ConditionPlugin, + LibraryPluginOptions, +} from '../typing'; + import { join } from 'node:path'; -import viteVueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'; import { getPackage } 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'; import { visualizer as viteVisualizerPlugin } from 'rollup-plugin-visualizer'; @@ -19,13 +27,6 @@ import { viteExtraAppConfigPlugin } from './extra-app-config'; import { viteImportMapPlugin } from './importmap'; import { viteInjectAppLoadingPlugin } from './inject-app-loading'; -import type { - ApplicationPluginOptions, - CommonPluginOptions, - ConditionPlugin, - LibraryPluginOptions, -} from '../typing'; - /** * 获取条件成立的 vite 插件 * @param conditionPlugins 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 f5d8f252..7aedc3e9 100644 --- a/internal/vite-config/src/plugins/inject-app-loading/index.ts +++ b/internal/vite-config/src/plugins/inject-app-loading/index.ts @@ -2,6 +2,7 @@ import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { fs } from '@vben/node-utils'; + import { type PluginOption } from 'vite'; /** diff --git a/internal/vite-config/src/plugins/inject-app-loading/loading-antd.html b/internal/vite-config/src/plugins/inject-app-loading/loading-antd.html index cf7e9676..44bdd91a 100644 --- a/internal/vite-config/src/plugins/inject-app-loading/loading-antd.html +++ b/internal/vite-config/src/plugins/inject-app-loading/loading-antd.html @@ -29,7 +29,7 @@ .loading.hidden { visibility: hidden; opacity: 0; - transition: all 1s ease-out; + transition: all 0.6s ease-out; } .loading .dots { diff --git a/internal/vite-config/src/plugins/inject-app-loading/loading.html b/internal/vite-config/src/plugins/inject-app-loading/loading.html index a947cd82..8833fb3c 100644 --- a/internal/vite-config/src/plugins/inject-app-loading/loading.html +++ b/internal/vite-config/src/plugins/inject-app-loading/loading.html @@ -23,7 +23,7 @@ .loading.hidden { visibility: hidden; opacity: 0; - transition: all 1s ease-out; + transition: all 0.6s ease-out; } .dark .loading { diff --git a/internal/vite-config/src/utils/env.ts b/internal/vite-config/src/utils/env.ts index c08c3cfc..ee5adc24 100644 --- a/internal/vite-config/src/utils/env.ts +++ b/internal/vite-config/src/utils/env.ts @@ -1,6 +1,7 @@ import { join } from 'node:path'; import { fs } from '@vben/node-utils'; + import dotenv from 'dotenv'; /** diff --git a/package.json b/package.json index 62456c28..49fdc979 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@changesets/cli": "^2.27.5", "@ls-lint/ls-lint": "^2.2.3", "@types/jsdom": "^21.1.7", - "@types/node": "^20.14.1", + "@types/node": "^20.14.2", "@vben/commitlint-config": "workspace:*", "@vben/eslint-config": "workspace:*", "@vben/lint-staged-config": "workspace:*", @@ -76,7 +76,7 @@ "node": ">=18.7.0", "pnpm": ">=8.5.0" }, - "packageManager": "pnpm@9.1.4", + "packageManager": "pnpm@9.2.0", "pnpm": { "overrides": { "@ctrl/tinycolor": "4.1.0", diff --git a/packages/@vben-core/forward/helpers/src/generator-menus.ts b/packages/@vben-core/forward/helpers/src/generator-menus.ts index 1a590175..ae0d2ca6 100644 --- a/packages/@vben-core/forward/helpers/src/generator-menus.ts +++ b/packages/@vben-core/forward/helpers/src/generator-menus.ts @@ -1,7 +1,7 @@ import type { ExRouteRecordRaw, MenuRecordRaw } from '@vben-core/typings'; +import type { RouteRecordRaw, Router } from 'vue-router'; import { mapTree } from '@vben-core/toolkit'; -import type { RouteRecordRaw, Router } from 'vue-router'; /** * 根据 routes 生成菜单列表 diff --git a/packages/@vben-core/forward/helpers/src/generator-routes.ts b/packages/@vben-core/forward/helpers/src/generator-routes.ts index c1e2674b..5e787566 100644 --- a/packages/@vben-core/forward/helpers/src/generator-routes.ts +++ b/packages/@vben-core/forward/helpers/src/generator-routes.ts @@ -1,16 +1,30 @@ -import { filterTree } from '@vben-core/toolkit'; import type { RouteRecordRaw } from 'vue-router'; + +import { filterTree, mapTree } from '@vben-core/toolkit'; /** * 动态生成路由 */ async function generatorRoutes( routes: RouteRecordRaw[], roles: string[], + forbiddenPage?: RouteRecordRaw['component'], ): Promise { // 根据角色标识过滤路由表,判断当前用户是否拥有指定权限 - return filterTree(routes, (route) => { + const finalRoutes = filterTree(routes, (route) => { return hasVisible(route) && hasAuthority(route, roles); }); + + if (!forbiddenPage) { + return finalRoutes; + } + + // 如果有禁止访问的页面,将禁止访问的页面替换为403页面 + return mapTree(finalRoutes, (route) => { + if (menuHasVisibleWithForbidden(route)) { + route.component = forbiddenPage; + } + return route; + }); } /** @@ -24,9 +38,10 @@ function hasAuthority(route: RouteRecordRaw, access: string[]) { if (!authority) { return true; } - return access.some((value) => { - return authority.includes(value); - }); + return ( + access.some((value) => authority.includes(value)) || + menuHasVisibleWithForbidden(route) + ); } /** @@ -37,4 +52,12 @@ function hasVisible(route?: RouteRecordRaw) { return !route?.meta?.hideInMenu; } +/** + * 判断路由是否在菜单中显示,但是访问会被重定向到403 + * @param route + */ +function menuHasVisibleWithForbidden(route: RouteRecordRaw) { + return !!route.meta?.menuVisibleWithForbidden; +} + export { generatorRoutes, hasAuthority, hasVisible }; diff --git a/packages/@vben-core/forward/helpers/src/merge-route-modules.test.ts b/packages/@vben-core/forward/helpers/src/merge-route-modules.test.ts index 591ffd26..40d9243c 100644 --- a/packages/@vben-core/forward/helpers/src/merge-route-modules.test.ts +++ b/packages/@vben-core/forward/helpers/src/merge-route-modules.test.ts @@ -1,11 +1,11 @@ import type { RouteRecordRaw } from 'vue-router'; +import type { RouteModuleType } from './merge-route-modules'; + import { describe, expect, it } from 'vitest'; import { mergeRouteModules } from './merge-route-modules'; -import type { RouteModuleType } from './merge-route-modules'; - describe('mergeRouteModules', () => { it('should merge route modules correctly', () => { const routeModules: Record = { diff --git a/packages/@vben-core/forward/preferences/src/index.ts b/packages/@vben-core/forward/preferences/src/index.ts index 75305555..011b814e 100644 --- a/packages/@vben-core/forward/preferences/src/index.ts +++ b/packages/@vben-core/forward/preferences/src/index.ts @@ -1,9 +1,9 @@ import type { Flatten } from '@vben-core/typings'; -import { preferencesManager } from './preferences'; - import type { Preferences } from './types'; +import { preferencesManager } from './preferences'; + // 偏好设置(带有层级关系) const preferences: Preferences = preferencesManager.getPreferences(); diff --git a/packages/@vben-core/forward/preferences/src/preferences.ts b/packages/@vben-core/forward/preferences/src/preferences.ts index b835bee7..19b27bda 100644 --- a/packages/@vben-core/forward/preferences/src/preferences.ts +++ b/packages/@vben-core/forward/preferences/src/preferences.ts @@ -4,6 +4,10 @@ import type { FlattenObjectKeys, } from '@vben-core/typings'; +import type { Preferences } from './types'; + +import { markRaw, reactive, watch } from 'vue'; + import { StorageManager } from '@vben-core/cache'; import { flattenObject, nestedObject } from '@vben-core/helpers'; import { convertToHslCssVar, merge } from '@vben-core/toolkit'; @@ -14,12 +18,9 @@ import { useCssVar, useDebounceFn, } from '@vueuse/core'; -import { markRaw, reactive, watch } from 'vue'; import { defaultPreferences } from './config'; -import type { Preferences } from './types'; - const STORAGE_KEY = 'preferences'; const STORAGE_KEY_LOCALE = `${STORAGE_KEY}-locale`; const STORAGE_KEY_THEME = `${STORAGE_KEY}-theme`; diff --git a/packages/@vben-core/forward/preferences/src/use-preferences.ts b/packages/@vben-core/forward/preferences/src/use-preferences.ts index 5b4ff90b..21fad0ad 100644 --- a/packages/@vben-core/forward/preferences/src/use-preferences.ts +++ b/packages/@vben-core/forward/preferences/src/use-preferences.ts @@ -1,7 +1,7 @@ -import { diff } from '@vben-core/toolkit'; - import { computed } from 'vue'; +import { diff } from '@vben-core/toolkit'; + import { isDarkTheme, preferencesManager } from './preferences'; function usePreferences() { diff --git a/packages/@vben-core/forward/request/src/request-client/request-client.ts b/packages/@vben-core/forward/request/src/request-client/request-client.ts index 3e8554bf..3d85c957 100644 --- a/packages/@vben-core/forward/request/src/request-client/request-client.ts +++ b/packages/@vben-core/forward/request/src/request-client/request-client.ts @@ -6,6 +6,8 @@ import type { InternalAxiosRequestConfig, } from 'axios'; +import type { MakeAuthorizationFn, RequestClientOptions } from './types'; + import { merge } from '@vben-core/toolkit'; import axios from 'axios'; @@ -15,8 +17,6 @@ import { FileDownloader } from './modules/downloader'; import { InterceptorManager } from './modules/interceptor'; import { FileUploader } from './modules/uploader'; -import type { MakeAuthorizationFn, RequestClientOptions } from './types'; - class RequestClient { private instance: AxiosInstance; private makeAuthorization: MakeAuthorizationFn | undefined; diff --git a/packages/@vben-core/forward/stores/src/modules/access.ts b/packages/@vben-core/forward/stores/src/modules/access.ts index 34fe3571..ff1a0ca5 100644 --- a/packages/@vben-core/forward/stores/src/modules/access.ts +++ b/packages/@vben-core/forward/stores/src/modules/access.ts @@ -1,5 +1,4 @@ import type { MenuRecordRaw } from '@vben-core/typings'; - import type { RouteRecordRaw } from 'vue-router'; import { acceptHMRUpdate, defineStore } from 'pinia'; diff --git a/packages/@vben-core/forward/stores/src/modules/tabs.test.ts b/packages/@vben-core/forward/stores/src/modules/tabs.test.ts index 255c1115..9cb17ce6 100644 --- a/packages/@vben-core/forward/stores/src/modules/tabs.test.ts +++ b/packages/@vben-core/forward/stores/src/modules/tabs.test.ts @@ -1,6 +1,7 @@ +import { createRouter, createWebHistory } from 'vue-router'; + import { createPinia, setActivePinia } from 'pinia'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { createRouter, createWebHistory } from 'vue-router'; import { useTabsStore } from './tabs'; diff --git a/packages/@vben-core/forward/stores/src/modules/tabs.ts b/packages/@vben-core/forward/stores/src/modules/tabs.ts index 01ad1884..d9e962f8 100644 --- a/packages/@vben-core/forward/stores/src/modules/tabs.ts +++ b/packages/@vben-core/forward/stores/src/modules/tabs.ts @@ -1,10 +1,12 @@ -import { startProgress, stopProgress } from '@vben-core/toolkit'; -import { TabItem } from '@vben-core/typings'; import type { RouteRecordNormalized, Router } from 'vue-router'; -import { acceptHMRUpdate, defineStore } from 'pinia'; import { toRaw } from 'vue'; +import { startProgress, stopProgress } from '@vben-core/toolkit'; +import { TabItem } from '@vben-core/typings'; + +import { acceptHMRUpdate, defineStore } from 'pinia'; + /** * @zh_CN 克隆路由,防止路由被修改 * @param route diff --git a/packages/@vben-core/shared/iconify/src/factory.ts b/packages/@vben-core/shared/iconify/src/factory.ts index 09d3ba46..b4323cd3 100644 --- a/packages/@vben-core/shared/iconify/src/factory.ts +++ b/packages/@vben-core/shared/iconify/src/factory.ts @@ -1,6 +1,7 @@ -import { Icon } from '@iconify/vue'; import { defineComponent, h } from 'vue'; +import { Icon } from '@iconify/vue'; + function createIcon(name: string) { return defineComponent({ setup(props, { attrs }) { diff --git a/packages/@vben-core/shared/typings/src/vue-router.d.ts b/packages/@vben-core/shared/typings/src/vue-router.d.ts index 47787eb7..e1d4b8f2 100644 --- a/packages/@vben-core/shared/typings/src/vue-router.d.ts +++ b/packages/@vben-core/shared/typings/src/vue-router.d.ts @@ -72,6 +72,10 @@ interface RouteMeta { * 路由是否已经加载过 */ loaded?: boolean; + /** + * 菜单可以看到,但是访问会被重定向到403 + */ + menuVisibleWithForbidden?: boolean; /** * 用于路由->菜单排序 */ @@ -80,6 +84,7 @@ interface RouteMeta { * 外链-跳转路径 */ target?: string; + /** * 标题名称 */ diff --git a/packages/@vben-core/shared/typings/vue-router.d.ts b/packages/@vben-core/shared/typings/vue-router.d.ts index abe9f926..239f315e 100644 --- a/packages/@vben-core/shared/typings/vue-router.d.ts +++ b/packages/@vben-core/shared/typings/vue-router.d.ts @@ -1,7 +1,7 @@ -import 'vue-router'; - import type { RouteMeta as IRouteMeta } from '@vben-core/typings'; +import 'vue-router'; + declare module 'vue-router' { interface RouteMeta extends IRouteMeta {} } diff --git a/packages/@vben-core/uikit/layout-ui/src/components/layout-content.vue b/packages/@vben-core/uikit/layout-ui/src/components/layout-content.vue index 96b93b76..7d3aef76 100644 --- a/packages/@vben-core/uikit/layout-ui/src/components/layout-content.vue +++ b/packages/@vben-core/uikit/layout-ui/src/components/layout-content.vue @@ -2,7 +2,6 @@ import type { ContentCompactType } from '@vben-core/typings'; import type { CSSProperties } from 'vue'; - import { computed } from 'vue'; interface Props { diff --git a/packages/@vben-core/uikit/layout-ui/src/components/layout-footer.vue b/packages/@vben-core/uikit/layout-ui/src/components/layout-footer.vue index b4d68616..07a69017 100644 --- a/packages/@vben-core/uikit/layout-ui/src/components/layout-footer.vue +++ b/packages/@vben-core/uikit/layout-ui/src/components/layout-footer.vue @@ -1,10 +1,9 @@