From 8c8c5a709c277fd1c6b7368013e2a90998b689db Mon Sep 17 00:00:00 2001 From: vben Date: Sun, 11 Aug 2024 09:28:39 +0800 Subject: [PATCH 1/5] chore: detail adjustment --- .github/release-drafter.yml | 5 ++--- packages/@core/base/design/src/css/global.css | 2 ++ .../design/src/design-tokens/default/index.css | 2 +- packages/effects/common-ui/src/about/about.vue | 8 ++++---- .../effects/layouts/src/basic/tabbar/use-tabbar.ts | 14 +++++--------- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 6fef16be8f9..f700c6368f0 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -25,7 +25,7 @@ categories: - "perf" - "chore" - "dependencies" - collapse-after: 5 + # collapse-after: 12 - title: 🚦 Tests labels: - "tests" @@ -40,11 +40,10 @@ version-resolver: minor: labels: - "minor" - # - "feature" + - "feature" patch: labels: - "patch" - - "feature" - "bug" - "maintenance" - "docs" diff --git a/packages/@core/base/design/src/css/global.css b/packages/@core/base/design/src/css/global.css index 6646403ddcb..562cafa9356 100644 --- a/packages/@core/base/design/src/css/global.css +++ b/packages/@core/base/design/src/css/global.css @@ -35,7 +35,9 @@ min-height: 100vh; /* overflow: overlay; */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } a, diff --git a/packages/@core/base/design/src/design-tokens/default/index.css b/packages/@core/base/design/src/design-tokens/default/index.css index 5b0c985d17e..bf334175134 100644 --- a/packages/@core/base/design/src/design-tokens/default/index.css +++ b/packages/@core/base/design/src/design-tokens/default/index.css @@ -8,7 +8,7 @@ /* 主体区域背景色 */ --background-deep: 210 11.11% 96.47%; - --foreground: 210 6% 21%; + --foreground: 222 84% 5%; /* Background color for */ --card: 0 0% 100%; diff --git a/packages/effects/common-ui/src/about/about.vue b/packages/effects/common-ui/src/about/about.vue index d9ed88a8509..2a1709b5e14 100644 --- a/packages/effects/common-ui/src/about/about.vue +++ b/packages/effects/common-ui/src/about/about.vue @@ -125,7 +125,7 @@ const devDependenciesItems = Object.keys(devDependencies).map((key) => ({

{{ title }}

-

+

{{ name }} @@ -139,7 +139,7 @@ const devDependenciesItems = Object.keys(devDependencies).map((key) => ({

{{ item.title }}
-
+
@@ -159,7 +159,7 @@ const devDependenciesItems = Object.keys(devDependencies).map((key) => ({
{{ item.title }}
-
+
@@ -178,7 +178,7 @@ const devDependenciesItems = Object.keys(devDependencies).map((key) => ({
{{ item.title }}
-
+
diff --git a/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts b/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts index 020c72dc1b0..aef6405228f 100644 --- a/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts +++ b/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts @@ -100,15 +100,11 @@ export function useTabbar() { watch( () => route.path, () => { - // 这里不能用route,用route时,vue-router会自动将父级meta进行合并 - const routes = router.getRoutes(); - const currentRoute = routes.find((item) => item.path === route.path); - if (currentRoute) { - tabbarStore.addTab({ - ...route, - meta: currentRoute.meta, - } as unknown as RouteLocationNormalizedGeneric); - } + const meta = route.matched?.[route.matched.length - 1]?.meta; + tabbarStore.addTab({ + ...route, + meta: meta || route.meta, + }); }, { immediate: true }, ); From 73e694ee5f5d21826581c03e38eb4eccaeaf9442 Mon Sep 17 00:00:00 2001 From: vben Date: Sun, 11 Aug 2024 10:20:07 +0800 Subject: [PATCH 2/5] refactor: Migrate demo applications to playground --- .github/workflows/deploy.yml | 109 +++- .vscode/extensions.json | 4 - .vscode/launch.json | 15 +- .vscode/settings.json | 1 + Dockerfile | 2 +- apps/web-antd/.env | 2 +- apps/web-antd/.env.development | 2 +- apps/web-antd/index.html | 2 +- apps/web-antd/src/api/index.ts | 1 - apps/web-antd/src/locales/langs/en-US.json | 61 +-- apps/web-antd/src/locales/langs/zh-CN.json | 61 +-- .../src/router/routes/modules/demos.ts | 474 +---------------- .../src/router/routes/modules/vben.ts | 3 +- apps/web-antd/src/views/demos/antd/index.vue | 86 +++ apps/web-ele/.env.development | 2 +- apps/web-ele/src/router/guard.ts | 4 +- .../src/router/routes/modules/demos.ts | 1 - .../web-ele/src/router/routes/modules/vben.ts | 7 +- apps/web-naive/.env.development | 2 +- apps/web-naive/src/router/guard.ts | 4 +- .../src/router/routes/modules/demos.ts | 1 - .../src/router/routes/modules/vben.ts | 7 +- docs/.vitepress/config.mts | 6 +- docs/src/guide/essentials/development.md | 2 + internal/tsconfig/base.json | 1 - package.json | 1 + .../@core/base/shared/src/constants/vben.ts | 2 + playground/.env | 5 + playground/.env.analyze | 7 + playground/.env.development | 16 + playground/.env.production | 16 + playground/index.html | 35 ++ playground/package.json | 50 ++ playground/postcss.config.mjs | 1 + playground/public/favicon.ico | Bin 0 -> 5430 bytes playground/src/api/core/auth.ts | 33 ++ playground/src/api/core/index.ts | 3 + playground/src/api/core/menu.ts | 10 + playground/src/api/core/user.ts | 10 + .../src/api/demos/index.ts | 0 .../src/api/demos/status.ts | 0 playground/src/api/index.ts | 2 + playground/src/api/request.ts | 67 +++ playground/src/app.vue | 39 ++ playground/src/bootstrap.ts | 31 ++ playground/src/layouts/basic.vue | 154 ++++++ playground/src/layouts/index.ts | 8 + playground/src/locales/README.md | 3 + playground/src/locales/index.ts | 90 ++++ playground/src/locales/langs/en-US.json | 67 +++ playground/src/locales/langs/zh-CN.json | 67 +++ playground/src/main.ts | 31 ++ playground/src/preferences.ts | 12 + playground/src/router/access.ts | 42 ++ playground/src/router/guard.ts | 138 +++++ playground/src/router/index.ts | 32 ++ playground/src/router/routes/core.ts | 86 +++ playground/src/router/routes/index.ts | 31 ++ .../src/router/routes/modules/dashboard.ts | 39 ++ playground/src/router/routes/modules/demos.ts | 494 ++++++++++++++++++ .../src/router/routes/modules/examples.ts | 0 playground/src/router/routes/modules/vben.ts | 90 ++++ playground/src/store/auth.ts | 111 ++++ playground/src/store/index.ts | 1 + playground/src/views/_core/README.md | 3 + .../views/_core/authentication/code-login.vue | 30 ++ .../_core/authentication/forget-password.vue | 23 + .../src/views/_core/authentication/login.vue | 18 + .../_core/authentication/qrcode-login.vue | 10 + .../views/_core/authentication/register.vue | 25 + .../src/views/_core/fallback/coming-soon.vue | 0 .../src/views/_core/fallback/forbidden.vue | 9 + .../views/_core/fallback/internal-error.vue | 9 + .../src/views/_core/fallback/not-found.vue | 9 + .../src/views/_core/fallback/offline.vue | 9 + .../src/views/_core/vben/about/index.vue | 9 + .../dashboard/analytics/analytics-trends.vue | 78 +++ .../analytics/analytics-visits-data.vue | 80 +++ .../analytics/analytics-visits-sales.vue | 44 ++ .../analytics/analytics-visits-source.vue | 63 +++ .../dashboard/analytics/analytics-visits.vue | 53 ++ .../src/views/dashboard/analytics/index.vue | 90 ++++ .../src/views/dashboard/workspace/index.vue | 225 ++++++++ .../src/views/demos/access/admin-visible.vue | 0 .../src/views/demos/access/button-control.vue | 0 .../src/views/demos/access/index.vue | 0 .../views/demos/access/menu-visible-403.vue | 0 .../src/views/demos/access/super-visible.vue | 0 .../src/views/demos/access/user-visible.vue | 0 .../src/views/demos/active-icon/index.vue | 0 .../src/views/demos/badge/index.vue | 0 .../views/demos/breadcrumb/lateral-detail.vue | 0 .../src/views/demos/breadcrumb/lateral.vue | 0 .../views/demos/breadcrumb/level-detail.vue | 0 .../features/hide-menu-children/children.vue | 0 .../features/hide-menu-children/parent.vue | 0 .../src/views/demos/features/icons/index.vue | 0 .../demos/features/login-expired/index.vue | 0 .../src/views/demos/features/tabs/index.vue | 0 .../views/demos/features/tabs/tab-detail.vue | 0 .../views/demos/features/watermark/index.vue | 0 .../src/views/demos/nested/menu-1.vue | 0 .../src/views/demos/nested/menu-2-1.vue | 0 .../src/views/demos/nested/menu-3-1.vue | 0 .../src/views/demos/nested/menu-3-2-1.vue | 7 + .../src/views/examples/ellipsis/data.ts | 0 .../src/views/examples/ellipsis/index.vue | 0 playground/tailwind.config.mjs | 1 + playground/tsconfig.json | 12 + playground/tsconfig.node.json | 10 + playground/vite.config.mts | 20 + pnpm-lock.yaml | 64 ++- pnpm-workspace.yaml | 1 + vben-admin.code-workspace | 4 + 114 files changed, 2949 insertions(+), 641 deletions(-) create mode 100644 apps/web-antd/src/views/demos/antd/index.vue create mode 100644 playground/.env create mode 100644 playground/.env.analyze create mode 100644 playground/.env.development create mode 100644 playground/.env.production create mode 100644 playground/index.html create mode 100644 playground/package.json create mode 100644 playground/postcss.config.mjs create mode 100644 playground/public/favicon.ico create mode 100644 playground/src/api/core/auth.ts create mode 100644 playground/src/api/core/index.ts create mode 100644 playground/src/api/core/menu.ts create mode 100644 playground/src/api/core/user.ts rename {apps/web-antd => playground}/src/api/demos/index.ts (100%) rename {apps/web-antd => playground}/src/api/demos/status.ts (100%) create mode 100644 playground/src/api/index.ts create mode 100644 playground/src/api/request.ts create mode 100644 playground/src/app.vue create mode 100644 playground/src/bootstrap.ts create mode 100644 playground/src/layouts/basic.vue create mode 100644 playground/src/layouts/index.ts create mode 100644 playground/src/locales/README.md create mode 100644 playground/src/locales/index.ts create mode 100644 playground/src/locales/langs/en-US.json create mode 100644 playground/src/locales/langs/zh-CN.json create mode 100644 playground/src/main.ts create mode 100644 playground/src/preferences.ts create mode 100644 playground/src/router/access.ts create mode 100644 playground/src/router/guard.ts create mode 100644 playground/src/router/index.ts create mode 100644 playground/src/router/routes/core.ts create mode 100644 playground/src/router/routes/index.ts create mode 100644 playground/src/router/routes/modules/dashboard.ts create mode 100644 playground/src/router/routes/modules/demos.ts rename {apps/web-antd => playground}/src/router/routes/modules/examples.ts (100%) create mode 100644 playground/src/router/routes/modules/vben.ts create mode 100644 playground/src/store/auth.ts create mode 100644 playground/src/store/index.ts create mode 100644 playground/src/views/_core/README.md create mode 100644 playground/src/views/_core/authentication/code-login.vue create mode 100644 playground/src/views/_core/authentication/forget-password.vue create mode 100644 playground/src/views/_core/authentication/login.vue create mode 100644 playground/src/views/_core/authentication/qrcode-login.vue create mode 100644 playground/src/views/_core/authentication/register.vue rename apps/web-antd/src/views/demos/nested/menu-1.vue => playground/src/views/_core/fallback/coming-soon.vue (100%) create mode 100644 playground/src/views/_core/fallback/forbidden.vue create mode 100644 playground/src/views/_core/fallback/internal-error.vue create mode 100644 playground/src/views/_core/fallback/not-found.vue create mode 100644 playground/src/views/_core/fallback/offline.vue create mode 100644 playground/src/views/_core/vben/about/index.vue create mode 100644 playground/src/views/dashboard/analytics/analytics-trends.vue create mode 100644 playground/src/views/dashboard/analytics/analytics-visits-data.vue create mode 100644 playground/src/views/dashboard/analytics/analytics-visits-sales.vue create mode 100644 playground/src/views/dashboard/analytics/analytics-visits-source.vue create mode 100644 playground/src/views/dashboard/analytics/analytics-visits.vue create mode 100644 playground/src/views/dashboard/analytics/index.vue create mode 100644 playground/src/views/dashboard/workspace/index.vue rename {apps/web-antd => playground}/src/views/demos/access/admin-visible.vue (100%) rename {apps/web-antd => playground}/src/views/demos/access/button-control.vue (100%) rename {apps/web-antd => playground}/src/views/demos/access/index.vue (100%) rename {apps/web-antd => playground}/src/views/demos/access/menu-visible-403.vue (100%) rename {apps/web-antd => playground}/src/views/demos/access/super-visible.vue (100%) rename {apps/web-antd => playground}/src/views/demos/access/user-visible.vue (100%) rename {apps/web-antd => playground}/src/views/demos/active-icon/index.vue (100%) rename {apps/web-antd => playground}/src/views/demos/badge/index.vue (100%) rename {apps/web-antd => playground}/src/views/demos/breadcrumb/lateral-detail.vue (100%) rename {apps/web-antd => playground}/src/views/demos/breadcrumb/lateral.vue (100%) rename {apps/web-antd => playground}/src/views/demos/breadcrumb/level-detail.vue (100%) rename {apps/web-antd => playground}/src/views/demos/features/hide-menu-children/children.vue (100%) rename {apps/web-antd => playground}/src/views/demos/features/hide-menu-children/parent.vue (100%) rename {apps/web-antd => playground}/src/views/demos/features/icons/index.vue (100%) rename {apps/web-antd => playground}/src/views/demos/features/login-expired/index.vue (100%) rename {apps/web-antd => playground}/src/views/demos/features/tabs/index.vue (100%) rename {apps/web-antd => playground}/src/views/demos/features/tabs/tab-detail.vue (100%) rename {apps/web-antd => playground}/src/views/demos/features/watermark/index.vue (100%) rename apps/web-antd/src/views/demos/nested/menu-2-1.vue => playground/src/views/demos/nested/menu-1.vue (100%) rename apps/web-antd/src/views/demos/nested/menu-3-1.vue => playground/src/views/demos/nested/menu-2-1.vue (100%) rename apps/web-antd/src/views/demos/nested/menu-3-2-1.vue => playground/src/views/demos/nested/menu-3-1.vue (100%) create mode 100644 playground/src/views/demos/nested/menu-3-2-1.vue rename {apps/web-antd => playground}/src/views/examples/ellipsis/data.ts (100%) rename {apps/web-antd => playground}/src/views/examples/ellipsis/index.vue (100%) create mode 100644 playground/tailwind.config.mjs create mode 100644 playground/tsconfig.json create mode 100644 playground/tsconfig.node.json create mode 100644 playground/vite.config.mts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9832a90023a..d3363c1b728 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,7 +6,46 @@ on: - main jobs: - deploy-push-ftp: + deploy-push-playground-ftp: + name: Deploy Push Ftp + if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Sed Config Base + shell: bash + run: | + sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./playground/.env.production + sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./playground/.env.production + cat ./playground/.env.production + + - name: Setup Node + uses: ./.github/actions/setup-node + + - name: Build + run: pnpm run build + + - name: Sync Playground files + uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + with: + server: ${{ secrets.PRO_FTP_HOST }} + username: ${{ secrets.WEB_PLAYGROUND_FTP_ACCOUNT }} + password: ${{ secrets.WEB_PLAYGROUND_FTP_PWSSWORD }} + local-dir: ./playground/dist/ + + - name: Sync Docs files + uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + with: + server: ${{ secrets.PRO_FTP_HOST }} + username: ${{ secrets.WEBSITE_FTP_ACCOUNT }} + password: ${{ secrets.WEBSITE_FTP_PASSWORD }} + local-dir: ./docs/.vitepress/dist/ + + deploy-push-antd-ftp: name: Deploy Push Ftp if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-latest @@ -22,12 +61,6 @@ jobs: sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-antd/.env.production sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-antd/.env.production cat ./apps/web-antd/.env.production - sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-ele/.env.production - sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-ele/.env.production - cat ./apps/web-ele/.env.production - sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-naive/.env.production - sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-naive/.env.production - cat ./apps/web-naive/.env.production - name: Setup Node uses: ./.github/actions/setup-node @@ -35,7 +68,7 @@ jobs: - name: Build run: pnpm run build - - name: Sync Web Antd files + - name: Sync files uses: SamKirkland/FTP-Deploy-Action@v4.3.5 with: server: ${{ secrets.PRO_FTP_HOST }} @@ -43,15 +76,30 @@ jobs: password: ${{ secrets.WEB_ANTD_FTP_PASSWORD }} local-dir: ./apps/web-antd/dist/ - - name: Sync Web Naive files - uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + deploy-push-ele-ftp: + name: Deploy Push Ftp + if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 with: - server: ${{ secrets.PRO_FTP_HOST }} - username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }} - password: ${{ secrets.WEB_NAIVE_FTP_PASSWORD }} - local-dir: ./apps/web-naive/dist/ + fetch-depth: 0 + + - name: Sed Config Base + shell: bash + run: | + sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-ele/.env.production + sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-ele/.env.production + cat ./apps/web-ele/.env.production - - name: Sync Web Ele files + - name: Setup Node + uses: ./.github/actions/setup-node + + - name: Build + run: pnpm run build + + - name: Sync files uses: SamKirkland/FTP-Deploy-Action@v4.3.5 with: server: ${{ secrets.PRO_FTP_HOST }} @@ -59,10 +107,33 @@ jobs: password: ${{ secrets.WEB_ELE_FTP_PASSWORD }} local-dir: ./apps/web-ele/dist/ - - name: Sync Docs files + deploy-push-naive-ftp: + name: Deploy Push Ftp + if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Sed Config Base + shell: bash + run: | + sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-naive/.env.production + sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-naive/.env.production + cat ./apps/web-naive/.env.production + + - name: Setup Node + uses: ./.github/actions/setup-node + + - name: Build + run: pnpm run build + + - name: Sync files uses: SamKirkland/FTP-Deploy-Action@v4.3.5 with: server: ${{ secrets.PRO_FTP_HOST }} - username: ${{ secrets.WEBSITE_FTP_ACCOUNT }} - password: ${{ secrets.WEBSITE_FTP_PASSWORD }} - local-dir: ./docs/.vitepress/dist/ + username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }} + password: ${{ secrets.WEB_NAIVE_FTP_PASSWORD }} + local-dir: ./apps/web-naive/dist/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a0fec000099..cf48052d543 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -10,10 +10,6 @@ "esbenp.prettier-vscode", // 支持 dotenv 文件语法 "mikestead.dotenv", - // 获取每个 CSS 属性的初始值。 - "dzhavat.css-initial-value", - // 使 VSCode 中的 TypeScript 错误更漂亮、更易于理解 - "yoavbls.pretty-ts-errors", // 源代码的拼写检查器 "streetsidesoftware.code-spell-checker", // Tailwind CSS 的官方 VS Code 插件 diff --git a/.vscode/launch.json b/.vscode/launch.json index 08bd54f0d76..3b4e323c12f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,18 +4,27 @@ "configurations": [ { "type": "chrome", - "name": "vben admin antd dev", + "name": "vben admin playground dev", "request": "launch", "url": "http://localhost:5555", "env": { "NODE_ENV": "development" }, "sourceMaps": true, + "webRoot": "${workspaceFolder}/playground/src" + }, + { + "type": "chrome", + "name": "vben admin antd dev", + "request": "launch", + "url": "http://localhost:5666", + "env": { "NODE_ENV": "development" }, + "sourceMaps": true, "webRoot": "${workspaceFolder}/apps/web-antd/src" }, { "type": "chrome", "name": "vben admin ele dev", "request": "launch", - "url": "http://localhost:5666", + "url": "http://localhost:5777", "env": { "NODE_ENV": "development" }, "sourceMaps": true, "webRoot": "${workspaceFolder}/apps/web-ele/src" @@ -24,7 +33,7 @@ "type": "chrome", "name": "vben admin naive dev", "request": "launch", - "url": "http://localhost:5777", + "url": "http://localhost:5888", "env": { "NODE_ENV": "development" }, "sourceMaps": true, "webRoot": "${workspaceFolder}/apps/web-naive/src" diff --git a/.vscode/settings.json b/.vscode/settings.json index 563f7f159fb..6a97d0aafbc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -167,6 +167,7 @@ "i18n-ally.localesPaths": [ "packages/locales/src/langs", + "playground/src/langs", "apps/*/src/locales/langs" ], "i18n-ally.enabledParsers": ["json", "ts", "js", "yaml"], diff --git a/Dockerfile b/Dockerfile index 4b76d886c58..61076b6dc38 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ RUN echo "Builder Success 🎉" FROM nginx:stable-alpine as production RUN echo "types { application/javascript js mjs; }" > /etc/nginx/conf.d/mjs.conf -COPY --from=builder /app/apps/web-antd/dist /usr/share/nginx/html +COPY --from=builder /app/playground/dist /usr/share/nginx/html COPY ./nginx.conf /etc/nginx/nginx.conf diff --git a/apps/web-antd/.env b/apps/web-antd/.env index 87821d87cde..c14a467fb00 100644 --- a/apps/web-antd/.env +++ b/apps/web-antd/.env @@ -1,5 +1,5 @@ # 应用标题 -VITE_APP_TITLE=Vben Admin +VITE_APP_TITLE=Vben Admin Antd # 应用命名空间,用于缓存、store等功能的前缀,确保隔离 VITE_APP_NAMESPACE=vben-web-antd diff --git a/apps/web-antd/.env.development b/apps/web-antd/.env.development index dcf361e730e..c138f482918 100644 --- a/apps/web-antd/.env.development +++ b/apps/web-antd/.env.development @@ -1,5 +1,5 @@ # 端口号 -VITE_PORT=5555 +VITE_PORT=5666 VITE_BASE=/ diff --git a/apps/web-antd/index.html b/apps/web-antd/index.html index ca532699031..480eb84de6a 100644 --- a/apps/web-antd/index.html +++ b/apps/web-antd/index.html @@ -21,7 +21,7 @@ (function () { var hm = document.createElement('script'); hm.src = - 'https://hm.baidu.com/hm.js?d20a01273820422b6aa2ee41b6c9414d'; + 'https://hm.baidu.com/hm.js?b38e689f40558f20a9a686d7f6f33edf'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(hm, s); })(); diff --git a/apps/web-antd/src/api/index.ts b/apps/web-antd/src/api/index.ts index 2b42e898a12..4b0e0413762 100644 --- a/apps/web-antd/src/api/index.ts +++ b/apps/web-antd/src/api/index.ts @@ -1,2 +1 @@ export * from './core'; -export * from './demos'; diff --git a/apps/web-antd/src/locales/langs/en-US.json b/apps/web-antd/src/locales/langs/en-US.json index 13179fd2c10..864c721f5a5 100644 --- a/apps/web-antd/src/locales/langs/en-US.json +++ b/apps/web-antd/src/locales/langs/en-US.json @@ -2,66 +2,7 @@ "page": { "demos": { "title": "Demos", - "access": { - "frontendPermissions": "Frontend Permissions", - "backendPermissions": "Backend Permissions", - "pageAccess": "Page Access", - "buttonControl": "Button Control", - "menuVisible403": "Menu Visible(403)", - "superVisible": "Visible to Super", - "adminVisible": "Visible to Admin", - "userVisible": "Visible to User" - }, - "nested": { - "title": "Nested Menu", - "menu1": "Menu 1", - "menu2": "Menu 2", - "menu2_1": "Menu 2-1", - "menu3": "Menu 3", - "menu3_1": "Menu 3-1", - "menu3_2": "Menu 3-2", - "menu3_2_1": "Menu 3-2-1" - }, - "outside": { - "title": "External Pages", - "embedded": "Embedded", - "externalLink": "External Link" - }, - "badge": { - "title": "Menu Badge", - "dot": "Dot Badge", - "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" - }, - "breadcrumb": { - "navigation": "Breadcrumb Navigation", - "lateral": "Lateral Mode", - "lateralDetail": "Lateral Mode Detail", - "level": "Level Mode", - "levelDetail": "Level Mode Detail" - } - }, - "examples": { - "title": "Examples", - "ellipsis": { - "title": "EllipsisText" - } + "antd": "Ant Design Vue" } } } diff --git a/apps/web-antd/src/locales/langs/zh-CN.json b/apps/web-antd/src/locales/langs/zh-CN.json index 86fbcabcf6a..31d3475b900 100644 --- a/apps/web-antd/src/locales/langs/zh-CN.json +++ b/apps/web-antd/src/locales/langs/zh-CN.json @@ -2,66 +2,7 @@ "page": { "demos": { "title": "演示", - "access": { - "frontendPermissions": "前端权限", - "backendPermissions": "后端权限", - "pageAccess": "页面访问", - "buttonControl": "按钮控制", - "menuVisible403": "菜单可见(403)", - "superVisible": "Super 可见", - "adminVisible": "Admin 可见", - "userVisible": "User 可见" - }, - "nested": { - "title": "嵌套菜单", - "menu1": "菜单 1", - "menu2": "菜单 2", - "menu2_1": "菜单 2-1", - "menu3": "菜单 3", - "menu3_1": "菜单 3-1", - "menu3_2": "菜单 3-2", - "menu3_2_1": "菜单 3-2-1" - }, - "outside": { - "title": "外部页面", - "embedded": "内嵌", - "externalLink": "外链" - }, - "badge": { - "title": "菜单徽标", - "dot": "点徽标", - "text": "文本徽标", - "color": "徽标颜色" - }, - "activeIcon": { - "title": "菜单激活图标", - "children": "子级激活图标" - }, - "fallback": { - "title": "缺省页" - }, - "features": { - "title": "功能", - "hideChildrenInMenu": "隐藏子菜单", - "loginExpired": "登录过期", - "icons": "图标", - "watermark": "水印", - "tabs": "标签页", - "tabDetail": "标签详情页" - }, - "breadcrumb": { - "navigation": "面包屑导航", - "lateral": "平级模式", - "level": "层级模式", - "levelDetail": "层级模式详情", - "lateralDetail": "平级模式详情" - } - }, - "examples": { - "title": "示例", - "ellipsis": { - "title": "文本省略" - } + "antd": "Ant Design Vue" } } } diff --git a/apps/web-antd/src/router/routes/modules/demos.ts b/apps/web-antd/src/router/routes/modules/demos.ts index ad88a548611..fa28acc763c 100644 --- a/apps/web-antd/src/router/routes/modules/demos.ts +++ b/apps/web-antd/src/router/routes/modules/demos.ts @@ -1,6 +1,6 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout, IFrameView } from '#/layouts'; +import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ @@ -15,477 +15,13 @@ const routes: RouteRecordRaw[] = [ name: 'Demos', path: '/demos', children: [ - // 权限控制 { meta: { - icon: 'mdi:shield-key-outline', - title: $t('page.demos.access.frontendPermissions'), + title: $t('page.demos.antd'), }, - name: 'AccessDemos', - path: '/demos/access', - children: [ - { - name: 'AccessPageControlDemo', - path: '/demos/access/page-control', - component: () => import('#/views/demos/access/index.vue'), - meta: { - icon: 'mdi:page-previous-outline', - title: $t('page.demos.access.pageAccess'), - }, - }, - { - name: 'AccessButtonControlDemo', - path: '/demos/access/button-control', - component: () => import('#/views/demos/access/button-control.vue'), - meta: { - icon: 'mdi:button-cursor', - title: $t('page.demos.access.buttonControl'), - }, - }, - { - name: 'AccessMenuVisible403Demo', - path: '/demos/access/menu-visible-403', - component: () => - import('#/views/demos/access/menu-visible-403.vue'), - meta: { - authority: ['no-body'], - icon: 'mdi:button-cursor', - menuVisibleWithForbidden: true, - title: $t('page.demos.access.menuVisible403'), - }, - }, - { - name: 'AccessSuperVisibleDemo', - path: '/demos/access/super-visible', - component: () => import('#/views/demos/access/super-visible.vue'), - meta: { - authority: ['super'], - icon: 'mdi:button-cursor', - title: $t('page.demos.access.superVisible'), - }, - }, - { - name: 'AccessAdminVisibleDemo', - path: '/demos/access/admin-visible', - component: () => import('#/views/demos/access/admin-visible.vue'), - meta: { - authority: ['admin'], - icon: 'mdi:button-cursor', - title: $t('page.demos.access.adminVisible'), - }, - }, - { - name: 'AccessUserVisibleDemo', - path: '/demos/access/user-visible', - component: () => import('#/views/demos/access/user-visible.vue'), - meta: { - authority: ['user'], - icon: 'mdi:button-cursor', - title: $t('page.demos.access.userVisible'), - }, - }, - ], - }, - // 功能 - { - meta: { - icon: 'mdi:feature-highlight', - title: $t('page.demos.features.title'), - }, - 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: '/demos/features/tabs', - component: () => import('#/views/demos/features/tabs/index.vue'), - meta: { - icon: 'lucide:app-window', - title: $t('page.demos.features.tabs'), - }, - }, - { - name: 'FeatureTabDetailDemo', - path: '/demos/features/tabs/detail/:id', - component: () => - import('#/views/demos/features/tabs/tab-detail.vue'), - meta: { - activePath: '/demos/features/tabs', - hideInMenu: true, - maxNumOfOpenTab: 3, - title: $t('page.demos.features.tabDetail'), - }, - }, - { - 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: $t('page.demos.features.hideChildrenInMenu'), - }, - children: [ - { - name: 'HideChildrenInMenuChildrenDemo', - path: '/demos/features/hide-menu-children/children', - component: () => - import( - '#/views/demos/features/hide-menu-children/children.vue' - ), - meta: { title: 'HideChildrenInMenuChildrenDemo' }, - }, - ], - }, - ], - }, - // 面包屑导航 - { - name: 'BreadcrumbDemos', - path: '/demos/breadcrumb', - meta: { - icon: 'lucide:navigation', - title: $t('page.demos.breadcrumb.navigation'), - }, - children: [ - { - name: 'BreadcrumbLateralDemo', - path: '/demos/breadcrumb/lateral', - component: () => import('#/views/demos/breadcrumb/lateral.vue'), - meta: { - icon: 'lucide:navigation', - title: $t('page.demos.breadcrumb.lateral'), - }, - }, - { - name: 'BreadcrumbLateralDetailDemo', - path: '/demos/breadcrumb/lateral-detail', - component: () => - import('#/views/demos/breadcrumb/lateral-detail.vue'), - meta: { - activePath: '/demos/breadcrumb/lateral', - hideInMenu: true, - title: $t('page.demos.breadcrumb.lateralDetail'), - }, - }, - { - name: 'BreadcrumbLevelDemo', - path: '/demos/breadcrumb/level', - meta: { - icon: 'lucide:navigation', - title: $t('page.demos.breadcrumb.level'), - }, - children: [ - { - name: 'BreadcrumbLevelDetailDemo', - path: '/demos/breadcrumb/level/detail', - component: () => - import('#/views/demos/breadcrumb/level-detail.vue'), - meta: { - title: $t('page.demos.breadcrumb.levelDetail'), - }, - }, - ], - }, - ], - }, - // 缺省页 - { - meta: { - icon: 'mdi:lightbulb-error-outline', - title: $t('page.demos.fallback.title'), - }, - name: 'FallbackDemos', - path: '/demos/fallback', - children: [ - { - name: 'Fallback403Demo', - path: '/demos/fallback/403', - component: () => import('#/views/_core/fallback/forbidden.vue'), - meta: { - icon: 'mdi:do-not-disturb-alt', - title: '403', - }, - }, - { - name: 'Fallback404Demo', - path: '/demos/fallback/404', - component: () => import('#/views/_core/fallback/not-found.vue'), - meta: { - icon: 'mdi:table-off', - title: '404', - }, - }, - { - name: 'Fallback500Demo', - path: '/demos/fallback/500', - component: () => - import('#/views/_core/fallback/internal-error.vue'), - meta: { - icon: 'mdi:server-network-off', - title: '500', - }, - }, - { - name: 'FallbackOfflineDemo', - path: '/demos/fallback/offline', - component: () => import('#/views/_core/fallback/offline.vue'), - meta: { - icon: 'mdi:offline', - title: $t('fallback.offline'), - }, - }, - ], - }, - // 菜单徽标 - { - meta: { - badgeType: 'dot', - badgeVariants: 'destructive', - icon: 'lucide:circle-dot', - title: $t('page.demos.badge.title'), - }, - name: 'BadgeDemos', - path: '/demos/badge', - children: [ - { - name: 'BadgeDotDemo', - component: () => import('#/views/demos/badge/index.vue'), - path: '/demos/badge/dot', - meta: { - badgeType: 'dot', - icon: 'lucide:square-dot', - title: $t('page.demos.badge.dot'), - }, - }, - { - name: 'BadgeTextDemo', - component: () => import('#/views/demos/badge/index.vue'), - path: '/demos/badge/text', - meta: { - badge: '10', - icon: 'lucide:square-dot', - title: $t('page.demos.badge.text'), - }, - }, - { - name: 'BadgeColorDemo', - component: () => import('#/views/demos/badge/index.vue'), - path: '/demos/badge/color', - meta: { - badge: 'Hot', - badgeVariants: 'destructive', - icon: 'lucide:square-dot', - title: $t('page.demos.badge.color'), - }, - }, - ], - }, - // 菜单激活图标 - { - 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: 'OutsideDemos', - path: '/demos/outside', - children: [ - { - name: 'IframeDemos', - path: '/demos/outside/iframe', - meta: { - icon: 'mdi:newspaper-variant-outline', - title: $t('page.demos.outside.embedded'), - }, - children: [ - { - name: 'VueDocumentDemo', - path: '/demos/outside/iframe/vue-document', - component: IFrameView, - meta: { - icon: 'logos:vue', - iframeSrc: 'https://cn.vuejs.org/', - keepAlive: true, - title: 'Vue', - }, - }, - { - name: 'TailwindcssDemo', - path: '/demos/outside/iframe/tailwindcss', - component: IFrameView, - meta: { - icon: 'devicon:tailwindcss', - iframeSrc: 'https://tailwindcss.com/', - // keepAlive: true, - title: 'Tailwindcss', - }, - }, - ], - }, - { - name: 'ExternalLinkDemos', - path: '/demos/outside/external-link', - meta: { - icon: 'mdi:newspaper-variant-multiple-outline', - title: $t('page.demos.outside.externalLink'), - }, - children: [ - { - name: 'ViteDemo', - path: '/demos/outside/external-link/vite', - component: IFrameView, - meta: { - icon: 'logos:vitejs', - link: 'https://vitejs.dev/', - title: 'Vite', - }, - }, - { - name: 'VueUseDemo', - path: '/demos/outside/external-link/vue-use', - component: IFrameView, - meta: { - icon: 'logos:vueuse', - link: 'https://vueuse.org', - title: 'VueUse', - }, - }, - ], - }, - ], - }, - // 嵌套菜单 - { - meta: { - icon: 'ic:round-menu', - title: $t('page.demos.nested.title'), - }, - name: 'NestedDemos', - path: '/demos/nested', - children: [ - { - name: 'Menu1Demo', - path: '/demos/nested/menu1', - component: () => import('#/views/demos/nested/menu-1.vue'), - meta: { - icon: 'ic:round-menu', - keepAlive: true, - title: $t('page.demos.nested.menu1'), - }, - }, - { - name: 'Menu2Demo', - path: '/demos/nested/menu2', - meta: { - icon: 'ic:round-menu', - keepAlive: true, - title: $t('page.demos.nested.menu2'), - }, - children: [ - { - name: 'Menu21Demo', - path: '/demos/nested/menu2/menu2-1', - component: () => import('#/views/demos/nested/menu-2-1.vue'), - meta: { - icon: 'ic:round-menu', - keepAlive: true, - title: $t('page.demos.nested.menu2_1'), - }, - }, - ], - }, - { - name: 'Menu3Demo', - path: '/demos/nested/menu3', - meta: { - icon: 'ic:round-menu', - title: $t('page.demos.nested.menu3'), - }, - children: [ - { - name: 'Menu31Demo', - path: 'menu3-1', - component: () => import('#/views/demos/nested/menu-3-1.vue'), - meta: { - icon: 'ic:round-menu', - keepAlive: true, - title: $t('page.demos.nested.menu3_1'), - }, - }, - { - name: 'Menu32Demo', - path: 'menu3-2', - meta: { - icon: 'ic:round-menu', - title: $t('page.demos.nested.menu3_2'), - }, - children: [ - { - name: 'Menu321Demo', - path: '/demos/nested/menu3/menu3-2/menu3-2-1', - component: () => - import('#/views/demos/nested/menu-3-2-1.vue'), - meta: { - icon: 'ic:round-menu', - keepAlive: true, - title: $t('page.demos.nested.menu3_2_1'), - }, - }, - ], - }, - ], - }, - ], + name: 'AntDesignDemos', + path: '/demos/ant-design', + component: () => import('#/views/demos/antd/index.vue'), }, ], }, diff --git a/apps/web-antd/src/router/routes/modules/vben.ts b/apps/web-antd/src/router/routes/modules/vben.ts index 3a0daabe346..48e38fb2e10 100644 --- a/apps/web-antd/src/router/routes/modules/vben.ts +++ b/apps/web-antd/src/router/routes/modules/vben.ts @@ -38,8 +38,7 @@ const routes: RouteRecordRaw[] = [ component: IFrameView, meta: { icon: 'lucide:book-open-text', - iframeSrc: VBEN_DOC_URL, - keepAlive: true, + link: VBEN_DOC_URL, title: $t('page.vben.document'), }, }, diff --git a/apps/web-antd/src/views/demos/antd/index.vue b/apps/web-antd/src/views/demos/antd/index.vue new file mode 100644 index 00000000000..a9710f12a66 --- /dev/null +++ b/apps/web-antd/src/views/demos/antd/index.vue @@ -0,0 +1,86 @@ + + + diff --git a/apps/web-ele/.env.development b/apps/web-ele/.env.development index c138f482918..8bcb432e6d0 100644 --- a/apps/web-ele/.env.development +++ b/apps/web-ele/.env.development @@ -1,5 +1,5 @@ # 端口号 -VITE_PORT=5666 +VITE_PORT=5777 VITE_BASE=/ diff --git a/apps/web-ele/src/router/guard.ts b/apps/web-ele/src/router/guard.ts index 1581d015174..f6b79f06cd1 100644 --- a/apps/web-ele/src/router/guard.ts +++ b/apps/web-ele/src/router/guard.ts @@ -115,10 +115,10 @@ function setupAccessGuard(router: Router) { // 保存菜单信息和路由信息 accessStore.setAccessMenus(accessibleMenus); accessStore.setAccessRoutes(accessibleRoutes); - const redirectPath = (from.query.redirect ?? to.path) as string; + const redirectPath = (from.query.redirect ?? to.fullPath) as string; return { - path: decodeURIComponent(redirectPath), + ...router.resolve(decodeURIComponent(redirectPath)), replace: true, }; }); diff --git a/apps/web-ele/src/router/routes/modules/demos.ts b/apps/web-ele/src/router/routes/modules/demos.ts index 25a58989a7a..7b638a9989e 100644 --- a/apps/web-ele/src/router/routes/modules/demos.ts +++ b/apps/web-ele/src/router/routes/modules/demos.ts @@ -17,7 +17,6 @@ const routes: RouteRecordRaw[] = [ children: [ { meta: { - icon: 'mdi:shield-key-outline', title: $t('page.demos.element-plus'), }, name: 'NaiveDemos', diff --git a/apps/web-ele/src/router/routes/modules/vben.ts b/apps/web-ele/src/router/routes/modules/vben.ts index a5ced040e78..3a8f2e9424d 100644 --- a/apps/web-ele/src/router/routes/modules/vben.ts +++ b/apps/web-ele/src/router/routes/modules/vben.ts @@ -1,11 +1,11 @@ import type { RouteRecordRaw } from 'vue-router'; import { + VBEN_ANT_PREVIEW_URL, VBEN_DOC_URL, VBEN_GITHUB_URL, VBEN_LOGO_URL, VBEN_NAIVE_PREVIEW_URL, - VBEN_PREVIEW_URL, } from '@vben/constants'; import { BasicLayout, IFrameView } from '#/layouts'; @@ -38,8 +38,7 @@ const routes: RouteRecordRaw[] = [ component: IFrameView, meta: { icon: 'lucide:book-open-text', - iframeSrc: VBEN_DOC_URL, - keepAlive: true, + link: VBEN_DOC_URL, title: $t('page.vben.document'), }, }, @@ -69,7 +68,7 @@ const routes: RouteRecordRaw[] = [ component: IFrameView, meta: { badgeType: 'dot', - link: VBEN_PREVIEW_URL, + link: VBEN_ANT_PREVIEW_URL, title: $t('page.vben.antdv'), }, }, diff --git a/apps/web-naive/.env.development b/apps/web-naive/.env.development index 8bcb432e6d0..11c5254ae44 100644 --- a/apps/web-naive/.env.development +++ b/apps/web-naive/.env.development @@ -1,5 +1,5 @@ # 端口号 -VITE_PORT=5777 +VITE_PORT=5888 VITE_BASE=/ diff --git a/apps/web-naive/src/router/guard.ts b/apps/web-naive/src/router/guard.ts index 1581d015174..f6b79f06cd1 100644 --- a/apps/web-naive/src/router/guard.ts +++ b/apps/web-naive/src/router/guard.ts @@ -115,10 +115,10 @@ function setupAccessGuard(router: Router) { // 保存菜单信息和路由信息 accessStore.setAccessMenus(accessibleMenus); accessStore.setAccessRoutes(accessibleRoutes); - const redirectPath = (from.query.redirect ?? to.path) as string; + const redirectPath = (from.query.redirect ?? to.fullPath) as string; return { - path: decodeURIComponent(redirectPath), + ...router.resolve(decodeURIComponent(redirectPath)), replace: true, }; }); diff --git a/apps/web-naive/src/router/routes/modules/demos.ts b/apps/web-naive/src/router/routes/modules/demos.ts index 8ddf81eb414..5a2dc12a036 100644 --- a/apps/web-naive/src/router/routes/modules/demos.ts +++ b/apps/web-naive/src/router/routes/modules/demos.ts @@ -17,7 +17,6 @@ const routes: RouteRecordRaw[] = [ children: [ { meta: { - icon: 'mdi:shield-key-outline', title: $t('page.demos.naive'), }, name: 'NaiveDemos', diff --git a/apps/web-naive/src/router/routes/modules/vben.ts b/apps/web-naive/src/router/routes/modules/vben.ts index 3fd5942b597..0b1273df63c 100644 --- a/apps/web-naive/src/router/routes/modules/vben.ts +++ b/apps/web-naive/src/router/routes/modules/vben.ts @@ -1,11 +1,11 @@ import type { RouteRecordRaw } from 'vue-router'; import { + VBEN_ANT_PREVIEW_URL, VBEN_DOC_URL, VBEN_ELE_PREVIEW_URL, VBEN_GITHUB_URL, VBEN_LOGO_URL, - VBEN_PREVIEW_URL, } from '@vben/constants'; import { BasicLayout, IFrameView } from '#/layouts'; @@ -38,8 +38,7 @@ const routes: RouteRecordRaw[] = [ component: IFrameView, meta: { icon: 'lucide:book-open-text', - iframeSrc: VBEN_DOC_URL, - keepAlive: true, + link: VBEN_DOC_URL, title: $t('page.vben.document'), }, }, @@ -59,7 +58,7 @@ const routes: RouteRecordRaw[] = [ component: IFrameView, meta: { badgeType: 'dot', - link: VBEN_PREVIEW_URL, + link: VBEN_ANT_PREVIEW_URL, title: $t('page.vben.antdv'), }, }, diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 3e98f54db70..296e2e042c0 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -162,7 +162,11 @@ function nav(): DefaultTheme.NavItem[] { items: [ { link: 'https://www.vben.pro', - text: 'Ant Design Vue 版本(默认)', + text: '演示版本', + }, + { + link: 'https://ant.vben.pro', + text: 'Ant Design Vue 版本', }, { link: 'https://naive.vben.pro', diff --git a/docs/src/guide/essentials/development.md b/docs/src/guide/essentials/development.md index 8fb82a1953a..4b92b10f109 100644 --- a/docs/src/guide/essentials/development.md +++ b/docs/src/guide/essentials/development.md @@ -78,6 +78,8 @@ npm 脚本是项目常见的配置,用于执行一些常见的任务,比如 "dev:docs": "pnpm -F @vben/docs run dev", // 启动web-ele应用 "dev:ele": "pnpm -F @vben/web-ele run dev", + // 启动演示应用 + "dev:play": "pnpm -F @vben/playground run dev", // 启动web-naive应用 "dev:naive": "pnpm -F @vben/web-naive run dev", // 格式化代码 diff --git a/internal/tsconfig/base.json b/internal/tsconfig/base.json index 82e35f319df..1e45a784397 100644 --- a/internal/tsconfig/base.json +++ b/internal/tsconfig/base.json @@ -13,7 +13,6 @@ "moduleResolution": "node", "resolveJsonModule": true, - "allowImportingTsExtensions": true, "strict": true, "strictNullChecks": true, diff --git a/package.json b/package.json index 338e59a73eb..be4baaba1dc 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "dev:antd": "pnpm -F @vben/web-antd run dev", "dev:docs": "pnpm -F @vben/docs run dev", "dev:ele": "pnpm -F @vben/web-ele run dev", + "dev:play": "pnpm -F @vben/playground run dev", "dev:naive": "pnpm -F @vben/web-naive run dev", "format": "vsh lint --format", "lint": "vsh lint", diff --git a/packages/@core/base/shared/src/constants/vben.ts b/packages/@core/base/shared/src/constants/vben.ts index a7231757980..4ae5d1e6af1 100644 --- a/packages/@core/base/shared/src/constants/vben.ts +++ b/packages/@core/base/shared/src/constants/vben.ts @@ -22,3 +22,5 @@ export const VBEN_PREVIEW_URL = 'https://www.vben.pro'; export const VBEN_ELE_PREVIEW_URL = 'https://ele.vben.pro'; export const VBEN_NAIVE_PREVIEW_URL = 'https://naive.vben.pro'; + +export const VBEN_ANT_PREVIEW_URL = 'https://ant.vben.pro'; diff --git a/playground/.env b/playground/.env new file mode 100644 index 00000000000..87821d87cde --- /dev/null +++ b/playground/.env @@ -0,0 +1,5 @@ +# 应用标题 +VITE_APP_TITLE=Vben Admin + +# 应用命名空间,用于缓存、store等功能的前缀,确保隔离 +VITE_APP_NAMESPACE=vben-web-antd diff --git a/playground/.env.analyze b/playground/.env.analyze new file mode 100644 index 00000000000..ffafa8dd529 --- /dev/null +++ b/playground/.env.analyze @@ -0,0 +1,7 @@ +# public path +VITE_BASE=/ + +# Basic interface address SPA +VITE_GLOB_API_URL=/api + +VITE_VISUALIZER=true diff --git a/playground/.env.development b/playground/.env.development new file mode 100644 index 00000000000..dcf361e730e --- /dev/null +++ b/playground/.env.development @@ -0,0 +1,16 @@ +# 端口号 +VITE_PORT=5555 + +VITE_BASE=/ + +# 接口地址 +VITE_GLOB_API_URL=/api + +# 是否开启 Nitro Mock服务,true 为开启,false 为关闭 +VITE_NITRO_MOCK=true + +# 是否打开 devtools,true 为打开,false 为关闭 +VITE_DEVTOOLS=false + +# 是否注入全局loading +VITE_INJECT_APP_LOADING=true diff --git a/playground/.env.production b/playground/.env.production new file mode 100644 index 00000000000..37947069ff5 --- /dev/null +++ b/playground/.env.production @@ -0,0 +1,16 @@ +VITE_BASE=/ + +# 接口地址 +VITE_GLOB_API_URL=https://mock-napi.vben.pro/api + +# 是否开启压缩,可以设置为 none, brotli, gzip +VITE_COMPRESS=none + +# 是否开启 PWA +VITE_PWA=false + +# vue-router 的模式 +VITE_ROUTER_HISTORY=hash + +# 是否注入全局loading +VITE_INJECT_APP_LOADING=true diff --git a/playground/index.html b/playground/index.html new file mode 100644 index 00000000000..ca532699031 --- /dev/null +++ b/playground/index.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + <%= VITE_APP_TITLE %> + + + + +
+ + + diff --git a/playground/package.json b/playground/package.json new file mode 100644 index 00000000000..03413e1d4e0 --- /dev/null +++ b/playground/package.json @@ -0,0 +1,50 @@ +{ + "name": "@vben/playground", + "version": "5.0.1", + "homepage": "https://vben.pro", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "playground" + }, + "license": "MIT", + "author": { + "name": "vben", + "email": "ann.vben@gmail.com", + "url": "https://github.com/anncwb" + }, + "type": "module", + "scripts": { + "build": "pnpm vite build --mode production", + "build:analyze": "pnpm vite build --mode analyze", + "dev": "pnpm vite --mode development", + "preview": "vite preview", + "typecheck": "vue-tsc --noEmit --skipLibCheck" + }, + "imports": { + "#/*": "./src/*" + }, + "dependencies": { + "@vben/access": "workspace:*", + "@vben/chart-ui": "workspace:*", + "@vben/common-ui": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/layouts": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/request": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/styles": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "^10.11.1", + "ant-design-vue": "^4.2.3", + "dayjs": "^1.11.12", + "pinia": "2.2.1", + "vue": "^3.4.37", + "vue-router": "^4.4.3" + } +} diff --git a/playground/postcss.config.mjs b/playground/postcss.config.mjs new file mode 100644 index 00000000000..3d807045561 --- /dev/null +++ b/playground/postcss.config.mjs @@ -0,0 +1 @@ +export { default } from '@vben/tailwind-config/postcss'; diff --git a/playground/public/favicon.ico b/playground/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..fcf9818e2cf855039b272bdbfbb202d3ff3fa159 GIT binary patch literal 5430 zcmbtY33L=y7JY$12T+8gBk14=GU5gfn~Es=nm`C4AtVq2WF`B)l8}u>kcfdG`xZh7 z1QH@k!WNbQA}Y8rB4pn?Rn^_qRo&T0_nY^tI}C`PBiiTO^QwMT{dfQSKkwZa04wMM zy?X=M0kG_E0D}QwzyR}o4vl|CV{g(JUD6xoaWVij{_9_Z+hnMBoj6eCWqqZ|bDG3? zP1V)7jjOBM^TxNWH(wK5z8fU#uMgCSB?DD--$H2RPrcYwmDwZk?iT=9oB(QH+axi3 z9?DS*P#;M)Y%a>#V-YKdAXdDsz*NrcJ2dtV8t=Z2nsdGH*5&qiyUE>Vm@dqrLZt6> z(Th+Yvk>*s^HEHlg>AP+S>_mmri!6xDj#NlJ7oxWQ{b1Xzn%o3F2 zJy4EYjGB~r*z(;Z#Hz7qs`{9|A<#8ejX|uNj3&YMhCXf82Xb<7t(@Sce5a-F#rS2Y zPw=KW-BEkM4vp1Q(NsN!FqN*!*3f;EooLj|vHO`eL|&WtiJTISa&ibti29;|D&^HFD6dLJF=aUl zQK_g7S%X4w3TiekkUA=zPMK5-kDF8rPMA1{9Ua2-!AA8AkaE6;=1?EdZ_7=RH)f)o zz6YhW?I?uqK+bnR@_w194oGPgLT0)mW_M}f{ky5|&9E|QX9CL8LEd)~4EqY9IqS?o z{kEgO$QcJ|@3tc!kcFIoHX)1V*p0kb)a5#dX?;}TF3?iv0EkJTD|G@qe-~Pce+GHo zUNGz}jFvZLqdGDZxqw5+`{xi25@;Up%^i&{5ngJKR4B7`edHK7AGr+82hKvv;WMv@ zYYyG!LyjO9l#6`eVdBv_&jI9pw_i|sXY^1l+y2P+cXkv&)3*J<2c8E_(&=zM;!EU0 z@{kKYf_xCoLGuVSpE^80voR>Un<^;hk?+}!ZdX2t>o0=%S;@=7@}lqg@B-vQ^J$Kw z$kRMRNM0isbo4zwI1l*X$GyH9LO9vCUO3gaUR~I)UQ^VszB=pq zJ72ARzCd00QaRnLwP*Ti2{J*f&gxTfBdPm^TdR65(H6Yc<=&PT;fM6WaKmm2a4u7z zY0)I8b)GPl_qG;vVa_N8*`pjZ+l*jlZISnwfl zwR4>1*Y=-YMWLuCHstj@R+ZJSN6o<(K)=KqG#)n4uxOg27&xazjC4dT`IKUoTJp9N z>Z6DU%ij@>sEM{gL&=Bc{L6Cp!%%!u97-9Vcg-A>{BGewG17na4{SQ#y`>@VSqJV! zcc^fj1ovi-e@^h8bzUMLBKgvWJD@glA>~?k%K0vo`<)5%mHaPJ4dFIT#QUX`ua$K_ zv`!@kR1+xvrt;y$@z-$}g2rF|jbd>xY&y~NLfzpPo|paXq0w!|VqJ(syG(v`(M}c) z^C(YyQq5S5hFBLgC>)$ACQisl+Msy;Bg*%~&Gm=YYGUiHG=tS7V<~&aI8v>n*qgzw zhhqDW|3clxUTvDQeS)f;roW`|oBNea>y*ilMC(*IP%N}m_vCnQ;y|^Jp0PN{>^bdE zL)sKg8Kh+_=+ri~?4IeHdE|Qz-@)6CF{)<) zjs-BRjtBk54es*F==&1+kV)$>F7gULf-mibH*%4rpOQ!mtq3B}^A+qlAw8#G}T>Y-?wcHN#jlo%oSHYH%Rm4ro+cZ_?b3=+aWV5Y&5fK;E$%$f_)8BCDP)cJNdD z5dR5M*#fwd?XFZ~Zg(zdGj@V{^PXPPhOKAhPts6cvkv9eYc2Rhv0`cpHiRc5w`?T^8l1Q&Crn*oxsRVg_ar{VE#)>+#dL67hLvPFyvkW!mt=0{?cCmWIH@mr+o(*k>E2{86CkHbRwjbs_ z;eGcaw{!*8Il9N8dkTDGI}Ok+=`mlwH&Byx8Vtv-fuZ&9*o3w*^g`-N}!37fsOYgx4us+7ZgITsO%AU}9@c0~6uE~i8yCA6W+4Kb70q*X~ z1AYEAXs;5x${9Jk)G_2cgnE{cP7c&Z5fA?1UP|ew%;$pjW$KWFCzc8j?yl(;c%DQUTT-YW=pQN6P`YqPGu^v$A11)>exgV^rWIbW%QR)c`>QsTb zBe+27tpc;3*gyL~x0Us6C9H27sZKoG$VZs_K92Q$ojlmNVC%O)pq^6AVikBK30GD_ zMZkWz8Mfn3`Trp!2gH5Xq1C9d64zZy7vc&@^BhNW56d_Z=v?p=3wAyk1d0(Kly|Gn zJG*~_Zw}N39{f|jJ3rFxybe;vRgkt^8=^@)U&}|GR5-Byw=)jB5)0x%RtPO('/auth/login', data); +} + +/** + * 获取用户权限码 + */ +export async function getAccessCodesApi() { + return requestClient.get('/auth/codes'); +} diff --git a/playground/src/api/core/index.ts b/playground/src/api/core/index.ts new file mode 100644 index 00000000000..28a5aef47ef --- /dev/null +++ b/playground/src/api/core/index.ts @@ -0,0 +1,3 @@ +export * from './auth'; +export * from './menu'; +export * from './user'; diff --git a/playground/src/api/core/menu.ts b/playground/src/api/core/menu.ts new file mode 100644 index 00000000000..9ef60b11cd2 --- /dev/null +++ b/playground/src/api/core/menu.ts @@ -0,0 +1,10 @@ +import type { RouteRecordStringComponent } from '@vben/types'; + +import { requestClient } from '#/api/request'; + +/** + * 获取用户所有菜单 + */ +export async function getAllMenusApi() { + return requestClient.get('/menu/all'); +} diff --git a/playground/src/api/core/user.ts b/playground/src/api/core/user.ts new file mode 100644 index 00000000000..7e28ea8489a --- /dev/null +++ b/playground/src/api/core/user.ts @@ -0,0 +1,10 @@ +import type { UserInfo } from '@vben/types'; + +import { requestClient } from '#/api/request'; + +/** + * 获取用户信息 + */ +export async function getUserInfoApi() { + return requestClient.get('/user/info'); +} diff --git a/apps/web-antd/src/api/demos/index.ts b/playground/src/api/demos/index.ts similarity index 100% rename from apps/web-antd/src/api/demos/index.ts rename to playground/src/api/demos/index.ts diff --git a/apps/web-antd/src/api/demos/status.ts b/playground/src/api/demos/status.ts similarity index 100% rename from apps/web-antd/src/api/demos/status.ts rename to playground/src/api/demos/status.ts diff --git a/playground/src/api/index.ts b/playground/src/api/index.ts new file mode 100644 index 00000000000..2b42e898a12 --- /dev/null +++ b/playground/src/api/index.ts @@ -0,0 +1,2 @@ +export * from './core'; +export * from './demos'; diff --git a/playground/src/api/request.ts b/playground/src/api/request.ts new file mode 100644 index 00000000000..7cd11e27de7 --- /dev/null +++ b/playground/src/api/request.ts @@ -0,0 +1,67 @@ +/** + * 该文件可自行根据业务逻辑进行调整 + */ +import type { HttpResponse } from '@vben/request'; + +import { useAppConfig } from '@vben/hooks'; +import { preferences } from '@vben/preferences'; +import { RequestClient } from '@vben/request'; +import { useAccessStore } from '@vben/stores'; + +import { message } from 'ant-design-vue'; + +import { useAuthStore } from '#/store'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); + +function createRequestClient(baseURL: string) { + const client = new RequestClient({ + baseURL, + // 为每个请求携带 Authorization + makeAuthorization: () => { + return { + // 默认 + key: 'Authorization', + tokenHandler: () => { + const accessStore = useAccessStore(); + return { + refreshToken: `${accessStore.refreshToken}`, + token: `${accessStore.accessToken}`, + }; + }, + unAuthorizedHandler: async () => { + const accessStore = useAccessStore(); + const authStore = useAuthStore(); + accessStore.setAccessToken(null); + + if (preferences.app.loginExpiredMode === 'modal') { + accessStore.setLoginExpired(true); + } else { + // 退出登录 + await authStore.logout(); + } + }, + }; + }, + makeErrorMessage: (msg) => message.error(msg), + + makeRequestHeaders: () => { + return { + // 为每个请求携带 Accept-Language + 'Accept-Language': preferences.app.locale, + }; + }, + }); + client.addResponseInterceptor((response) => { + const { data: responseData, status } = response; + + const { code, data, message: msg } = responseData; + if (status >= 200 && status < 400 && code === 0) { + return data; + } + throw new Error(msg); + }); + return client; +} + +export const requestClient = createRequestClient(apiURL); diff --git a/playground/src/app.vue b/playground/src/app.vue new file mode 100644 index 00000000000..bbaccce13bc --- /dev/null +++ b/playground/src/app.vue @@ -0,0 +1,39 @@ + + + diff --git a/playground/src/bootstrap.ts b/playground/src/bootstrap.ts new file mode 100644 index 00000000000..1be9bbe0084 --- /dev/null +++ b/playground/src/bootstrap.ts @@ -0,0 +1,31 @@ +import { createApp } from 'vue'; + +import { registerAccessDirective } from '@vben/access'; +import { initStores } from '@vben/stores'; +import '@vben/styles'; +import '@vben/styles/antd'; + +import { setupI18n } from '#/locales'; + +import App from './app.vue'; +import { router } from './router'; + +async function bootstrap(namespace: string) { + const app = createApp(App); + + // 国际化 i18n 配置 + await setupI18n(app); + + // 配置 pinia-tore + await initStores(app, { namespace }); + + // 安装权限指令 + registerAccessDirective(app); + + // 配置路由及路由守卫 + app.use(router); + + app.mount('#app'); +} + +export { bootstrap }; diff --git a/playground/src/layouts/basic.vue b/playground/src/layouts/basic.vue new file mode 100644 index 00000000000..630d8bdd77c --- /dev/null +++ b/playground/src/layouts/basic.vue @@ -0,0 +1,154 @@ + + + diff --git a/playground/src/layouts/index.ts b/playground/src/layouts/index.ts new file mode 100644 index 00000000000..23d79d6e465 --- /dev/null +++ b/playground/src/layouts/index.ts @@ -0,0 +1,8 @@ +const BasicLayout = () => import('./basic.vue'); + +const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView); + +const AuthPageLayout = () => + import('@vben/layouts').then((m) => m.AuthPageLayout); + +export { AuthPageLayout, BasicLayout, IFrameView }; diff --git a/playground/src/locales/README.md b/playground/src/locales/README.md new file mode 100644 index 00000000000..7b451032e6c --- /dev/null +++ b/playground/src/locales/README.md @@ -0,0 +1,3 @@ +# locale + +每个app使用的国际化可能不同,这里用于扩展国际化的功能,例如扩展 dayjs、antd组件库的多语言切换,以及app本身的国际化文件。 diff --git a/playground/src/locales/index.ts b/playground/src/locales/index.ts new file mode 100644 index 00000000000..ef3b8d5c76c --- /dev/null +++ b/playground/src/locales/index.ts @@ -0,0 +1,90 @@ +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, 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'; +import dayjs from 'dayjs'; + +const antdLocale = ref(antdDefaultLocale); + +const modules = import.meta.glob('./langs/*.json'); + +const localesMap = loadLocalesMap(modules); + +/** + * 加载应用特有的语言包 + * 这里也可以改造为从服务端获取翻译数据 + * @param lang + */ +async function loadMessages(lang: SupportedLanguagesType) { + const [appLocaleMessages] = await Promise.all([ + localesMap[lang]?.(), + loadThirdPartyMessage(lang), + ]); + return appLocaleMessages?.default; +} + +/** + * 加载第三方组件库的语言包 + * @param lang + */ +async function loadThirdPartyMessage(lang: SupportedLanguagesType) { + await Promise.all([loadAntdLocale(lang), loadDayjsLocale(lang)]); +} + +/** + * 加载dayjs的语言包 + * @param lang + */ +async function loadDayjsLocale(lang: SupportedLanguagesType) { + let locale; + switch (lang) { + case 'zh-CN': { + locale = await import('dayjs/locale/zh-cn'); + break; + } + case 'en-US': { + locale = await import('dayjs/locale/en'); + break; + } + // 默认使用英语 + default: { + locale = await import('dayjs/locale/en'); + } + } + dayjs.locale(locale); +} + +/** + * 加载antd的语言包 + * @param lang + */ +async function loadAntdLocale(lang: SupportedLanguagesType) { + switch (lang) { + case 'zh-CN': { + antdLocale.value = antdDefaultLocale; + break; + } + case 'en-US': { + antdLocale.value = antdEnLocale; + break; + } + } +} + +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/playground/src/locales/langs/en-US.json b/playground/src/locales/langs/en-US.json new file mode 100644 index 00000000000..13179fd2c10 --- /dev/null +++ b/playground/src/locales/langs/en-US.json @@ -0,0 +1,67 @@ +{ + "page": { + "demos": { + "title": "Demos", + "access": { + "frontendPermissions": "Frontend Permissions", + "backendPermissions": "Backend Permissions", + "pageAccess": "Page Access", + "buttonControl": "Button Control", + "menuVisible403": "Menu Visible(403)", + "superVisible": "Visible to Super", + "adminVisible": "Visible to Admin", + "userVisible": "Visible to User" + }, + "nested": { + "title": "Nested Menu", + "menu1": "Menu 1", + "menu2": "Menu 2", + "menu2_1": "Menu 2-1", + "menu3": "Menu 3", + "menu3_1": "Menu 3-1", + "menu3_2": "Menu 3-2", + "menu3_2_1": "Menu 3-2-1" + }, + "outside": { + "title": "External Pages", + "embedded": "Embedded", + "externalLink": "External Link" + }, + "badge": { + "title": "Menu Badge", + "dot": "Dot Badge", + "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" + }, + "breadcrumb": { + "navigation": "Breadcrumb Navigation", + "lateral": "Lateral Mode", + "lateralDetail": "Lateral Mode Detail", + "level": "Level Mode", + "levelDetail": "Level Mode Detail" + } + }, + "examples": { + "title": "Examples", + "ellipsis": { + "title": "EllipsisText" + } + } + } +} diff --git a/playground/src/locales/langs/zh-CN.json b/playground/src/locales/langs/zh-CN.json new file mode 100644 index 00000000000..86fbcabcf6a --- /dev/null +++ b/playground/src/locales/langs/zh-CN.json @@ -0,0 +1,67 @@ +{ + "page": { + "demos": { + "title": "演示", + "access": { + "frontendPermissions": "前端权限", + "backendPermissions": "后端权限", + "pageAccess": "页面访问", + "buttonControl": "按钮控制", + "menuVisible403": "菜单可见(403)", + "superVisible": "Super 可见", + "adminVisible": "Admin 可见", + "userVisible": "User 可见" + }, + "nested": { + "title": "嵌套菜单", + "menu1": "菜单 1", + "menu2": "菜单 2", + "menu2_1": "菜单 2-1", + "menu3": "菜单 3", + "menu3_1": "菜单 3-1", + "menu3_2": "菜单 3-2", + "menu3_2_1": "菜单 3-2-1" + }, + "outside": { + "title": "外部页面", + "embedded": "内嵌", + "externalLink": "外链" + }, + "badge": { + "title": "菜单徽标", + "dot": "点徽标", + "text": "文本徽标", + "color": "徽标颜色" + }, + "activeIcon": { + "title": "菜单激活图标", + "children": "子级激活图标" + }, + "fallback": { + "title": "缺省页" + }, + "features": { + "title": "功能", + "hideChildrenInMenu": "隐藏子菜单", + "loginExpired": "登录过期", + "icons": "图标", + "watermark": "水印", + "tabs": "标签页", + "tabDetail": "标签详情页" + }, + "breadcrumb": { + "navigation": "面包屑导航", + "lateral": "平级模式", + "level": "层级模式", + "levelDetail": "层级模式详情", + "lateralDetail": "平级模式详情" + } + }, + "examples": { + "title": "示例", + "ellipsis": { + "title": "文本省略" + } + } + } +} diff --git a/playground/src/main.ts b/playground/src/main.ts new file mode 100644 index 00000000000..5d728a02acb --- /dev/null +++ b/playground/src/main.ts @@ -0,0 +1,31 @@ +import { initPreferences } from '@vben/preferences'; +import { unmountGlobalLoading } from '@vben/utils'; + +import { overridesPreferences } from './preferences'; + +/** + * 应用初始化完成之后再进行页面加载渲染 + */ +async function initApplication() { + // name用于指定项目唯一标识 + // 用于区分不同项目的偏好设置以及存储数据的key前缀以及其他一些需要隔离的数据 + const env = import.meta.env.PROD ? 'prod' : 'dev'; + const appVersion = import.meta.env.VITE_APP_VERSION; + const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`; + + // app偏好设置初始化 + await initPreferences({ + namespace, + overrides: overridesPreferences, + }); + + // 启动应用并挂载 + // vue应用主要逻辑及视图 + const { bootstrap } = await import('./bootstrap'); + await bootstrap(namespace); + + // 移除并销毁loading + unmountGlobalLoading(); +} + +initApplication(); diff --git a/playground/src/preferences.ts b/playground/src/preferences.ts new file mode 100644 index 00000000000..63edb3a9be3 --- /dev/null +++ b/playground/src/preferences.ts @@ -0,0 +1,12 @@ +import { defineOverridesPreferences } from '@vben/preferences'; + +/** + * @description 项目配置文件 + * 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置 + */ +export const overridesPreferences = defineOverridesPreferences({ + // overrides + app: { + name: import.meta.env.VITE_APP_TITLE, + }, +}); diff --git a/playground/src/router/access.ts b/playground/src/router/access.ts new file mode 100644 index 00000000000..3a48be2378c --- /dev/null +++ b/playground/src/router/access.ts @@ -0,0 +1,42 @@ +import type { + ComponentRecordType, + GenerateMenuAndRoutesOptions, +} from '@vben/types'; + +import { generateAccessible } from '@vben/access'; +import { preferences } from '@vben/preferences'; + +import { message } from 'ant-design-vue'; + +import { getAllMenusApi } from '#/api'; +import { BasicLayout, IFrameView } from '#/layouts'; +import { $t } from '#/locales'; + +const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue'); + +async function generateAccess(options: GenerateMenuAndRoutesOptions) { + const pageMap: ComponentRecordType = import.meta.glob('../views/**/*.vue'); + + const layoutMap: ComponentRecordType = { + BasicLayout, + IFrameView, + }; + + return await generateAccessible(preferences.app.accessMode, { + ...options, + fetchMenuListAsync: async () => { + message.loading({ + content: `${$t('common.loadingMenu')}...`, + duration: 1.5, + }); + return await getAllMenusApi(); + }, + // 可以指定没有权限跳转403页面 + forbiddenComponent, + // 如果 route.meta.menuVisibleWithForbidden = true + layoutMap, + pageMap, + }); +} + +export { generateAccess }; diff --git a/playground/src/router/guard.ts b/playground/src/router/guard.ts new file mode 100644 index 00000000000..f6b79f06cd1 --- /dev/null +++ b/playground/src/router/guard.ts @@ -0,0 +1,138 @@ +import type { Router } from 'vue-router'; + +import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; +import { preferences } from '@vben/preferences'; +import { useAccessStore, useUserStore } from '@vben/stores'; +import { startProgress, stopProgress } from '@vben/utils'; + +import { useTitle } from '@vueuse/core'; + +import { $t } from '#/locales'; +import { coreRouteNames, dynamicRoutes } from '#/router/routes'; +import { useAuthStore } from '#/store'; + +import { generateAccess } from './access'; + +/** + * 通用守卫配置 + * @param router + */ +function setupCommonGuard(router: Router) { + // 记录已经加载的页面 + const loadedPaths = new Set(); + + router.beforeEach(async (to) => { + to.meta.loaded = loadedPaths.has(to.path); + + // 页面加载进度条 + if (!to.meta.loaded && preferences.transition.progress) { + startProgress(); + } + return true; + }); + + router.afterEach((to) => { + // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行 + + if (preferences.tabbar.enable) { + loadedPaths.add(to.path); + } + + // 关闭页面加载进度条 + if (preferences.transition.progress) { + stopProgress(); + } + + // 动态修改标题 + if (preferences.app.dynamicTitle) { + const { title } = to.meta; + // useTitle(`${$t(title)} - ${preferences.app.name}`); + useTitle(`${$t(title)} - ${preferences.app.name}`); + } + }); +} + +/** + * 权限访问守卫配置 + * @param router + */ +function setupAccessGuard(router: Router) { + router.beforeEach(async (to, from) => { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const authStore = useAuthStore(); + + // 基本路由,这些路由不需要进入权限拦截 + if (coreRouteNames.includes(to.name as string)) { + if (to.path === LOGIN_PATH && accessStore.accessToken) { + return decodeURIComponent( + (to.query?.redirect as string) || DEFAULT_HOME_PATH, + ); + } + return true; + } + + // accessToken 检查 + if (!accessStore.accessToken) { + // 明确声明忽略权限访问权限,则可以访问 + if (to.meta.ignoreAccess) { + return true; + } + + // 没有访问权限,跳转登录页面 + if (to.fullPath !== LOGIN_PATH) { + return { + path: LOGIN_PATH, + // 如不需要,直接删除 query + query: { redirect: encodeURIComponent(to.fullPath) }, + // 携带当前跳转的页面,登录后重新跳转该页面 + replace: true, + }; + } + return to; + } + + const accessRoutes = accessStore.accessRoutes; + + // 是否已经生成过动态路由 + if (accessRoutes && accessRoutes.length > 0) { + return true; + } + + // 生成路由表 + // 当前登录用户拥有的角色标识列表 + const userInfo = userStore.userInfo || (await authStore.fetchUserInfo()); + const userRoles = userInfo.roles ?? []; + + // 生成菜单和路由 + const { accessibleMenus, accessibleRoutes } = await generateAccess({ + roles: userRoles, + router, + // 则会在菜单中显示,但是访问会被重定向到403 + routes: dynamicRoutes, + }); + + // 保存菜单信息和路由信息 + accessStore.setAccessMenus(accessibleMenus); + accessStore.setAccessRoutes(accessibleRoutes); + const redirectPath = (from.query.redirect ?? to.fullPath) as string; + + return { + ...router.resolve(decodeURIComponent(redirectPath)), + replace: true, + }; + }); +} + +/** + * 项目守卫配置 + * @param router + */ +function createRouterGuard(router: Router) { + /** 通用 */ + setupCommonGuard(router); + /** 权限访问 */ + setupAccessGuard(router); +} + +export { createRouterGuard }; diff --git a/playground/src/router/index.ts b/playground/src/router/index.ts new file mode 100644 index 00000000000..313b372bdcf --- /dev/null +++ b/playground/src/router/index.ts @@ -0,0 +1,32 @@ +import { + createRouter, + createWebHashHistory, + createWebHistory, +} from 'vue-router'; + +import { resetStaticRoutes } from '@vben/utils'; + +import { createRouterGuard } from './guard'; +import { routes } from './routes'; + +/** + * @zh_CN 创建vue-router实例 + */ +const router = createRouter({ + 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, +}); + +const resetRoutes = () => resetStaticRoutes(router, routes); + +// 创建路由守卫 +createRouterGuard(router); + +export { resetRoutes, router }; diff --git a/playground/src/router/routes/core.ts b/playground/src/router/routes/core.ts new file mode 100644 index 00000000000..f793a57eb3f --- /dev/null +++ b/playground/src/router/routes/core.ts @@ -0,0 +1,86 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { DEFAULT_HOME_PATH } from '@vben/constants'; + +import { AuthPageLayout } from '#/layouts'; +import { $t } from '#/locales'; +import Login from '#/views/_core/authentication/login.vue'; + +/** 全局404页面 */ +const fallbackNotFoundRoute: RouteRecordRaw = { + component: () => import('#/views/_core/fallback/not-found.vue'), + meta: { + hideInBreadcrumb: true, + hideInMenu: true, + hideInTab: true, + title: '404', + }, + name: 'FallbackNotFound', + path: '/:path(.*)*', +}; + +/** 基本路由,这些路由是必须存在的 */ +const coreRoutes: RouteRecordRaw[] = [ + { + meta: { + title: 'Root', + }, + name: 'Root', + path: '/', + redirect: DEFAULT_HOME_PATH, + }, + { + component: AuthPageLayout, + meta: { + title: 'Authentication', + }, + name: 'Authentication', + path: '/auth', + children: [ + { + name: 'Login', + path: 'login', + component: Login, + meta: { + title: $t('page.core.login'), + }, + }, + { + name: 'CodeLogin', + path: 'code-login', + component: () => import('#/views/_core/authentication/code-login.vue'), + meta: { + title: $t('page.core.codeLogin'), + }, + }, + { + name: 'QrCodeLogin', + path: 'qrcode-login', + component: () => + import('#/views/_core/authentication/qrcode-login.vue'), + meta: { + title: $t('page.core.qrcodeLogin'), + }, + }, + { + name: 'ForgetPassword', + path: 'forget-password', + component: () => + import('#/views/_core/authentication/forget-password.vue'), + meta: { + title: $t('page.core.forgetPassword'), + }, + }, + { + name: 'Register', + path: 'register', + component: () => import('#/views/_core/authentication/register.vue'), + meta: { + title: $t('page.core.register'), + }, + }, + ], + }, +]; + +export { coreRoutes, fallbackNotFoundRoute }; diff --git a/playground/src/router/routes/index.ts b/playground/src/router/routes/index.ts new file mode 100644 index 00000000000..78da5436c75 --- /dev/null +++ b/playground/src/router/routes/index.ts @@ -0,0 +1,31 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { mergeRouteModules, traverseTreeValues } from '@vben/utils'; + +import { coreRoutes, fallbackNotFoundRoute } from './core'; + +const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', { + eager: true, +}); + +// 有需要可以自行打开注释,并创建文件夹 +// const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true }); + +/** 动态路由 */ +const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles); + +/** 静态路由列表,访问这些页面可以不需要权限 */ +// const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles); +const staticRoutes: RouteRecordRaw[] = []; + +/** 路由列表,由基本路由+静态路由组成 */ +const routes: RouteRecordRaw[] = [ + ...coreRoutes, + ...staticRoutes, + fallbackNotFoundRoute, +]; + +/** 基本路由列表,这些路由不需要进入权限拦截 */ +const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name); + +export { coreRouteNames, dynamicRoutes, routes }; diff --git a/playground/src/router/routes/modules/dashboard.ts b/playground/src/router/routes/modules/dashboard.ts new file mode 100644 index 00000000000..a8cdb05da08 --- /dev/null +++ b/playground/src/router/routes/modules/dashboard.ts @@ -0,0 +1,39 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { BasicLayout } from '#/layouts'; +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + component: BasicLayout, + meta: { + icon: 'lucide:layout-dashboard', + order: -1, + title: $t('page.dashboard.title'), + }, + name: 'Dashboard', + path: '/', + children: [ + { + name: 'Analytics', + path: '/analytics', + component: () => import('#/views/dashboard/analytics/index.vue'), + meta: { + affixTab: true, + icon: 'lucide:area-chart', + title: $t('page.dashboard.analytics'), + }, + }, + { + name: 'Workspace', + path: '/workspace', + component: () => import('#/views/dashboard/workspace/index.vue'), + meta: { + title: $t('page.dashboard.workspace'), + }, + }, + ], + }, +]; + +export default routes; diff --git a/playground/src/router/routes/modules/demos.ts b/playground/src/router/routes/modules/demos.ts new file mode 100644 index 00000000000..ad88a548611 --- /dev/null +++ b/playground/src/router/routes/modules/demos.ts @@ -0,0 +1,494 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { BasicLayout, IFrameView } from '#/layouts'; +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + component: BasicLayout, + meta: { + icon: 'ic:baseline-view-in-ar', + keepAlive: true, + order: 1000, + title: $t('page.demos.title'), + }, + name: 'Demos', + path: '/demos', + children: [ + // 权限控制 + { + meta: { + icon: 'mdi:shield-key-outline', + title: $t('page.demos.access.frontendPermissions'), + }, + name: 'AccessDemos', + path: '/demos/access', + children: [ + { + name: 'AccessPageControlDemo', + path: '/demos/access/page-control', + component: () => import('#/views/demos/access/index.vue'), + meta: { + icon: 'mdi:page-previous-outline', + title: $t('page.demos.access.pageAccess'), + }, + }, + { + name: 'AccessButtonControlDemo', + path: '/demos/access/button-control', + component: () => import('#/views/demos/access/button-control.vue'), + meta: { + icon: 'mdi:button-cursor', + title: $t('page.demos.access.buttonControl'), + }, + }, + { + name: 'AccessMenuVisible403Demo', + path: '/demos/access/menu-visible-403', + component: () => + import('#/views/demos/access/menu-visible-403.vue'), + meta: { + authority: ['no-body'], + icon: 'mdi:button-cursor', + menuVisibleWithForbidden: true, + title: $t('page.demos.access.menuVisible403'), + }, + }, + { + name: 'AccessSuperVisibleDemo', + path: '/demos/access/super-visible', + component: () => import('#/views/demos/access/super-visible.vue'), + meta: { + authority: ['super'], + icon: 'mdi:button-cursor', + title: $t('page.demos.access.superVisible'), + }, + }, + { + name: 'AccessAdminVisibleDemo', + path: '/demos/access/admin-visible', + component: () => import('#/views/demos/access/admin-visible.vue'), + meta: { + authority: ['admin'], + icon: 'mdi:button-cursor', + title: $t('page.demos.access.adminVisible'), + }, + }, + { + name: 'AccessUserVisibleDemo', + path: '/demos/access/user-visible', + component: () => import('#/views/demos/access/user-visible.vue'), + meta: { + authority: ['user'], + icon: 'mdi:button-cursor', + title: $t('page.demos.access.userVisible'), + }, + }, + ], + }, + // 功能 + { + meta: { + icon: 'mdi:feature-highlight', + title: $t('page.demos.features.title'), + }, + 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: '/demos/features/tabs', + component: () => import('#/views/demos/features/tabs/index.vue'), + meta: { + icon: 'lucide:app-window', + title: $t('page.demos.features.tabs'), + }, + }, + { + name: 'FeatureTabDetailDemo', + path: '/demos/features/tabs/detail/:id', + component: () => + import('#/views/demos/features/tabs/tab-detail.vue'), + meta: { + activePath: '/demos/features/tabs', + hideInMenu: true, + maxNumOfOpenTab: 3, + title: $t('page.demos.features.tabDetail'), + }, + }, + { + 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: $t('page.demos.features.hideChildrenInMenu'), + }, + children: [ + { + name: 'HideChildrenInMenuChildrenDemo', + path: '/demos/features/hide-menu-children/children', + component: () => + import( + '#/views/demos/features/hide-menu-children/children.vue' + ), + meta: { title: 'HideChildrenInMenuChildrenDemo' }, + }, + ], + }, + ], + }, + // 面包屑导航 + { + name: 'BreadcrumbDemos', + path: '/demos/breadcrumb', + meta: { + icon: 'lucide:navigation', + title: $t('page.demos.breadcrumb.navigation'), + }, + children: [ + { + name: 'BreadcrumbLateralDemo', + path: '/demos/breadcrumb/lateral', + component: () => import('#/views/demos/breadcrumb/lateral.vue'), + meta: { + icon: 'lucide:navigation', + title: $t('page.demos.breadcrumb.lateral'), + }, + }, + { + name: 'BreadcrumbLateralDetailDemo', + path: '/demos/breadcrumb/lateral-detail', + component: () => + import('#/views/demos/breadcrumb/lateral-detail.vue'), + meta: { + activePath: '/demos/breadcrumb/lateral', + hideInMenu: true, + title: $t('page.demos.breadcrumb.lateralDetail'), + }, + }, + { + name: 'BreadcrumbLevelDemo', + path: '/demos/breadcrumb/level', + meta: { + icon: 'lucide:navigation', + title: $t('page.demos.breadcrumb.level'), + }, + children: [ + { + name: 'BreadcrumbLevelDetailDemo', + path: '/demos/breadcrumb/level/detail', + component: () => + import('#/views/demos/breadcrumb/level-detail.vue'), + meta: { + title: $t('page.demos.breadcrumb.levelDetail'), + }, + }, + ], + }, + ], + }, + // 缺省页 + { + meta: { + icon: 'mdi:lightbulb-error-outline', + title: $t('page.demos.fallback.title'), + }, + name: 'FallbackDemos', + path: '/demos/fallback', + children: [ + { + name: 'Fallback403Demo', + path: '/demos/fallback/403', + component: () => import('#/views/_core/fallback/forbidden.vue'), + meta: { + icon: 'mdi:do-not-disturb-alt', + title: '403', + }, + }, + { + name: 'Fallback404Demo', + path: '/demos/fallback/404', + component: () => import('#/views/_core/fallback/not-found.vue'), + meta: { + icon: 'mdi:table-off', + title: '404', + }, + }, + { + name: 'Fallback500Demo', + path: '/demos/fallback/500', + component: () => + import('#/views/_core/fallback/internal-error.vue'), + meta: { + icon: 'mdi:server-network-off', + title: '500', + }, + }, + { + name: 'FallbackOfflineDemo', + path: '/demos/fallback/offline', + component: () => import('#/views/_core/fallback/offline.vue'), + meta: { + icon: 'mdi:offline', + title: $t('fallback.offline'), + }, + }, + ], + }, + // 菜单徽标 + { + meta: { + badgeType: 'dot', + badgeVariants: 'destructive', + icon: 'lucide:circle-dot', + title: $t('page.demos.badge.title'), + }, + name: 'BadgeDemos', + path: '/demos/badge', + children: [ + { + name: 'BadgeDotDemo', + component: () => import('#/views/demos/badge/index.vue'), + path: '/demos/badge/dot', + meta: { + badgeType: 'dot', + icon: 'lucide:square-dot', + title: $t('page.demos.badge.dot'), + }, + }, + { + name: 'BadgeTextDemo', + component: () => import('#/views/demos/badge/index.vue'), + path: '/demos/badge/text', + meta: { + badge: '10', + icon: 'lucide:square-dot', + title: $t('page.demos.badge.text'), + }, + }, + { + name: 'BadgeColorDemo', + component: () => import('#/views/demos/badge/index.vue'), + path: '/demos/badge/color', + meta: { + badge: 'Hot', + badgeVariants: 'destructive', + icon: 'lucide:square-dot', + title: $t('page.demos.badge.color'), + }, + }, + ], + }, + // 菜单激活图标 + { + 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: 'OutsideDemos', + path: '/demos/outside', + children: [ + { + name: 'IframeDemos', + path: '/demos/outside/iframe', + meta: { + icon: 'mdi:newspaper-variant-outline', + title: $t('page.demos.outside.embedded'), + }, + children: [ + { + name: 'VueDocumentDemo', + path: '/demos/outside/iframe/vue-document', + component: IFrameView, + meta: { + icon: 'logos:vue', + iframeSrc: 'https://cn.vuejs.org/', + keepAlive: true, + title: 'Vue', + }, + }, + { + name: 'TailwindcssDemo', + path: '/demos/outside/iframe/tailwindcss', + component: IFrameView, + meta: { + icon: 'devicon:tailwindcss', + iframeSrc: 'https://tailwindcss.com/', + // keepAlive: true, + title: 'Tailwindcss', + }, + }, + ], + }, + { + name: 'ExternalLinkDemos', + path: '/demos/outside/external-link', + meta: { + icon: 'mdi:newspaper-variant-multiple-outline', + title: $t('page.demos.outside.externalLink'), + }, + children: [ + { + name: 'ViteDemo', + path: '/demos/outside/external-link/vite', + component: IFrameView, + meta: { + icon: 'logos:vitejs', + link: 'https://vitejs.dev/', + title: 'Vite', + }, + }, + { + name: 'VueUseDemo', + path: '/demos/outside/external-link/vue-use', + component: IFrameView, + meta: { + icon: 'logos:vueuse', + link: 'https://vueuse.org', + title: 'VueUse', + }, + }, + ], + }, + ], + }, + // 嵌套菜单 + { + meta: { + icon: 'ic:round-menu', + title: $t('page.demos.nested.title'), + }, + name: 'NestedDemos', + path: '/demos/nested', + children: [ + { + name: 'Menu1Demo', + path: '/demos/nested/menu1', + component: () => import('#/views/demos/nested/menu-1.vue'), + meta: { + icon: 'ic:round-menu', + keepAlive: true, + title: $t('page.demos.nested.menu1'), + }, + }, + { + name: 'Menu2Demo', + path: '/demos/nested/menu2', + meta: { + icon: 'ic:round-menu', + keepAlive: true, + title: $t('page.demos.nested.menu2'), + }, + children: [ + { + name: 'Menu21Demo', + path: '/demos/nested/menu2/menu2-1', + component: () => import('#/views/demos/nested/menu-2-1.vue'), + meta: { + icon: 'ic:round-menu', + keepAlive: true, + title: $t('page.demos.nested.menu2_1'), + }, + }, + ], + }, + { + name: 'Menu3Demo', + path: '/demos/nested/menu3', + meta: { + icon: 'ic:round-menu', + title: $t('page.demos.nested.menu3'), + }, + children: [ + { + name: 'Menu31Demo', + path: 'menu3-1', + component: () => import('#/views/demos/nested/menu-3-1.vue'), + meta: { + icon: 'ic:round-menu', + keepAlive: true, + title: $t('page.demos.nested.menu3_1'), + }, + }, + { + name: 'Menu32Demo', + path: 'menu3-2', + meta: { + icon: 'ic:round-menu', + title: $t('page.demos.nested.menu3_2'), + }, + children: [ + { + name: 'Menu321Demo', + path: '/demos/nested/menu3/menu3-2/menu3-2-1', + component: () => + import('#/views/demos/nested/menu-3-2-1.vue'), + meta: { + icon: 'ic:round-menu', + keepAlive: true, + title: $t('page.demos.nested.menu3_2_1'), + }, + }, + ], + }, + ], + }, + ], + }, + ], + }, +]; + +export default routes; diff --git a/apps/web-antd/src/router/routes/modules/examples.ts b/playground/src/router/routes/modules/examples.ts similarity index 100% rename from apps/web-antd/src/router/routes/modules/examples.ts rename to playground/src/router/routes/modules/examples.ts diff --git a/playground/src/router/routes/modules/vben.ts b/playground/src/router/routes/modules/vben.ts new file mode 100644 index 00000000000..4dd75629a04 --- /dev/null +++ b/playground/src/router/routes/modules/vben.ts @@ -0,0 +1,90 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { + VBEN_ANT_PREVIEW_URL, + VBEN_DOC_URL, + VBEN_ELE_PREVIEW_URL, + VBEN_GITHUB_URL, + VBEN_LOGO_URL, + VBEN_NAIVE_PREVIEW_URL, +} from '@vben/constants'; + +import { BasicLayout, IFrameView } from '#/layouts'; +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + component: BasicLayout, + meta: { + badgeType: 'dot', + icon: VBEN_LOGO_URL, + order: 9999, + title: $t('page.vben.title'), + }, + name: 'VbenProject', + path: '/vben-admin', + children: [ + { + name: 'VbenAbout', + path: '/vben-admin/about', + component: () => import('#/views/_core/vben/about/index.vue'), + meta: { + icon: 'lucide:copyright', + title: $t('page.vben.about'), + }, + }, + { + name: 'VbenDocument', + path: '/vben-admin/document', + component: IFrameView, + meta: { + icon: 'lucide:book-open-text', + link: VBEN_DOC_URL, + title: $t('page.vben.document'), + }, + }, + { + name: 'VbenGithub', + path: '/vben-admin/github', + component: IFrameView, + meta: { + icon: 'mdi:github', + link: VBEN_GITHUB_URL, + title: 'Github', + }, + }, + { + name: 'VbenAntdv', + path: '/vben-admin/antdv', + component: IFrameView, + meta: { + badgeType: 'dot', + link: VBEN_ANT_PREVIEW_URL, + title: $t('page.vben.antdv'), + }, + }, + { + name: 'VbenNaive', + path: '/vben-admin/naive', + component: IFrameView, + meta: { + badgeType: 'dot', + link: VBEN_NAIVE_PREVIEW_URL, + title: $t('page.vben.naive-ui'), + }, + }, + { + name: 'VbenElementPlus', + path: '/vben-admin/ele', + component: IFrameView, + meta: { + badgeType: 'dot', + link: VBEN_ELE_PREVIEW_URL, + title: $t('page.vben.element-plus'), + }, + }, + ], + }, +]; + +export default routes; diff --git a/playground/src/store/auth.ts b/playground/src/store/auth.ts new file mode 100644 index 00000000000..59a640ca477 --- /dev/null +++ b/playground/src/store/auth.ts @@ -0,0 +1,111 @@ +import type { LoginAndRegisterParams } from '@vben/common-ui'; +import type { UserInfo } from '@vben/types'; + +import { ref } from 'vue'; +import { useRouter } from 'vue-router'; + +import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; +import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores'; + +import { notification } from 'ant-design-vue'; +import { defineStore } from 'pinia'; + +import { getAccessCodesApi, getUserInfoApi, loginApi } from '#/api'; +import { $t } from '#/locales'; + +export const useAuthStore = defineStore('auth', () => { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const router = useRouter(); + + const loginLoading = ref(false); + + /** + * 异步处理登录操作 + * Asynchronously handle the login process + * @param params 登录表单数据 + */ + async function authLogin( + params: LoginAndRegisterParams, + onSuccess?: () => Promise | void, + ) { + // 异步处理用户登录操作并获取 accessToken + let userInfo: null | UserInfo = null; + try { + loginLoading.value = true; + const { accessToken, refreshToken } = await loginApi(params); + + // 如果成功获取到 accessToken + if (accessToken) { + // 将 accessToken 存储到 accessStore 中 + accessStore.setAccessToken(accessToken); + accessStore.setRefreshToken(refreshToken); + + // 获取用户信息并存储到 accessStore 中 + const [fetchUserInfoResult, accessCodes] = await Promise.all([ + fetchUserInfo(), + getAccessCodesApi(), + ]); + + userInfo = fetchUserInfoResult; + + userStore.setUserInfo(userInfo); + accessStore.setAccessCodes(accessCodes); + + if (accessStore.loginExpired) { + accessStore.setLoginExpired(false); + } else { + onSuccess + ? await onSuccess?.() + : await router.push(userInfo.homePath || DEFAULT_HOME_PATH); + } + + if (userInfo?.realName) { + notification.success({ + description: `${$t('authentication.loginSuccessDesc')}:${userInfo?.realName}`, + duration: 3, + message: $t('authentication.loginSuccess'), + }); + } + } + } finally { + loginLoading.value = false; + } + + return { + userInfo, + }; + } + + async function logout() { + resetAllStores(); + accessStore.setLoginExpired(false); + + // 回登陆页带上当前路由地址 + await router.replace({ + path: LOGIN_PATH, + query: { + redirect: encodeURIComponent(router.currentRoute.value.fullPath), + }, + }); + } + + async function fetchUserInfo() { + let userInfo: null | UserInfo = null; + userInfo = await getUserInfoApi(); + userStore.setUserInfo(userInfo); + return userInfo; + } + + function $reset() { + loginLoading.value = false; + } + + return { + $reset, + authLogin, + fetchUserInfo, + loginLoading, + logout, + }; +}); diff --git a/playground/src/store/index.ts b/playground/src/store/index.ts new file mode 100644 index 00000000000..269586ee8b8 --- /dev/null +++ b/playground/src/store/index.ts @@ -0,0 +1 @@ +export * from './auth'; diff --git a/playground/src/views/_core/README.md b/playground/src/views/_core/README.md new file mode 100644 index 00000000000..8248afe6c10 --- /dev/null +++ b/playground/src/views/_core/README.md @@ -0,0 +1,3 @@ +# \_core + +此目录包含应用程序正常运行所需的基本视图。这些视图是应用程序布局中使用的视图。 diff --git a/playground/src/views/_core/authentication/code-login.vue b/playground/src/views/_core/authentication/code-login.vue new file mode 100644 index 00000000000..36cf50ec0f2 --- /dev/null +++ b/playground/src/views/_core/authentication/code-login.vue @@ -0,0 +1,30 @@ + + + diff --git a/playground/src/views/_core/authentication/forget-password.vue b/playground/src/views/_core/authentication/forget-password.vue new file mode 100644 index 00000000000..74ccc27ed8f --- /dev/null +++ b/playground/src/views/_core/authentication/forget-password.vue @@ -0,0 +1,23 @@ + + + diff --git a/playground/src/views/_core/authentication/login.vue b/playground/src/views/_core/authentication/login.vue new file mode 100644 index 00000000000..3bcdd9500af --- /dev/null +++ b/playground/src/views/_core/authentication/login.vue @@ -0,0 +1,18 @@ + + + diff --git a/playground/src/views/_core/authentication/qrcode-login.vue b/playground/src/views/_core/authentication/qrcode-login.vue new file mode 100644 index 00000000000..23f5f2dad58 --- /dev/null +++ b/playground/src/views/_core/authentication/qrcode-login.vue @@ -0,0 +1,10 @@ + + + diff --git a/playground/src/views/_core/authentication/register.vue b/playground/src/views/_core/authentication/register.vue new file mode 100644 index 00000000000..f6b227d9628 --- /dev/null +++ b/playground/src/views/_core/authentication/register.vue @@ -0,0 +1,25 @@ + + + diff --git a/apps/web-antd/src/views/demos/nested/menu-1.vue b/playground/src/views/_core/fallback/coming-soon.vue similarity index 100% rename from apps/web-antd/src/views/demos/nested/menu-1.vue rename to playground/src/views/_core/fallback/coming-soon.vue diff --git a/playground/src/views/_core/fallback/forbidden.vue b/playground/src/views/_core/fallback/forbidden.vue new file mode 100644 index 00000000000..8ea65fedb52 --- /dev/null +++ b/playground/src/views/_core/fallback/forbidden.vue @@ -0,0 +1,9 @@ + + + diff --git a/playground/src/views/_core/fallback/internal-error.vue b/playground/src/views/_core/fallback/internal-error.vue new file mode 100644 index 00000000000..819a47d5ea8 --- /dev/null +++ b/playground/src/views/_core/fallback/internal-error.vue @@ -0,0 +1,9 @@ + + + diff --git a/playground/src/views/_core/fallback/not-found.vue b/playground/src/views/_core/fallback/not-found.vue new file mode 100644 index 00000000000..4d178e9cbd4 --- /dev/null +++ b/playground/src/views/_core/fallback/not-found.vue @@ -0,0 +1,9 @@ + + + diff --git a/playground/src/views/_core/fallback/offline.vue b/playground/src/views/_core/fallback/offline.vue new file mode 100644 index 00000000000..5de4a88de4e --- /dev/null +++ b/playground/src/views/_core/fallback/offline.vue @@ -0,0 +1,9 @@ + + + diff --git a/playground/src/views/_core/vben/about/index.vue b/playground/src/views/_core/vben/about/index.vue new file mode 100644 index 00000000000..0ee524335c2 --- /dev/null +++ b/playground/src/views/_core/vben/about/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/playground/src/views/dashboard/analytics/analytics-trends.vue b/playground/src/views/dashboard/analytics/analytics-trends.vue new file mode 100644 index 00000000000..1bef4d4ec1b --- /dev/null +++ b/playground/src/views/dashboard/analytics/analytics-trends.vue @@ -0,0 +1,78 @@ + + + diff --git a/playground/src/views/dashboard/analytics/analytics-visits-data.vue b/playground/src/views/dashboard/analytics/analytics-visits-data.vue new file mode 100644 index 00000000000..f03907d0260 --- /dev/null +++ b/playground/src/views/dashboard/analytics/analytics-visits-data.vue @@ -0,0 +1,80 @@ + + + diff --git a/playground/src/views/dashboard/analytics/analytics-visits-sales.vue b/playground/src/views/dashboard/analytics/analytics-visits-sales.vue new file mode 100644 index 00000000000..0529a3b0504 --- /dev/null +++ b/playground/src/views/dashboard/analytics/analytics-visits-sales.vue @@ -0,0 +1,44 @@ + + + diff --git a/playground/src/views/dashboard/analytics/analytics-visits-source.vue b/playground/src/views/dashboard/analytics/analytics-visits-source.vue new file mode 100644 index 00000000000..99292bf9de0 --- /dev/null +++ b/playground/src/views/dashboard/analytics/analytics-visits-source.vue @@ -0,0 +1,63 @@ + + + diff --git a/playground/src/views/dashboard/analytics/analytics-visits.vue b/playground/src/views/dashboard/analytics/analytics-visits.vue new file mode 100644 index 00000000000..4dfc163fd71 --- /dev/null +++ b/playground/src/views/dashboard/analytics/analytics-visits.vue @@ -0,0 +1,53 @@ + + + diff --git a/playground/src/views/dashboard/analytics/index.vue b/playground/src/views/dashboard/analytics/index.vue new file mode 100644 index 00000000000..00b34df1e26 --- /dev/null +++ b/playground/src/views/dashboard/analytics/index.vue @@ -0,0 +1,90 @@ + + + diff --git a/playground/src/views/dashboard/workspace/index.vue b/playground/src/views/dashboard/workspace/index.vue new file mode 100644 index 00000000000..c8ae02737be --- /dev/null +++ b/playground/src/views/dashboard/workspace/index.vue @@ -0,0 +1,225 @@ + + + diff --git a/apps/web-antd/src/views/demos/access/admin-visible.vue b/playground/src/views/demos/access/admin-visible.vue similarity index 100% rename from apps/web-antd/src/views/demos/access/admin-visible.vue rename to playground/src/views/demos/access/admin-visible.vue diff --git a/apps/web-antd/src/views/demos/access/button-control.vue b/playground/src/views/demos/access/button-control.vue similarity index 100% rename from apps/web-antd/src/views/demos/access/button-control.vue rename to playground/src/views/demos/access/button-control.vue diff --git a/apps/web-antd/src/views/demos/access/index.vue b/playground/src/views/demos/access/index.vue similarity index 100% rename from apps/web-antd/src/views/demos/access/index.vue rename to playground/src/views/demos/access/index.vue diff --git a/apps/web-antd/src/views/demos/access/menu-visible-403.vue b/playground/src/views/demos/access/menu-visible-403.vue similarity index 100% rename from apps/web-antd/src/views/demos/access/menu-visible-403.vue rename to playground/src/views/demos/access/menu-visible-403.vue diff --git a/apps/web-antd/src/views/demos/access/super-visible.vue b/playground/src/views/demos/access/super-visible.vue similarity index 100% rename from apps/web-antd/src/views/demos/access/super-visible.vue rename to playground/src/views/demos/access/super-visible.vue diff --git a/apps/web-antd/src/views/demos/access/user-visible.vue b/playground/src/views/demos/access/user-visible.vue similarity index 100% rename from apps/web-antd/src/views/demos/access/user-visible.vue rename to playground/src/views/demos/access/user-visible.vue diff --git a/apps/web-antd/src/views/demos/active-icon/index.vue b/playground/src/views/demos/active-icon/index.vue similarity index 100% rename from apps/web-antd/src/views/demos/active-icon/index.vue rename to playground/src/views/demos/active-icon/index.vue diff --git a/apps/web-antd/src/views/demos/badge/index.vue b/playground/src/views/demos/badge/index.vue similarity index 100% rename from apps/web-antd/src/views/demos/badge/index.vue rename to playground/src/views/demos/badge/index.vue diff --git a/apps/web-antd/src/views/demos/breadcrumb/lateral-detail.vue b/playground/src/views/demos/breadcrumb/lateral-detail.vue similarity index 100% rename from apps/web-antd/src/views/demos/breadcrumb/lateral-detail.vue rename to playground/src/views/demos/breadcrumb/lateral-detail.vue diff --git a/apps/web-antd/src/views/demos/breadcrumb/lateral.vue b/playground/src/views/demos/breadcrumb/lateral.vue similarity index 100% rename from apps/web-antd/src/views/demos/breadcrumb/lateral.vue rename to playground/src/views/demos/breadcrumb/lateral.vue diff --git a/apps/web-antd/src/views/demos/breadcrumb/level-detail.vue b/playground/src/views/demos/breadcrumb/level-detail.vue similarity index 100% rename from apps/web-antd/src/views/demos/breadcrumb/level-detail.vue rename to playground/src/views/demos/breadcrumb/level-detail.vue diff --git a/apps/web-antd/src/views/demos/features/hide-menu-children/children.vue b/playground/src/views/demos/features/hide-menu-children/children.vue similarity index 100% rename from apps/web-antd/src/views/demos/features/hide-menu-children/children.vue rename to playground/src/views/demos/features/hide-menu-children/children.vue diff --git a/apps/web-antd/src/views/demos/features/hide-menu-children/parent.vue b/playground/src/views/demos/features/hide-menu-children/parent.vue similarity index 100% rename from apps/web-antd/src/views/demos/features/hide-menu-children/parent.vue rename to playground/src/views/demos/features/hide-menu-children/parent.vue diff --git a/apps/web-antd/src/views/demos/features/icons/index.vue b/playground/src/views/demos/features/icons/index.vue similarity index 100% rename from apps/web-antd/src/views/demos/features/icons/index.vue rename to playground/src/views/demos/features/icons/index.vue diff --git a/apps/web-antd/src/views/demos/features/login-expired/index.vue b/playground/src/views/demos/features/login-expired/index.vue similarity index 100% rename from apps/web-antd/src/views/demos/features/login-expired/index.vue rename to playground/src/views/demos/features/login-expired/index.vue diff --git a/apps/web-antd/src/views/demos/features/tabs/index.vue b/playground/src/views/demos/features/tabs/index.vue similarity index 100% rename from apps/web-antd/src/views/demos/features/tabs/index.vue rename to playground/src/views/demos/features/tabs/index.vue diff --git a/apps/web-antd/src/views/demos/features/tabs/tab-detail.vue b/playground/src/views/demos/features/tabs/tab-detail.vue similarity index 100% rename from apps/web-antd/src/views/demos/features/tabs/tab-detail.vue rename to playground/src/views/demos/features/tabs/tab-detail.vue diff --git a/apps/web-antd/src/views/demos/features/watermark/index.vue b/playground/src/views/demos/features/watermark/index.vue similarity index 100% rename from apps/web-antd/src/views/demos/features/watermark/index.vue rename to playground/src/views/demos/features/watermark/index.vue diff --git a/apps/web-antd/src/views/demos/nested/menu-2-1.vue b/playground/src/views/demos/nested/menu-1.vue similarity index 100% rename from apps/web-antd/src/views/demos/nested/menu-2-1.vue rename to playground/src/views/demos/nested/menu-1.vue diff --git a/apps/web-antd/src/views/demos/nested/menu-3-1.vue b/playground/src/views/demos/nested/menu-2-1.vue similarity index 100% rename from apps/web-antd/src/views/demos/nested/menu-3-1.vue rename to playground/src/views/demos/nested/menu-2-1.vue diff --git a/apps/web-antd/src/views/demos/nested/menu-3-2-1.vue b/playground/src/views/demos/nested/menu-3-1.vue similarity index 100% rename from apps/web-antd/src/views/demos/nested/menu-3-2-1.vue rename to playground/src/views/demos/nested/menu-3-1.vue diff --git a/playground/src/views/demos/nested/menu-3-2-1.vue b/playground/src/views/demos/nested/menu-3-2-1.vue new file mode 100644 index 00000000000..f394930f21a --- /dev/null +++ b/playground/src/views/demos/nested/menu-3-2-1.vue @@ -0,0 +1,7 @@ + + + diff --git a/apps/web-antd/src/views/examples/ellipsis/data.ts b/playground/src/views/examples/ellipsis/data.ts similarity index 100% rename from apps/web-antd/src/views/examples/ellipsis/data.ts rename to playground/src/views/examples/ellipsis/data.ts diff --git a/apps/web-antd/src/views/examples/ellipsis/index.vue b/playground/src/views/examples/ellipsis/index.vue similarity index 100% rename from apps/web-antd/src/views/examples/ellipsis/index.vue rename to playground/src/views/examples/ellipsis/index.vue diff --git a/playground/tailwind.config.mjs b/playground/tailwind.config.mjs new file mode 100644 index 00000000000..f17f556fa9f --- /dev/null +++ b/playground/tailwind.config.mjs @@ -0,0 +1 @@ +export { default } from '@vben/tailwind-config'; diff --git a/playground/tsconfig.json b/playground/tsconfig.json new file mode 100644 index 00000000000..02c287fe642 --- /dev/null +++ b/playground/tsconfig.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web-app.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "#/*": ["./src/*"] + } + }, + "references": [{ "path": "./tsconfig.node.json" }], + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/playground/tsconfig.node.json b/playground/tsconfig.node.json new file mode 100644 index 00000000000..c2f0d86cc78 --- /dev/null +++ b/playground/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/node.json", + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "noEmit": false + }, + "include": ["vite.config.mts"] +} diff --git a/playground/vite.config.mts b/playground/vite.config.mts new file mode 100644 index 00000000000..b6360f1d4ac --- /dev/null +++ b/playground/vite.config.mts @@ -0,0 +1,20 @@ +import { defineConfig } from '@vben/vite-config'; + +export default defineConfig(async () => { + return { + application: {}, + vite: { + server: { + proxy: { + '/api': { + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), + // mock代理目标地址 + target: 'http://localhost:5320/api', + ws: true, + }, + }, + }, + }, + }; +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 51a96ccfed2..3bed673ab60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1071,6 +1071,69 @@ importers: specifier: ^4.4.3 version: 4.4.3(vue@3.4.37(typescript@5.5.4)) + playground: + dependencies: + '@vben/access': + specifier: workspace:* + version: link:../packages/effects/access + '@vben/chart-ui': + specifier: workspace:* + version: link:../packages/effects/chart-ui + '@vben/common-ui': + specifier: workspace:* + version: link:../packages/effects/common-ui + '@vben/constants': + specifier: workspace:* + version: link:../packages/constants + '@vben/hooks': + specifier: workspace:* + version: link:../packages/effects/hooks + '@vben/icons': + specifier: workspace:* + version: link:../packages/icons + '@vben/layouts': + specifier: workspace:* + version: link:../packages/effects/layouts + '@vben/locales': + specifier: workspace:* + version: link:../packages/locales + '@vben/preferences': + specifier: workspace:* + version: link:../packages/preferences + '@vben/request': + specifier: workspace:* + version: link:../packages/effects/request + '@vben/stores': + specifier: workspace:* + version: link:../packages/stores + '@vben/styles': + specifier: workspace:* + version: link:../packages/styles + '@vben/types': + specifier: workspace:* + version: link:../packages/types + '@vben/utils': + specifier: workspace:* + version: link:../packages/utils + '@vueuse/core': + specifier: ^10.11.1 + version: 10.11.1(vue@3.4.37(typescript@5.5.4)) + ant-design-vue: + specifier: ^4.2.3 + version: 4.2.3(vue@3.4.37(typescript@5.5.4)) + dayjs: + specifier: ^1.11.12 + version: 1.11.12 + pinia: + specifier: 2.2.1 + version: 2.2.1(typescript@5.5.4)(vue@3.4.37(typescript@5.5.4)) + vue: + specifier: ^3.4.37 + version: 3.4.37(typescript@5.5.4) + vue-router: + specifier: ^4.4.3 + version: 4.4.3(vue@3.4.37(typescript@5.5.4)) + scripts/turbo-run: dependencies: '@clack/prompts': @@ -3281,7 +3344,6 @@ packages: '@ls-lint/ls-lint@2.2.3': resolution: {integrity: sha512-ekM12jNm/7O2I/hsRv9HvYkRdfrHpiV1epVuI2NP+eTIcEgdIdKkKCs9KgQydu/8R5YXTov9aHdOgplmCHLupw==} - cpu: [x64, arm64, s390x] os: [darwin, linux, win32] hasBin: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index cfbcb24543a..885e9c06f6c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,3 +11,4 @@ packages: - "apps/*" - "scripts/*" - "docs" + - "playground" diff --git a/vben-admin.code-workspace b/vben-admin.code-workspace index 445c0f3214c..dc3372568a9 100644 --- a/vben-admin.code-workspace +++ b/vben-admin.code-workspace @@ -148,6 +148,10 @@ "name": "@vben/utils", "path": "packages/utils", }, + { + "name": "@vben/playground", + "path": "playground", + }, { "name": "@vben/turbo-run", "path": "scripts/turbo-run", From 058ce2459b6f0fd3c69766c10ed730ff5113d1d2 Mon Sep 17 00:00:00 2001 From: vben Date: Sun, 11 Aug 2024 10:46:45 +0800 Subject: [PATCH 3/5] perf: logic optimization --- apps/web-antd/src/api/request.ts | 2 +- apps/web-antd/src/locales/index.ts | 6 +++++- .../src/views/dashboard/analytics/analytics-trends.vue | 2 +- .../src/views/dashboard/analytics/analytics-visits.vue | 2 +- apps/web-ele/src/api/request.ts | 2 +- apps/web-ele/src/locales/index.ts | 6 +++++- .../src/views/dashboard/analytics/analytics-trends.vue | 2 +- .../src/views/dashboard/analytics/analytics-visits.vue | 2 +- apps/web-naive/src/api/request.ts | 2 +- .../src/views/dashboard/analytics/analytics-trends.vue | 2 +- .../src/views/dashboard/analytics/analytics-visits.vue | 2 +- docs/src/guide/essentials/server.md | 2 +- docs/src/guide/in-depth/locale.md | 6 +++++- playground/src/api/request.ts | 2 +- playground/src/locales/index.ts | 6 +++++- .../src/views/dashboard/analytics/analytics-trends.vue | 2 +- .../src/views/dashboard/analytics/analytics-visits.vue | 2 +- 17 files changed, 33 insertions(+), 17 deletions(-) diff --git a/apps/web-antd/src/api/request.ts b/apps/web-antd/src/api/request.ts index 7cd11e27de7..19b0e250da7 100644 --- a/apps/web-antd/src/api/request.ts +++ b/apps/web-antd/src/api/request.ts @@ -59,7 +59,7 @@ function createRequestClient(baseURL: string) { if (status >= 200 && status < 400 && code === 0) { return data; } - throw new Error(msg); + throw new Error(`Error ${status}: ${msg}`); }); return client; } diff --git a/apps/web-antd/src/locales/index.ts b/apps/web-antd/src/locales/index.ts index ef3b8d5c76c..4ee7511b9ef 100644 --- a/apps/web-antd/src/locales/index.ts +++ b/apps/web-antd/src/locales/index.ts @@ -58,7 +58,11 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) { locale = await import('dayjs/locale/en'); } } - dayjs.locale(locale); + if (locale) { + dayjs.locale(locale); + } else { + console.error(`Failed to load dayjs locale for ${lang}`); + } } /** 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 1bef4d4ec1b..261c847ce91 100644 --- a/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue @@ -13,7 +13,7 @@ onMounted(() => { containLabel: true, left: '1%', right: '1%', - top: '2 %', + top: '2 %', }, series: [ { 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 4dfc163fd71..8230f6e5c7e 100644 --- a/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue @@ -13,7 +13,7 @@ onMounted(() => { containLabel: true, left: '1%', right: '1%', - top: '2 %', + top: '2 %', }, series: [ { diff --git a/apps/web-ele/src/api/request.ts b/apps/web-ele/src/api/request.ts index ac6c0f9b6a3..dd7816508dc 100644 --- a/apps/web-ele/src/api/request.ts +++ b/apps/web-ele/src/api/request.ts @@ -59,7 +59,7 @@ function createRequestClient(baseURL: string) { if (status >= 200 && status < 400 && code === 0) { return data; } - throw new Error(msg); + throw new Error(`Error ${status}: ${msg}`); }); return client; } diff --git a/apps/web-ele/src/locales/index.ts b/apps/web-ele/src/locales/index.ts index 186935e09e2..695cff56e09 100644 --- a/apps/web-ele/src/locales/index.ts +++ b/apps/web-ele/src/locales/index.ts @@ -58,7 +58,11 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) { locale = await import('dayjs/locale/en'); } } - dayjs.locale(locale); + if (locale) { + dayjs.locale(locale); + } else { + console.error(`Failed to load dayjs locale for ${lang}`); + } } /** diff --git a/apps/web-ele/src/views/dashboard/analytics/analytics-trends.vue b/apps/web-ele/src/views/dashboard/analytics/analytics-trends.vue index 1bef4d4ec1b..261c847ce91 100644 --- a/apps/web-ele/src/views/dashboard/analytics/analytics-trends.vue +++ b/apps/web-ele/src/views/dashboard/analytics/analytics-trends.vue @@ -13,7 +13,7 @@ onMounted(() => { containLabel: true, left: '1%', right: '1%', - top: '2 %', + top: '2 %', }, series: [ { diff --git a/apps/web-ele/src/views/dashboard/analytics/analytics-visits.vue b/apps/web-ele/src/views/dashboard/analytics/analytics-visits.vue index 4dfc163fd71..8230f6e5c7e 100644 --- a/apps/web-ele/src/views/dashboard/analytics/analytics-visits.vue +++ b/apps/web-ele/src/views/dashboard/analytics/analytics-visits.vue @@ -13,7 +13,7 @@ onMounted(() => { containLabel: true, left: '1%', right: '1%', - top: '2 %', + top: '2 %', }, series: [ { diff --git a/apps/web-naive/src/api/request.ts b/apps/web-naive/src/api/request.ts index 524576be5c0..6ebd6d930a3 100644 --- a/apps/web-naive/src/api/request.ts +++ b/apps/web-naive/src/api/request.ts @@ -58,7 +58,7 @@ function createRequestClient(baseURL: string) { if (status >= 200 && status < 400 && code === 0) { return data; } - throw new Error(msg); + throw new Error(`Error ${status}: ${msg}`); }); return client; } diff --git a/apps/web-naive/src/views/dashboard/analytics/analytics-trends.vue b/apps/web-naive/src/views/dashboard/analytics/analytics-trends.vue index 1bef4d4ec1b..261c847ce91 100644 --- a/apps/web-naive/src/views/dashboard/analytics/analytics-trends.vue +++ b/apps/web-naive/src/views/dashboard/analytics/analytics-trends.vue @@ -13,7 +13,7 @@ onMounted(() => { containLabel: true, left: '1%', right: '1%', - top: '2 %', + top: '2 %', }, series: [ { diff --git a/apps/web-naive/src/views/dashboard/analytics/analytics-visits.vue b/apps/web-naive/src/views/dashboard/analytics/analytics-visits.vue index 4dfc163fd71..8230f6e5c7e 100644 --- a/apps/web-naive/src/views/dashboard/analytics/analytics-visits.vue +++ b/apps/web-naive/src/views/dashboard/analytics/analytics-visits.vue @@ -13,7 +13,7 @@ onMounted(() => { containLabel: true, left: '1%', right: '1%', - top: '2 %', + top: '2 %', }, series: [ { diff --git a/docs/src/guide/essentials/server.md b/docs/src/guide/essentials/server.md index 5fe13967886..26a83934126 100644 --- a/docs/src/guide/essentials/server.md +++ b/docs/src/guide/essentials/server.md @@ -221,7 +221,7 @@ function createRequestClient(baseURL: string) { if (status >= 200 && status < 400 && code === 0) { return data; } - throw new Error(msg); + throw new Error(`Error ${status}: ${msg}`); }); return client; } diff --git a/docs/src/guide/in-depth/locale.md b/docs/src/guide/in-depth/locale.md index 69370e1571d..66626b7e26c 100644 --- a/docs/src/guide/in-depth/locale.md +++ b/docs/src/guide/in-depth/locale.md @@ -198,7 +198,11 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) { locale = await import('dayjs/locale/en'); } } - dayjs.locale(locale); + if (locale) { + dayjs.locale(locale); + } else { + console.error(`Failed to load dayjs locale for ${lang}`); + } } ``` diff --git a/playground/src/api/request.ts b/playground/src/api/request.ts index 7cd11e27de7..19b0e250da7 100644 --- a/playground/src/api/request.ts +++ b/playground/src/api/request.ts @@ -59,7 +59,7 @@ function createRequestClient(baseURL: string) { if (status >= 200 && status < 400 && code === 0) { return data; } - throw new Error(msg); + throw new Error(`Error ${status}: ${msg}`); }); return client; } diff --git a/playground/src/locales/index.ts b/playground/src/locales/index.ts index ef3b8d5c76c..4ee7511b9ef 100644 --- a/playground/src/locales/index.ts +++ b/playground/src/locales/index.ts @@ -58,7 +58,11 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) { locale = await import('dayjs/locale/en'); } } - dayjs.locale(locale); + if (locale) { + dayjs.locale(locale); + } else { + console.error(`Failed to load dayjs locale for ${lang}`); + } } /** diff --git a/playground/src/views/dashboard/analytics/analytics-trends.vue b/playground/src/views/dashboard/analytics/analytics-trends.vue index 1bef4d4ec1b..261c847ce91 100644 --- a/playground/src/views/dashboard/analytics/analytics-trends.vue +++ b/playground/src/views/dashboard/analytics/analytics-trends.vue @@ -13,7 +13,7 @@ onMounted(() => { containLabel: true, left: '1%', right: '1%', - top: '2 %', + top: '2 %', }, series: [ { diff --git a/playground/src/views/dashboard/analytics/analytics-visits.vue b/playground/src/views/dashboard/analytics/analytics-visits.vue index 4dfc163fd71..8230f6e5c7e 100644 --- a/playground/src/views/dashboard/analytics/analytics-visits.vue +++ b/playground/src/views/dashboard/analytics/analytics-visits.vue @@ -13,7 +13,7 @@ onMounted(() => { containLabel: true, left: '1%', right: '1%', - top: '2 %', + top: '2 %', }, series: [ { From 394199d1ff3b1176f6e4cfdfb3fad818f8abbae5 Mon Sep 17 00:00:00 2001 From: vben Date: Sun, 11 Aug 2024 12:54:45 +0800 Subject: [PATCH 4/5] chore: update docs --- .github/contributing.md | 45 ++++++++++-- README.ja-JP.md | 3 +- README.md | 3 +- README.zh-CN.md | 7 ++ docs/.vitepress/config.mts | 1 + .../theme/components/vben-contributors.vue | 5 +- docs/src/guide/project/dir.md | 68 +++++++++++++++++++ 7 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 docs/src/guide/project/dir.md diff --git a/.github/contributing.md b/.github/contributing.md index 9a5211b2a5a..f22370f3ba7 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -1,5 +1,42 @@ -# Contributing Guide +# Vben Admin Contributing Guide -1. Make sure you put things in the right category! -2. Always add your items to the end of a list. To be fair, the order is first-come-first-serve. -3. If you think something belongs in the wrong category, or think there needs to be a new category, feel free to edit things too. +Hi! We're really excited that you are interested in contributing to Vben Admin. Before submitting your contribution, please make sure to take a moment and read through the following guidelines: + +- [Pull Request Guidelines](#pull-request-guidelines) + +## Contributor Code of Conduct + +As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of the level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. + +## Pull Request Guidelines + +- Checkout a topic branch from the relevant branch, e.g. main, and merge back against that branch. + +- If adding a new feature: + + - Provide a convincing reason to add this feature. Ideally, you should open a suggestion issue first and have it approved before working on it. + +- If fixing bug: + + - Provide a detailed description of the bug in the PR. Live demo preferred. + +- It's OK to have multiple small commits as you work on the PR - GitHub can automatically squash them before merging. + +## Development Setup + +You will need [pnpm](https://pnpm.io/) + +After cloning the repo, run: + +```bash +# install the dependencies of the project +$ pnpm install +# start the project +$ pnpm run dev +``` diff --git a/README.ja-JP.md b/README.ja-JP.md index f794c792a2c..700f8357fbc 100644 --- a/README.ja-JP.md +++ b/README.ja-JP.md @@ -134,7 +134,8 @@ pnpm build ## 貢献者 - + Contributors ## Discord diff --git a/README.md b/README.md index 5ce954bc08d..7cf502ed1bf 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,8 @@ If you think this project is helpful to you, you can help the author buy a cup o ## Contributor - + Contributors ## Discord diff --git a/README.zh-CN.md b/README.zh-CN.md index 9137ef6eb77..73e8fbeb961 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -126,6 +126,13 @@ pnpm build Paypal Me +## Contributor + + + Contributors + + ## Discord - [Github Discussions](https://github.com/anncwb/vue-vben-admin/discussions) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 296e2e042c0..af1ab4a35ec 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -288,6 +288,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] { items: [ { link: 'project/standard', text: '规范' }, { link: 'project/cli', text: 'CLI' }, + { link: 'project/dir', text: '目录说明' }, { link: 'project/test', text: '单元测试' }, { link: 'project/tailwindcss', text: 'Tailwind CSS' }, { link: 'project/changeset', text: 'Changeset' }, diff --git a/docs/.vitepress/theme/components/vben-contributors.vue b/docs/.vitepress/theme/components/vben-contributors.vue index 987098b16fd..9b887d925ec 100644 --- a/docs/.vitepress/theme/components/vben-contributors.vue +++ b/docs/.vitepress/theme/components/vben-contributors.vue @@ -4,7 +4,10 @@

Contributors

- + Contributors
diff --git a/docs/src/guide/project/dir.md b/docs/src/guide/project/dir.md new file mode 100644 index 00000000000..3b32fedaff3 --- /dev/null +++ b/docs/src/guide/project/dir.md @@ -0,0 +1,68 @@ +# 目录说明 + +目录使用 Monorepo 管理,项目结构如下: + +```bash +. +├── Dockerfile # Docker 镜像构建文件 +├── README.md # 项目说明文档 +├── apps # 项目应用目录 +│   ├── backend-mock # 后端模拟服务应用 +│   ├── web-antd # 基于 Ant Design Vue 的前端应用 +│   ├── web-ele # 基于 Element Plus 的前端应用 +│   └── web-naive # 基于 Naive UI 的前端应用 +├── build-local-docker-image.sh # 本地构建 Docker 镜像脚本 +├── cspell.json # CSpell 配置文件 +├── docs # 项目文档目录 +├── eslint.config.mjs # ESLint 配置文件 +├── internal # 内部工具目录 +│   ├── lint-configs # 代码检查配置 +│   │   ├── commitlint-config # Commitlint 配置 +│   │   ├── eslint-config # ESLint 配置 +│   │   ├── prettier-config # Prettier 配置 +│   │   └── stylelint-config # Stylelint 配置 +│   ├── node-utils # Node.js 工具 +│   ├── tailwind-config # Tailwind 配置 +│   ├── tsconfig # 通用 tsconfig 配置 +│   └── vite-config # 通用Vite 配置 +├── package.json # 项目依赖配置 +├── packages # 项目包目录 +│   ├── @core # 核心包 +│   │   ├── base # 基础包 +│   │   │   ├── design # 设计相关 +│   │   │   ├── icons # 图标 +│   │   │   ├── shared # 共享 +│   │   │   └── typings # 类型定义 +│   │   ├── composables # 组合式 API +│   │   ├── preferences # 偏好设置 +│   │   └── ui-kit # UI 组件集合 +│   │   ├── layout-ui # 布局 UI +│   │   ├── menu-ui # 菜单 UI +│   │   ├── shadcn-ui # shadcn UI +│   │   └── tabs-ui # 标签页 UI +│   ├── constants # 常量 +│   ├── effects # 副作用相关包 +│   │   ├── access # 访问控制 +│   │   ├── chart-ui # 图表 UI +│   │   ├── common-ui # 通用 UI +│   │   ├── hooks # 组合式 API +│   │   ├── layouts # 布局 +│   │   └── request # 请求 +│   ├── icons # 图标 +│   ├── locales # 国际化 +│   ├── preferences # 偏好设置 +│   ├── stores # 状态管理 +│   ├── styles # 样式 +│   ├── types # 类型定义 +│   └── utils # 工具 +├── playground # 演示目录 +├── pnpm-lock.yaml # pnpm 锁定文件 +├── pnpm-workspace.yaml # pnpm 工作区配置文件 +├── scripts # 脚本目录 +│   ├── turbo-run # Turbo 运行脚本 +│   └── vsh # VSH 脚本 +├── stylelint.config.mjs # Stylelint 配置文件 +├── turbo.json # Turbo 配置文件 +├── vben-admin.code-workspace # VS Code 工作区配置文件 +└── vitest.config.ts # Vite 配置文件 +``` From 7fcb0a5e0135824daf8389922ecb68f20212d37d Mon Sep 17 00:00:00 2001 From: vben Date: Sun, 11 Aug 2024 15:48:25 +0800 Subject: [PATCH 5/5] chore: update docs --- docs/.vitepress/config.mts | 1 + docs/src/guide/introduction/quick-start.md | 15 +++++- docs/src/guide/introduction/thin.md | 62 ++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 docs/src/guide/introduction/thin.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index af1ab4a35ec..78f9ed27bcd 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -254,6 +254,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] { text: '为什么选择我们?', }, { link: 'introduction/quick-start', text: '快速开始' }, + { link: 'introduction/thin', text: '精简版本' }, ], }, { diff --git a/docs/src/guide/introduction/quick-start.md b/docs/src/guide/introduction/quick-start.md index 47e86160abe..852414f6a7b 100644 --- a/docs/src/guide/introduction/quick-start.md +++ b/docs/src/guide/introduction/quick-start.md @@ -72,11 +72,24 @@ pnpm install ### 运行项目 -执行以下命令即可运行项目: +执行以下命运行项目: ```bash # 启动项目 pnpm dev ``` +此时,你会看到类似如下的输出,选择你需要运行的项目: + +```bash +│ +◆ Select the app you need to run [dev]: +│ ● @vben/web-antd +│ ○ @vben/web-ele +│ ○ @vben/web-naive +│ ○ @vben/docs +│ ○ @vben/playground +└ +``` + 现在,你可以在浏览器访问 `http://localhost:5555` 查看项目。 diff --git a/docs/src/guide/introduction/thin.md b/docs/src/guide/introduction/thin.md new file mode 100644 index 00000000000..818227e906e --- /dev/null +++ b/docs/src/guide/introduction/thin.md @@ -0,0 +1,62 @@ +# 精简版本 + +从 `5.0` 版本开始,我们不再提供精简的仓库或者分支。我们的目标是提供一个更加一致的开发体验,同时减少维护成本。在这里,我们将如何介绍自己的项目,如何去精简以及移除不需要的功能。 + +## 应用精简 + +首先,确认你需要的 `UI` 组件库版本,然后删除对应的应用,比如你选择使用 `Ant Design Vue`,那么你可以删除其他应用, 只需要删除下面两个文件夹即可: + +```bash +apps/web-ele +apps/web-native + +``` + +::: tip + +如果项目没有内置你需要的 `UI` 组件库应用,你可以直接全部删除其他应用。然后自行新建应用即可。 + +::: + +## 演示代码精简 + +如果你不需要演示代码,你可以直接删除的`playground`文件夹。 + +## 文档精简 + +如果你不需要文档,你可以直接删除`docs`文件夹。 + +## Mock 服务精简 + +如果你不需要`Mock`服务,你可以直接删除`apps/backend-mock`文件夹。同时在你的应用下`.env.development`文件中删除`VITE_NITRO_MOCK`变量。 + +```bash +# 是否开启 Nitro Mock服务,true 为开启,false 为关闭 +VITE_NITRO_MOCK=false +``` + +## 安装依赖 + +到这里,你已经完成了精简操作,接下来你可以安装依赖,并启动你的项目: + +```bash +# 根目录下执行 +pnpm install + +``` + +## 命令调整 + +在精简后,你可能需要根据你的项目调整命令,在根目录下的`package.json`文件中,你可以调整`scripts`字段,移除你不需要的命令。 + +```json +{ + "scripts": { + "dev:antd": "pnpm -F @vben/web-antd run dev", + "dev:docs": "pnpm -F @vben/docs run dev", + "dev:ele": "pnpm -F @vben/web-ele run dev", + "dev:play": "pnpm -F @vben/playground run dev", + "dev:naive": "pnpm -F @vben/web-naive run dev" + } +} +```