Compare commits

..

69 Commits

Author SHA1 Message Date
Soybean
1f6d079644 chore: release v0.10.2 2023-06-01 02:24:57 +08:00
Soybean
5c085a1986 fix(components): fix mix-menu layout when the locale is English (fixed 241) 2023-06-01 02:24:26 +08:00
Soybean
9a23817473 chore(projects): update deps and use soy lint-staged replace lint-staged 2023-06-01 02:10:07 +08:00
Soybean
56ea8937f6 docs(projects): fix README.md: example image link 2023-05-31 09:28:34 +08:00
Soybean
4f51263501 docs(projects): update README.md: update example image url [更新示例图片的链接] 2023-05-31 02:32:06 +08:00
Soybean
bb2eab60f4 docs(projects): CHANGELOG.md 2023-05-30 18:20:40 +00:00
Soybean
44e4c04811 chore: release v0.10.1 2023-05-31 02:19:56 +08:00
Soybean
b5839eab26 docs(projects): update README.md 2023-05-31 02:18:19 +08:00
Soybean
780ac75bf6 chore(projects): add switch for pageRoute plugin [添加自动生成路由的插件的开关] 2023-05-31 01:52:57 +08:00
Soybean
a252138594 docs(projects): CHANGELOG.md 2023-05-30 17:47:56 +00:00
Soybean
270a055072 chore: release v0.10.0 2023-05-31 01:47:13 +08:00
Soybean
08e194efe9 perf(projects): move changing document title by locale to global event of composables & add appLoading unmount 2023-05-31 01:44:49 +08:00
Soybean
5f6caab338 docs(projects): update CHANGELOG.md 2023-05-31 01:35:04 +08:00
Soybean
5aaa318142 chore(projects): remove useless packages, update lint-staged config, add githublogen 2023-05-31 01:33:46 +08:00
Soybean
cebbef680f chore(deps): update deps 2023-05-31 01:08:31 +08:00
Soybean
0abde46ef4 fix(projects): hide the drawer when it is initial mobile mode [初始化时为移动端布局则隐藏侧边栏] fixed #238 2023-05-26 13:08:35 +08:00
Soybean
f2b518ed26 feat(projects): support mobile layout [支持移动端布局] 2023-05-26 03:27:41 +08:00
Soybean
c6207f35e1 Merge pull request #237 from abstain23/fix-i18n
fix(projects): 修复面包屑导航下拉菜单语言显示问题
2023-05-25 21:13:45 +08:00
cc
ee8fa04814 fix(projects): 修复面包屑导航下拉菜单语言显示问题 2023-05-25 21:11:42 +08:00
Soybean
7b746fa053 perf(projects): complete dynamic route translate [补充动态路由的翻译] 2023-05-24 23:17:29 +08:00
Soybean
b7fea53107 Merge pull request #234 from chhinsras/main
Add Khmer Translation
2023-05-24 23:03:23 +08:00
Chhin Sras
f89f3e6a38 Added Khmer Translation 2023-05-24 21:07:35 +07:00
Chhin Sras
a0da2f6e16 Delete actions-template-sync.yml 2023-05-24 15:52:05 +07:00
Chhin Sras
3b5380e0d1 Update Sras.md 2023-05-24 15:38:43 +07:00
Chhin Sras
35276bfe41 Update and rename .github/workflows/actions-template-sync.yml to actions-template-sync.yml 2023-05-24 15:30:10 +07:00
Chhin Sras
215c1ecbd9 Update and rename sync-from-template.yml to actions-template-sync.yml 2023-05-24 15:24:51 +07:00
Chhin Sras
1698b21d7a Create Sras.md 2023-05-24 15:18:35 +07:00
Chhin Sras
ca1e66be47 Rename sync-from-template to sync-from-template.yml 2023-05-24 15:13:58 +07:00
Chhin Sras
22bf2823e8 Update and rename actions-template-sync to sync-from-template 2023-05-24 15:12:03 +07:00
Chhin Sras
32e98f1b3a Create actions-template-sync 2023-05-24 15:06:44 +07:00
Soybean
c1c4335ce7 build(projects): update deps and fix style [升级依赖&修复代码格式] 2023-05-24 00:17:00 +08:00
Soybean
6c50662280 Merge pull request #233 from fast-crud/优化README.md
优化readme.md
2023-05-24 00:12:44 +08:00
xiaojunnuo
f3a1707b94 docs(projects): readme.md 二次开发的项目内容换行 2023-05-23 17:52:06 +08:00
xiaojunnuo
6ea755f2a8 docs(projects): 优化README.md 2023-05-23 17:43:00 +08:00
Soybean
a989b44a15 docs(projects): update README.md [更新README.md] 2023-05-22 23:04:57 +08:00
Soybean
40f8587fd6 build(deps): update deps [升级依赖] 2023-05-21 22:35:30 +08:00
Soybean
9f5638f16d fix(projects): add prod mockjs switch [添加生产模式的mockjs的开关] 2023-05-17 07:55:12 +08:00
Soybean
9b19f96ff6 fix(projects): fix mockjs [修复mockjs] 2023-05-16 22:29:35 +08:00
Soybean
15da557892 Merge pull request #230 from kirklin/fix_tsconfig
fix(projects): tsconfig missing isolatedModules
2023-05-16 19:36:04 +08:00
Soybean
2d23c9a2e6 Merge pull request #229 from kirklin/vue3.3
refactor(projects): upgrade vue3.3, official support defineOptions
2023-05-16 19:34:16 +08:00
Kirk Lin
ab49afd3db fix(projects): tsconfig missing isolatedModules 2023-05-16 17:39:05 +08:00
Kirk Lin
86a370fd69 refactor(projects): upgrade vue3.3, official support defineOptions 2023-05-16 17:32:38 +08:00
Soybean
36fc74ce07 Merge pull request #226 from abstain23/feat/theme-transtion
feat(projects): 增加主题切换过渡效果
2023-05-14 16:49:26 +08:00
cc
8da8843fd0 feat(projects): 增加主题切换过渡效果 2023-05-14 16:35:38 +08:00
Soybean
f68285fbe5 feat(projects): add menu translate [翻译菜单] 2023-05-13 14:20:06 +08:00
Soybean
85b8ef8f88 Merge pull request #225 from abstain23/feat-i18n
feat(projects): 增加i18n支持翻译菜单,tab,title
2023-05-13 13:12:28 +08:00
cc
3d48aa8bbe feat(projects): 增加i18n支持翻译菜单,tab,title 2023-05-13 12:58:35 +08:00
Soybean
a765da6e28 docs(projects): update README.md [更新README.md] 2023-05-10 22:23:18 +08:00
Soybean
c57640acd0 fix(projects): fix better-mock usage [修复better-mock用法] 2023-05-06 19:41:55 +08:00
Soybean
c264216053 build(deps): update deps [升级依赖] 2023-05-06 19:35:54 +08:00
Soybean
9d3c732993 refactor(projects): use better-mock replace mockjs [用better-mock替换mockjs] 2023-05-06 19:34:31 +08:00
Soybean
34f023c4b1 build(projects): update deps and fix type error [升级依赖并修复类型问题] 2023-05-04 19:02:20 +08:00
Soybean
5a4f842774 docs(projects): update README.md [更新README.md] 2023-04-26 08:40:07 +08:00
Soybean
bae1767141 build(deps): update deps [升级依赖] 2023-04-26 08:32:54 +08:00
Soybean
5957833a4f fix(projects): fix router guide [修复路由跳转异常] fixed #216 2023-04-21 00:46:03 +08:00
Soybean
397092c21f docs(projects): update README.md [更新README.md] 2023-04-20 01:41:07 +08:00
Soybean
f309003e67 refactor(projects): remove page examples: tree [去除tree相关示例页面] 2023-04-20 01:37:52 +08:00
Soybean
eaf3678758 build(deps): update deps and remove vite-plugin-html [升级依赖,去除vite-plugin-html] 2023-04-20 01:32:53 +08:00
Soybean
f2e82da7c8 build(deps): update deps [升级依赖] 2023-04-19 09:10:36 +08:00
Soybean
211ae1f905 refactor(projects): update useTable 2023-04-04 19:19:13 +08:00
Soybean
db629593c6 build(deps): update deps 2023-04-02 12:57:36 +08:00
Soybean
80d58cce2b Merge pull request #210 from SonyLeo/feature/tree
Feature/tree
2023-04-02 12:56:00 +08:00
small_happy
a0f55aca69 feat(components): Add routing data related to tree components and page display optimization 2023-04-02 10:45:51 +08:00
small_happy
d203a3586c feat(components): Add tree related component instances 2023-04-02 10:07:50 +08:00
Soybean
f74a6424d0 docs(projects): add qq to README.md [文档添加QQ群] 2023-03-15 18:14:25 +08:00
Soybean
209ef3d890 style(projects): per style [完善样式] 2023-03-15 09:01:06 +08:00
Soybean
b549b32cbb Merge pull request #205 from yanbowe/main
feat(projects): fix to top [返回顶部功能适配新布局]
2023-03-14 22:31:41 +08:00
燕博文
54e2cb51cf feat(projects): 返回顶部功能适配新布局 2023-03-14 13:50:23 +08:00
Soybean
42e6de395f build(projects): remove old layout,tab package [去除旧的布局和页签依赖] 2023-03-14 00:34:47 +08:00
94 changed files with 4725 additions and 5053 deletions

View File

@@ -1 +1,2 @@
VITE_HTTP_PROXY=Y VITE_HTTP_PROXY=Y
VITE_SOYBEAN_ROUTE_PLUGIN=Y

View File

@@ -6,3 +6,5 @@ VITE_COMPRESS=N
VITE_COMPRESS_TYPE=gzip VITE_COMPRESS_TYPE=gzip
VITE_PWA=N VITE_PWA=N
VITE_PROD_MOCK=Y

View File

@@ -1,27 +1,25 @@
name: Release name: Release
on:
push:
tags:
- "v*.**"
permissions: permissions:
contents: write contents: write
on:
push:
tags:
- "v*"
jobs: jobs:
release: release:
name: Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Node.js - uses: actions/setup-node@v3
uses: actions/setup-node@v3
with: with:
node-version: 16.x node-version: 16.x
- name: Create github releases - run: npx githublogen
run: npx changelogithub
env: env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

2
.npmrc
View File

@@ -1,4 +1,2 @@
registry=https://registry.npmmirror.com/ registry=https://registry.npmmirror.com/
shamefully-hoist=true shamefully-hoist=true
strict-peer-dependencies=false
auto-install-peers=true

View File

@@ -39,7 +39,6 @@
"@": "${workspaceFolder}/src", "@": "${workspaceFolder}/src",
"~@": "${workspaceFolder}/src" "~@": "${workspaceFolder}/src"
}, },
"terminal.integrated.cursorStyle": "line",
"terminal.integrated.fontSize": 14, "terminal.integrated.fontSize": 14,
"terminal.integrated.fontWeight": 500, "terminal.integrated.fontWeight": 500,
"terminal.integrated.tabs.enabled": true, "terminal.integrated.tabs.enabled": true,
@@ -71,5 +70,6 @@
}, },
"[vue]": { "[vue]": {
"editor.defaultFormatter": "Vue.volar" "editor.defaultFormatter": "Vue.volar"
} },
"i18n-ally.localesPaths": ["src/locales", "src/locales/lang"]
} }

View File

@@ -1,6 +1,100 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. ## v0.10.0...v0.10.1
[compare changes](https://github.com/honghuangdc/soybean-admin/compare/v0.10.0...v0.10.1)
### 📖 Documentation
- **projects:** CHANGELOG.md ([a252138](https://github.com/honghuangdc/soybean-admin/commit/a252138))
- **projects:** Update README.md ([b5839ea](https://github.com/honghuangdc/soybean-admin/commit/b5839ea))
### 🏡 Chore
- **projects:** Add switch for pageRoute plugin [添加自动生成路由的插件的开关] ([780ac75](https://github.com/honghuangdc/soybean-admin/commit/780ac75))
- Release v0.10.1 ([44e4c04](https://github.com/honghuangdc/soybean-admin/commit/44e4c04))
### ❤️ Contributors
- Soybean ([@soybeanjs](http://github.com/soybeanjs))
## v0.9.9...v0.10.0
[compare changes](https://github.com/honghuangdc/soybean-admin/compare/v0.9.9...v0.10.0)
### 🚀 Features
- **projects:** 返回顶部功能适配新布局 ([54e2cb5](https://github.com/honghuangdc/soybean-admin/commit/54e2cb5))
- **components:** Add tree related component instances ([d203a35](https://github.com/honghuangdc/soybean-admin/commit/d203a35))
- **components:** Add routing data related to tree components and page display optimization ([a0f55ac](https://github.com/honghuangdc/soybean-admin/commit/a0f55ac))
- **projects:** 增加i18n支持翻译菜单,tab,title ([3d48aa8](https://github.com/honghuangdc/soybean-admin/commit/3d48aa8))
- **projects:** Add menu translate [翻译菜单] ([f68285f](https://github.com/honghuangdc/soybean-admin/commit/f68285f))
- **projects:** 增加主题切换过渡效果 ([8da8843](https://github.com/honghuangdc/soybean-admin/commit/8da8843))
- **projects:** Support mobile layout [支持移动端布局] ([f2b518e](https://github.com/honghuangdc/soybean-admin/commit/f2b518e))
### 🐞 Bug Fixes
- **projects:** Fix router guide [修复路由跳转异常] fixed #216 ([#216](https://github.com/honghuangdc/soybean-admin/issues/216))
- **projects:** Fix better-mock usage [修复better-mock用法] ([c57640a](https://github.com/honghuangdc/soybean-admin/commit/c57640a))
- **projects:** Tsconfig missing isolatedModules ([ab49afd](https://github.com/honghuangdc/soybean-admin/commit/ab49afd))
- **projects:** Fix mockjs [修复mockjs] ([9b19f96](https://github.com/honghuangdc/soybean-admin/commit/9b19f96))
- **projects:** Add prod mockjs switch [添加生产模式的mockjs的开关] ([9f5638f](https://github.com/honghuangdc/soybean-admin/commit/9f5638f))
- **projects:** 修复面包屑导航下拉菜单语言显示问题 ([ee8fa04](https://github.com/honghuangdc/soybean-admin/commit/ee8fa04))
- **projects:** Hide the drawer when it is initial mobile mode [初始化时为移动端布局则隐藏侧边栏] fixed #238 ([#238](https://github.com/honghuangdc/soybean-admin/issues/238))
### 🔥 Performance
- **projects:** Complete dynamic route translate [补充动态路由的翻译] ([7b746fa](https://github.com/honghuangdc/soybean-admin/commit/7b746fa))
- **projects:** Move changing document title by locale to global event of composables & add appLoading unmount ([08e194e](https://github.com/honghuangdc/soybean-admin/commit/08e194e))
### 💅 Refactors
- **projects:** Update useTable ([211ae1f](https://github.com/honghuangdc/soybean-admin/commit/211ae1f))
- **projects:** Remove page examples: tree [去除tree相关示例页面] ([f309003](https://github.com/honghuangdc/soybean-admin/commit/f309003))
- **projects:** Use better-mock replace mockjs [用better-mock替换mockjs] ([9d3c732](https://github.com/honghuangdc/soybean-admin/commit/9d3c732))
- **projects:** Upgrade vue3.3, official support defineOptions ([86a370f](https://github.com/honghuangdc/soybean-admin/commit/86a370f))
### 📖 Documentation
- **projects:** Add qq to README.md [文档添加QQ群] ([f74a642](https://github.com/honghuangdc/soybean-admin/commit/f74a642))
- **projects:** Update README.md [更新README.md] ([397092c](https://github.com/honghuangdc/soybean-admin/commit/397092c))
- **projects:** Update README.md [更新README.md] ([5a4f842](https://github.com/honghuangdc/soybean-admin/commit/5a4f842))
- **projects:** Update README.md [更新README.md] ([a765da6](https://github.com/honghuangdc/soybean-admin/commit/a765da6))
- **projects:** Update README.md [更新README.md] ([a989b44](https://github.com/honghuangdc/soybean-admin/commit/a989b44))
- **projects:** 优化README.md ([6ea755f](https://github.com/honghuangdc/soybean-admin/commit/6ea755f))
- **projects:** Readme.md 二次开发的项目内容换行 ([f3a1707](https://github.com/honghuangdc/soybean-admin/commit/f3a1707))
- **projects:** Update CHANGELOG.md ([5f6caab](https://github.com/honghuangdc/soybean-admin/commit/5f6caab))
### 📦 Build
- **projects:** Remove old layout,tab package [去除旧的布局和页签依赖] ([42e6de3](https://github.com/honghuangdc/soybean-admin/commit/42e6de3))
- **deps:** Update deps ([db62959](https://github.com/honghuangdc/soybean-admin/commit/db62959))
- **deps:** Update deps [升级依赖] ([f2e82da](https://github.com/honghuangdc/soybean-admin/commit/f2e82da))
- **deps:** Update deps and remove vite-plugin-html [升级依赖去除vite-plugin-html] ([eaf3678](https://github.com/honghuangdc/soybean-admin/commit/eaf3678))
- **deps:** Update deps [升级依赖] ([bae1767](https://github.com/honghuangdc/soybean-admin/commit/bae1767))
- **projects:** Update deps and fix type error [升级依赖并修复类型问题] ([34f023c](https://github.com/honghuangdc/soybean-admin/commit/34f023c))
- **deps:** Update deps [升级依赖] ([c264216](https://github.com/honghuangdc/soybean-admin/commit/c264216))
- **deps:** Update deps [升级依赖] ([40f8587](https://github.com/honghuangdc/soybean-admin/commit/40f8587))
- **projects:** Update deps and fix style [升级依赖&修复代码格式] ([c1c4335](https://github.com/honghuangdc/soybean-admin/commit/c1c4335))
### 🏡 Chore
- **deps:** Update deps ([cebbef6](https://github.com/honghuangdc/soybean-admin/commit/cebbef6))
- **projects:** Remove useless packages, update lint-staged config, add githublogen ([5aaa318](https://github.com/honghuangdc/soybean-admin/commit/5aaa318))
- Release v0.10.0 ([270a055](https://github.com/honghuangdc/soybean-admin/commit/270a055))
### 🎨 Styles
- **projects:** Per style [完善样式] ([209ef3d](https://github.com/honghuangdc/soybean-admin/commit/209ef3d))
### ❤️ Contributors
- Soybean ([@soybeanjs](http://github.com/soybeanjs))
- Cc <cc@qq.com>
- Xiaojunnuo ([@greper](http://github.com/greper))
- Kirk Lin ([@kirklin](http://github.com/kirklin))
- Small_happy <5304122+small_happy@user.noreply.gitee.com>
- 燕博文 <349952469@qq.com>
### [0.9.9](https://github.com/honghuangdc/soybean-admin/compare/v0.9.8...v0.9.9) (2023-03-13) ### [0.9.9](https://github.com/honghuangdc/soybean-admin/compare/v0.9.8...v0.9.9) (2023-03-13)

View File

@@ -29,12 +29,12 @@
## 代码仓库 ## 代码仓库
- [github](https://github.com/honghuangdc/soybean-admin) | 仓库 | github 地址 | gitee 镜像 | 预览 |
- [tauri 版](https://github.com/honghuangdc/soybean-admin/tree/tauri) | -------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------- |
- [精简版](https://github.com/honghuangdc/soybean-admin/tree/thin) | soybean-admin | [github](https://github.com/honghuangdc/soybean-admin) | [gitee](https://gitee.com/honghuangdc/soybean-admin) | [预览](https://soybean.pro/) |
- [gitee](https://gitee.com/honghuangdc/soybean-admin) | tauri 版 | [tauri 版](https://github.com/honghuangdc/soybean-admin/tree/tauri) | [tauri 版](https://gitee.com/honghuangdc/soybean-admin/tree/tauri) | |
- [tauri 版](https://gitee.com/honghuangdc/soybean-admin/tree/tauri) | 精简版 | [精简版](https://github.com/honghuangdc/soybean-admin/tree/thin) | [精简版](https://gitee.com/honghuangdc/soybean-admin/tree/thin) | |
- [精简版](https://gitee.com/honghuangdc/soybean-admin/tree/thin) | 集成 fast-crud | [集成 fast-crud](https://github.com/honghuangdc/soybean-admin/tree/fast-crud) | [集成 fast-crud](https://gitee.com/honghuangdc/soybean-admin/tree/fast-crud) | [预览](http://fast-crud.docmirror.cn/soybean/#/crud/demo) |
## 更新日志 ## 更新日志
@@ -48,25 +48,26 @@
![](https://s2.loli.net/2022/05/16/keOtgFH27r9nqYS.png) ![](https://s2.loli.net/2022/05/16/keOtgFH27r9nqYS.png)
![](https://s2.loli.net/2022/05/18/bW7mftiQexkvSTG.png) ![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin02-v2.png)
![](https://s2.loli.net/2022/05/16/uV5nzjb3gYptAEl.png) ![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin03-v2.png)
![](https://s2.loli.net/2022/05/16/rSnNHLdpuvkKxWq.png) ![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin04-v2.png)
![](https://s2.loli.net/2022/05/18/Mt6YZqmDxO8v4uR.png) ![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin05-v2.png)
![](https://s2.loli.net/2022/05/16/ktH5dcG3fuFOoKA.png) ![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin06-v2.png)
![](https://s2.loli.net/2022/05/16/VPl6Ru1iCAhLcS4.png) ![](https://s2.loli.net/2022/05/16/VPl6Ru1iCAhLcS4.png)
![](https://s2.loli.net/2022/05/16/bRlAKuHW7ZVh9DT.png) ![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin08-v2.png)
![](https://s2.loli.net/2022/06/07/rY8TyAftM5dxspv.png) ![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin09-v2.png)
![](https://s2.loli.net/2022/06/07/5GNBAd31IzQVjLP.png) <div align="center">
<img style="width:380px;margin-right:18px;border:1px solid #dedede;" src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin10-v2.png" />
![](https://s2.loli.net/2022/06/07/rRSG6mEZpujOACT.png) <img style="width:380px;border:1px solid #dedede;" src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin11-v2.png" />
</div>
## 安装使用 ## 安装使用
@@ -120,7 +121,9 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
项目已用 simple-git-hooks 代替了 husky, 旧版本用了 husky执行 pnpm soy init-git-hooks 进行初始化配置 项目已用 simple-git-hooks 代替了 husky, 旧版本用了 husky执行 pnpm soy init-git-hooks 进行初始化配置
## 基于 SoybeanAdmin 二次开发的项目 ## 基于 SoybeanAdmin 二次开发的项目
[electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统帮助前端开发伙伴快速实现接口的mock。
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。
## 浏览器支持 ## 浏览器支持
@@ -138,12 +141,12 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
## 交流 ## 交流
`Soybean Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群(人员已满),使用问题欢迎在群内提问。 `Soybean Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。
<div style="display:flex;"> <div style="display:flex;">
<div style="padding-right:24px;"> <div style="padding-right:24px;">
<p>微信交流群</p> <p>QQ交流群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybeanjs-wechat2.jpeg" style="width:200px" /> <img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin.jpg" style="width:200px" />
</div> </div>
<div> <div>
<p>添加本人微信,欢迎来技术交流,业务咨询</p> <p>添加本人微信,欢迎来技术交流,业务咨询</p>

View File

@@ -1,14 +0,0 @@
import type { PluginOption } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';
export default (viteEnv: ImportMetaEnv): PluginOption[] => {
return createHtmlPlugin({
minify: true,
inject: {
data: {
appName: viteEnv.VITE_APP_NAME,
appTitle: viteEnv.VITE_APP_TITLE
}
}
});
};

View File

@@ -4,7 +4,6 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
import unocss from '@unocss/vite'; import unocss from '@unocss/vite';
import progress from 'vite-plugin-progress'; import progress from 'vite-plugin-progress';
import pageRoute from '@soybeanjs/vite-plugin-vue-page-route'; import pageRoute from '@soybeanjs/vite-plugin-vue-page-route';
import html from './html';
import unplugin from './unplugin'; import unplugin from './unplugin';
import mock from './mock'; import mock from './mock';
import visualizer from './visualizer'; import visualizer from './visualizer';
@@ -16,7 +15,18 @@ import pwa from './pwa';
* @param viteEnv - 环境变量配置 * @param viteEnv - 环境变量配置
*/ */
export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | PluginOption[])[] { export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | PluginOption[])[] {
const plugins = [vue(), vueJsx(), html(viteEnv), ...unplugin(viteEnv), unocss(), mock, progress(), pageRoute()]; const plugins = [
vue({
script: {
defineModel: true
}
}),
vueJsx(),
...unplugin(viteEnv),
unocss(),
mock(viteEnv),
progress()
];
if (viteEnv.VITE_VISUALIZER === 'Y') { if (viteEnv.VITE_VISUALIZER === 'Y') {
plugins.push(visualizer as PluginOption); plugins.push(visualizer as PluginOption);
@@ -27,6 +37,9 @@ export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | Plugin
if (viteEnv.VITE_PWA === 'Y' || viteEnv.VITE_VERCEL === 'Y') { if (viteEnv.VITE_PWA === 'Y' || viteEnv.VITE_VERCEL === 'Y') {
plugins.push(pwa()); plugins.push(pwa());
} }
if (viteEnv.VITE_SOYBEAN_ROUTE_PLUGIN === 'Y') {
plugins.push(pageRoute());
}
return plugins; return plugins;
} }

View File

@@ -1,9 +1,14 @@
import { viteMockServe } from 'vite-plugin-mock'; import { viteMockServe } from 'vite-plugin-mock';
export default viteMockServe({ export default (viteEnv: ImportMetaEnv) => {
mockPath: 'mock', const prodMock = viteEnv.VITE_PROD_MOCK === 'Y';
injectCode: `
import { setupMockServer } from '../mock'; return viteMockServe({
setupMockServer(); mockPath: 'mock',
` prodEnabled: prodMock,
}); injectCode: `
import { setupMockServer } from '../mock';
setupMockServer();
`
});
};

View File

@@ -1,4 +1,3 @@
import VueMacros from 'unplugin-vue-macros/vite';
import Icons from 'unplugin-icons/vite'; import Icons from 'unplugin-icons/vite';
import IconsResolver from 'unplugin-icons/resolver'; import IconsResolver from 'unplugin-icons/resolver';
import Components from 'unplugin-vue-components/vite'; import Components from 'unplugin-vue-components/vite';
@@ -17,7 +16,6 @@ export default function unplugin(viteEnv: ImportMetaEnv) {
const collectionName = VITE_ICON_LOCAL_PREFFIX.replace(`${VITE_ICON_PREFFIX}-`, ''); const collectionName = VITE_ICON_LOCAL_PREFFIX.replace(`${VITE_ICON_PREFFIX}-`, '');
return [ return [
VueMacros({}),
Icons({ Icons({
compiler: 'vue3', compiler: 'vue3',
customCollections: { customCollections: {

View File

@@ -1,21 +0,0 @@
{
"types": {
"feat": { "title": "🚀 Features" },
"perf": { "title": "🔥 Performance" },
"fix": { "title": "🩹 Fixes" },
"refactor": { "title": "💅 Refactors" },
"docs": { "title": "📖 Documentation" },
"types": { "title": "🌊 Types" },
"chore": { "title": "🏡 Chore" },
"test": { "title": "🧪 Tests" },
"style": { "title": "🎨 Styles" },
"ci": { "title": "🤖 CI" }
},
"scopeMap": {},
"titles": {
"breakingChanges": "🚨 Breaking Changes"
},
"contributors": true,
"capitalize": true,
"group": true
}

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.svg" /> <link rel="icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= appName %></title> <title>%VITE_APP_NAME%</title>
</head> </head>
<body> <body>
<div id="app"> <div id="app">

View File

@@ -12,7 +12,8 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
meta: { meta: {
title: '分析页', title: '分析页',
requiresAuth: true, requiresAuth: true,
icon: 'icon-park-outline:analysis' icon: 'icon-park-outline:analysis',
i18nTitle: 'message.routes.dashboard.analysis'
} }
}, },
{ {
@@ -22,14 +23,16 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
meta: { meta: {
title: '工作台', title: '工作台',
requiresAuth: true, requiresAuth: true,
icon: 'icon-park-outline:workbench' icon: 'icon-park-outline:workbench',
i18nTitle: 'message.routes.dashboard.workbench'
} }
} }
], ],
meta: { meta: {
title: '仪表盘', title: '仪表盘',
icon: 'mdi:monitor-dashboard', icon: 'mdi:monitor-dashboard',
order: 1 order: 1,
i18nTitle: 'message.routes.dashboard._value'
} }
}, },
{ {
@@ -43,6 +46,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'vue文档', title: 'vue文档',
i18nTitle: 'message.routes.document.vue',
requiresAuth: true, requiresAuth: true,
icon: 'logos:vue' icon: 'logos:vue'
} }
@@ -53,6 +57,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'vite文档', title: 'vite文档',
i18nTitle: 'message.routes.document.vite',
requiresAuth: true, requiresAuth: true,
icon: 'logos:vitejs' icon: 'logos:vitejs'
} }
@@ -63,6 +68,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'naive文档', title: 'naive文档',
i18nTitle: 'message.routes.document.naive',
requiresAuth: true, requiresAuth: true,
icon: 'logos:naiveui' icon: 'logos:naiveui'
} }
@@ -73,6 +79,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '项目文档', title: '项目文档',
i18nTitle: 'message.routes.document.project',
requiresAuth: true, requiresAuth: true,
localIcon: 'logo' localIcon: 'logo'
} }
@@ -82,6 +89,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
path: '/document/project-link', path: '/document/project-link',
meta: { meta: {
title: '项目文档(外链)', title: '项目文档(外链)',
i18nTitle: 'message.routes.document.project-link',
requiresAuth: true, requiresAuth: true,
localIcon: 'logo', localIcon: 'logo',
href: 'https://docs.soybean.pro/' href: 'https://docs.soybean.pro/'
@@ -90,6 +98,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '文档', title: '文档',
i18nTitle: 'message.routes.document._value',
icon: 'mdi:file-document-multiple-outline', icon: 'mdi:file-document-multiple-outline',
order: 2 order: 2
} }
@@ -105,6 +114,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '按钮', title: '按钮',
i18nTitle: 'message.routes.component.button',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:button-cursor' icon: 'mdi:button-cursor'
} }
@@ -115,6 +125,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '卡片', title: '卡片',
i18nTitle: 'message.routes.component.card',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:card-outline' icon: 'mdi:card-outline'
} }
@@ -125,6 +136,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '表格', title: '表格',
i18nTitle: 'message.routes.component.table',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:table-large' icon: 'mdi:table-large'
} }
@@ -132,6 +144,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '组件示例', title: '组件示例',
i18nTitle: 'message.routes.component._value',
icon: 'cib:app-store', icon: 'cib:app-store',
order: 3 order: 3
} }
@@ -152,6 +165,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'ECharts', title: 'ECharts',
i18nTitle: 'message.routes.plugin.charts.echarts',
requiresAuth: true, requiresAuth: true,
icon: 'simple-icons:apacheecharts' icon: 'simple-icons:apacheecharts'
} }
@@ -162,6 +176,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'AntV', title: 'AntV',
i18nTitle: 'message.routes.plugin.charts.antv',
requiresAuth: true, requiresAuth: true,
icon: 'simple-icons:antdesign' icon: 'simple-icons:antdesign'
} }
@@ -169,6 +184,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '图表', title: '图表',
i18nTitle: 'message.routes.plugin.charts._value',
icon: 'mdi:chart-areaspline' icon: 'mdi:chart-areaspline'
} }
}, },
@@ -178,6 +194,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '地图', title: '地图',
i18nTitle: 'message.routes.plugin.map',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:map' icon: 'mdi:map'
} }
@@ -188,6 +205,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '视频', title: '视频',
i18nTitle: 'message.routes.plugin.video',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:video' icon: 'mdi:video'
} }
@@ -203,6 +221,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '富文本编辑器', title: '富文本编辑器',
i18nTitle: 'message.routes.plugin.editor.quill',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:file-document-edit-outline' icon: 'mdi:file-document-edit-outline'
} }
@@ -213,6 +232,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'markdown编辑器', title: 'markdown编辑器',
i18nTitle: 'message.routes.plugin.editor.markdown',
requiresAuth: true, requiresAuth: true,
icon: 'ri:markdown-line' icon: 'ri:markdown-line'
} }
@@ -220,6 +240,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '编辑器', title: '编辑器',
i18nTitle: 'message.routes.plugin.editor._value',
icon: 'icon-park-outline:editor' icon: 'icon-park-outline:editor'
} }
}, },
@@ -229,6 +250,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'Swiper插件', title: 'Swiper插件',
i18nTitle: 'message.routes.plugin.swiper',
requiresAuth: true, requiresAuth: true,
icon: 'simple-icons:swiper' icon: 'simple-icons:swiper'
} }
@@ -239,6 +261,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '剪贴板', title: '剪贴板',
i18nTitle: 'message.routes.plugin.copy',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:clipboard-outline' icon: 'mdi:clipboard-outline'
} }
@@ -249,6 +272,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '图标', title: '图标',
i18nTitle: 'message.routes.plugin.icon',
requiresAuth: true, requiresAuth: true,
localIcon: 'custom-icon' localIcon: 'custom-icon'
} }
@@ -259,6 +283,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '打印', title: '打印',
i18nTitle: 'message.routes.plugin.print',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:printer' icon: 'mdi:printer'
} }
@@ -266,6 +291,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '插件示例', title: '插件示例',
i18nTitle: 'message.routes.plugin._value',
icon: 'clarity:plugin-line', icon: 'clarity:plugin-line',
order: 4 order: 4
} }
@@ -281,6 +307,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '权限切换', title: '权限切换',
i18nTitle: 'message.routes.auth-demo.permission',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-construction' icon: 'ic:round-construction'
} }
@@ -291,6 +318,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '超级管理员可见', title: '超级管理员可见',
i18nTitle: 'message.routes.auth-demo.super',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-supervisor-account' icon: 'ic:round-supervisor-account'
} }
@@ -298,6 +326,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '权限示例', title: '权限示例',
i18nTitle: 'message.routes.auth-demo._value',
icon: 'ic:baseline-security', icon: 'ic:baseline-security',
order: 5 order: 5
} }
@@ -313,6 +342,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'Tab', title: 'Tab',
i18nTitle: 'message.routes.function.tab',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-tab' icon: 'ic:round-tab'
} }
@@ -345,6 +375,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '功能', title: '功能',
i18nTitle: 'message.routes.function._value',
icon: 'icon-park-outline:all-application', icon: 'icon-park-outline:all-application',
order: 6 order: 6
} }
@@ -360,6 +391,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '异常页403', title: '异常页403',
i18nTitle: 'message.routes.exception.403',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-block' icon: 'ic:baseline-block'
} }
@@ -370,6 +402,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '异常页404', title: '异常页404',
i18nTitle: 'message.routes.exception.404',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-web-asset-off' icon: 'ic:baseline-web-asset-off'
} }
@@ -380,12 +413,14 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '异常页500', title: '异常页500',
i18nTitle: 'message.routes.exception.500',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-wifi-off' icon: 'ic:baseline-wifi-off'
} }
} }
], ],
meta: { meta: {
i18nTitle: 'message.routes.exception._value',
title: '异常页', title: '异常页',
icon: 'ant-design:exception-outlined', icon: 'ant-design:exception-outlined',
order: 7 order: 7
@@ -407,6 +442,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '二级菜单', title: '二级菜单',
i18nTitle: 'message.routes.multi-menu.first.second',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:menu' icon: 'mdi:menu'
} }
@@ -422,6 +458,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '三级菜单', title: '三级菜单',
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:menu' icon: 'mdi:menu'
} }
@@ -429,18 +466,21 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '二级菜单(有子菜单)', title: '二级菜单(有子菜单)',
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
icon: 'mdi:menu' icon: 'mdi:menu'
} }
} }
], ],
meta: { meta: {
title: '一级菜单', title: '一级菜单',
i18nTitle: 'message.routes.multi-menu.first._value',
icon: 'mdi:menu' icon: 'mdi:menu'
} }
} }
], ],
meta: { meta: {
title: '多级菜单', title: '多级菜单',
i18nTitle: 'message.routes.multi-menu._value',
icon: 'carbon:menu', icon: 'carbon:menu',
order: 8 order: 8
} }
@@ -456,6 +496,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '权限管理', title: '权限管理',
i18nTitle: 'message.routes.management.auth',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-security' icon: 'ic:baseline-security'
} }
@@ -466,6 +507,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '角色管理', title: '角色管理',
i18nTitle: 'message.routes.management.role',
requiresAuth: true, requiresAuth: true,
icon: 'carbon:user-role' icon: 'carbon:user-role'
} }
@@ -476,6 +518,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '用户管理', title: '用户管理',
i18nTitle: 'message.routes.management.user',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-manage-accounts' icon: 'ic:round-manage-accounts'
} }
@@ -486,6 +529,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '路由管理', title: '路由管理',
i18nTitle: 'message.routes.management.route',
requiresAuth: true, requiresAuth: true,
icon: 'material-symbols:route' icon: 'material-symbols:route'
} }
@@ -493,6 +537,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '系统管理', title: '系统管理',
i18nTitle: 'message.routes.management._value',
icon: 'carbon:cloud-service-management', icon: 'carbon:cloud-service-management',
order: 9 order: 9
} }
@@ -503,7 +548,9 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '关于', title: '关于',
i18nTitle: 'message.routes.about',
requiresAuth: true, requiresAuth: true,
keepAlive: true,
singleLayout: 'basic', singleLayout: 'basic',
icon: 'fluent:book-information-24-regular', icon: 'fluent:book-information-24-regular',
order: 10 order: 10
@@ -523,7 +570,8 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
meta: { meta: {
title: '分析页', title: '分析页',
requiresAuth: true, requiresAuth: true,
icon: 'icon-park-outline:analysis' icon: 'icon-park-outline:analysis',
i18nTitle: 'message.routes.dashboard.analysis'
} }
}, },
{ {
@@ -533,14 +581,16 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
meta: { meta: {
title: '工作台', title: '工作台',
requiresAuth: true, requiresAuth: true,
icon: 'icon-park-outline:workbench' icon: 'icon-park-outline:workbench',
i18nTitle: 'message.routes.dashboard.workbench'
} }
} }
], ],
meta: { meta: {
title: '仪表盘', title: '仪表盘',
icon: 'mdi:monitor-dashboard', icon: 'mdi:monitor-dashboard',
order: 1 order: 1,
i18nTitle: 'message.routes.dashboard._value'
} }
}, },
{ {
@@ -554,6 +604,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'vue文档', title: 'vue文档',
i18nTitle: 'message.routes.document.vue',
requiresAuth: true, requiresAuth: true,
icon: 'logos:vue' icon: 'logos:vue'
} }
@@ -564,6 +615,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'vite文档', title: 'vite文档',
i18nTitle: 'message.routes.document.vite',
requiresAuth: true, requiresAuth: true,
icon: 'logos:vitejs' icon: 'logos:vitejs'
} }
@@ -574,6 +626,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'naive文档', title: 'naive文档',
i18nTitle: 'message.routes.document.naive',
requiresAuth: true, requiresAuth: true,
icon: 'logos:naiveui' icon: 'logos:naiveui'
} }
@@ -584,6 +637,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '项目文档', title: '项目文档',
i18nTitle: 'message.routes.document.project',
requiresAuth: true, requiresAuth: true,
localIcon: 'logo' localIcon: 'logo'
} }
@@ -593,6 +647,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
path: '/document/project-link', path: '/document/project-link',
meta: { meta: {
title: '项目文档(外链)', title: '项目文档(外链)',
i18nTitle: 'message.routes.document.project-link',
requiresAuth: true, requiresAuth: true,
localIcon: 'logo', localIcon: 'logo',
href: 'https://docs.soybean.pro/' href: 'https://docs.soybean.pro/'
@@ -601,6 +656,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '文档', title: '文档',
i18nTitle: 'message.routes.document._value',
icon: 'mdi:file-document-multiple-outline', icon: 'mdi:file-document-multiple-outline',
order: 2 order: 2
} }
@@ -616,6 +672,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '按钮', title: '按钮',
i18nTitle: 'message.routes.component.button',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:button-cursor' icon: 'mdi:button-cursor'
} }
@@ -626,6 +683,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '卡片', title: '卡片',
i18nTitle: 'message.routes.component.card',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:card-outline' icon: 'mdi:card-outline'
} }
@@ -636,6 +694,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '表格', title: '表格',
i18nTitle: 'message.routes.component.table',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:table-large' icon: 'mdi:table-large'
} }
@@ -643,6 +702,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '组件示例', title: '组件示例',
i18nTitle: 'message.routes.component._value',
icon: 'cib:app-store', icon: 'cib:app-store',
order: 3 order: 3
} }
@@ -663,6 +723,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'ECharts', title: 'ECharts',
i18nTitle: 'message.routes.plugin.charts.echarts',
requiresAuth: true, requiresAuth: true,
icon: 'simple-icons:apacheecharts' icon: 'simple-icons:apacheecharts'
} }
@@ -673,6 +734,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'AntV', title: 'AntV',
i18nTitle: 'message.routes.plugin.charts.antv',
requiresAuth: true, requiresAuth: true,
icon: 'simple-icons:antdesign' icon: 'simple-icons:antdesign'
} }
@@ -680,6 +742,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '图表', title: '图表',
i18nTitle: 'message.routes.plugin.charts._value',
icon: 'mdi:chart-areaspline' icon: 'mdi:chart-areaspline'
} }
}, },
@@ -689,6 +752,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '地图', title: '地图',
i18nTitle: 'message.routes.plugin.map',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:map' icon: 'mdi:map'
} }
@@ -699,6 +763,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '视频', title: '视频',
i18nTitle: 'message.routes.plugin.video',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:video' icon: 'mdi:video'
} }
@@ -714,6 +779,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '富文本编辑器', title: '富文本编辑器',
i18nTitle: 'message.routes.plugin.editor.quill',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:file-document-edit-outline' icon: 'mdi:file-document-edit-outline'
} }
@@ -724,6 +790,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'markdown编辑器', title: 'markdown编辑器',
i18nTitle: 'message.routes.plugin.editor.markdown',
requiresAuth: true, requiresAuth: true,
icon: 'ri:markdown-line' icon: 'ri:markdown-line'
} }
@@ -731,6 +798,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '编辑器', title: '编辑器',
i18nTitle: 'message.routes.plugin.editor._value',
icon: 'icon-park-outline:editor' icon: 'icon-park-outline:editor'
} }
}, },
@@ -740,6 +808,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'Swiper插件', title: 'Swiper插件',
i18nTitle: 'message.routes.plugin.swiper',
requiresAuth: true, requiresAuth: true,
icon: 'simple-icons:swiper' icon: 'simple-icons:swiper'
} }
@@ -750,6 +819,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '剪贴板', title: '剪贴板',
i18nTitle: 'message.routes.plugin.copy',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:clipboard-outline' icon: 'mdi:clipboard-outline'
} }
@@ -760,6 +830,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '图标', title: '图标',
i18nTitle: 'message.routes.plugin.icon',
requiresAuth: true, requiresAuth: true,
localIcon: 'custom-icon' localIcon: 'custom-icon'
} }
@@ -770,6 +841,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '打印', title: '打印',
i18nTitle: 'message.routes.plugin.print',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:printer' icon: 'mdi:printer'
} }
@@ -777,6 +849,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '插件示例', title: '插件示例',
i18nTitle: 'message.routes.plugin._value',
icon: 'clarity:plugin-line', icon: 'clarity:plugin-line',
order: 4 order: 4
} }
@@ -792,13 +865,26 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '权限切换', title: '权限切换',
i18nTitle: 'message.routes.auth-demo.permission',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-construction' icon: 'ic:round-construction'
} }
},
{
name: 'auth-demo_super',
path: '/auth-demo/super',
component: 'self',
meta: {
title: '超级管理员可见',
i18nTitle: 'message.routes.auth-demo.super',
requiresAuth: true,
icon: 'ic:round-supervisor-account'
}
} }
], ],
meta: { meta: {
title: '权限示例', title: '权限示例',
i18nTitle: 'message.routes.auth-demo._value',
icon: 'ic:baseline-security', icon: 'ic:baseline-security',
order: 5 order: 5
} }
@@ -814,6 +900,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: 'Tab', title: 'Tab',
i18nTitle: 'message.routes.function.tab',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-tab' icon: 'ic:round-tab'
} }
@@ -846,6 +933,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '功能', title: '功能',
i18nTitle: 'message.routes.function._value',
icon: 'icon-park-outline:all-application', icon: 'icon-park-outline:all-application',
order: 6 order: 6
} }
@@ -861,6 +949,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '异常页403', title: '异常页403',
i18nTitle: 'message.routes.exception.403',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-block' icon: 'ic:baseline-block'
} }
@@ -871,6 +960,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '异常页404', title: '异常页404',
i18nTitle: 'message.routes.exception.404',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-web-asset-off' icon: 'ic:baseline-web-asset-off'
} }
@@ -881,12 +971,14 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '异常页500', title: '异常页500',
i18nTitle: 'message.routes.exception.500',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-wifi-off' icon: 'ic:baseline-wifi-off'
} }
} }
], ],
meta: { meta: {
i18nTitle: 'message.routes.exception._value',
title: '异常页', title: '异常页',
icon: 'ant-design:exception-outlined', icon: 'ant-design:exception-outlined',
order: 7 order: 7
@@ -908,6 +1000,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '二级菜单', title: '二级菜单',
i18nTitle: 'message.routes.multi-menu.first.second',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:menu' icon: 'mdi:menu'
} }
@@ -923,6 +1016,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '三级菜单', title: '三级菜单',
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:menu' icon: 'mdi:menu'
} }
@@ -930,18 +1024,21 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '二级菜单(有子菜单)', title: '二级菜单(有子菜单)',
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
icon: 'mdi:menu' icon: 'mdi:menu'
} }
} }
], ],
meta: { meta: {
title: '一级菜单', title: '一级菜单',
i18nTitle: 'message.routes.multi-menu.first._value',
icon: 'mdi:menu' icon: 'mdi:menu'
} }
} }
], ],
meta: { meta: {
title: '多级菜单', title: '多级菜单',
i18nTitle: 'message.routes.multi-menu._value',
icon: 'carbon:menu', icon: 'carbon:menu',
order: 8 order: 8
} }
@@ -957,6 +1054,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '权限管理', title: '权限管理',
i18nTitle: 'message.routes.management.auth',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-security' icon: 'ic:baseline-security'
} }
@@ -967,6 +1065,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '角色管理', title: '角色管理',
i18nTitle: 'message.routes.management.role',
requiresAuth: true, requiresAuth: true,
icon: 'carbon:user-role' icon: 'carbon:user-role'
} }
@@ -977,6 +1076,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '用户管理', title: '用户管理',
i18nTitle: 'message.routes.management.user',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-manage-accounts' icon: 'ic:round-manage-accounts'
} }
@@ -987,6 +1087,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '路由管理', title: '路由管理',
i18nTitle: 'message.routes.management.route',
requiresAuth: true, requiresAuth: true,
icon: 'material-symbols:route' icon: 'material-symbols:route'
} }
@@ -994,6 +1095,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '系统管理', title: '系统管理',
i18nTitle: 'message.routes.management._value',
icon: 'carbon:cloud-service-management', icon: 'carbon:cloud-service-management',
order: 9 order: 9
} }
@@ -1004,7 +1106,9 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '关于', title: '关于',
i18nTitle: 'message.routes.about',
requiresAuth: true, requiresAuth: true,
keepAlive: true,
singleLayout: 'basic', singleLayout: 'basic',
icon: 'fluent:book-information-24-regular', icon: 'fluent:book-information-24-regular',
order: 10 order: 10
@@ -1024,14 +1128,27 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
meta: { meta: {
title: '分析页', title: '分析页',
requiresAuth: true, requiresAuth: true,
icon: 'icon-park-outline:analysis' icon: 'icon-park-outline:analysis',
i18nTitle: 'message.routes.dashboard.analysis'
}
},
{
name: 'dashboard_workbench',
path: '/dashboard/workbench',
component: 'self',
meta: {
title: '工作台',
requiresAuth: true,
icon: 'icon-park-outline:workbench',
i18nTitle: 'message.routes.dashboard.workbench'
} }
} }
], ],
meta: { meta: {
title: '仪表盘', title: '仪表盘',
icon: 'mdi:monitor-dashboard', icon: 'mdi:monitor-dashboard',
order: 1 order: 1,
i18nTitle: 'message.routes.dashboard._value'
} }
}, },
{ {
@@ -1045,13 +1162,26 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '权限切换', title: '权限切换',
i18nTitle: 'message.routes.auth-demo.permission',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-construction' icon: 'ic:round-construction'
} }
},
{
name: 'auth-demo_super',
path: '/auth-demo/super',
component: 'self',
meta: {
title: '超级管理员可见',
i18nTitle: 'message.routes.auth-demo.super',
requiresAuth: true,
icon: 'ic:round-supervisor-account'
}
} }
], ],
meta: { meta: {
title: '权限示例', title: '权限示例',
i18nTitle: 'message.routes.auth-demo._value',
icon: 'ic:baseline-security', icon: 'ic:baseline-security',
order: 5 order: 5
} }
@@ -1072,6 +1202,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '二级菜单', title: '二级菜单',
i18nTitle: 'message.routes.multi-menu.first.second',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:menu' icon: 'mdi:menu'
} }
@@ -1087,6 +1218,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '三级菜单', title: '三级菜单',
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:menu' icon: 'mdi:menu'
} }
@@ -1094,20 +1226,23 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
], ],
meta: { meta: {
title: '二级菜单(有子菜单)', title: '二级菜单(有子菜单)',
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
icon: 'mdi:menu' icon: 'mdi:menu'
} }
} }
], ],
meta: { meta: {
title: '一级菜单', title: '一级菜单',
i18nTitle: 'message.routes.multi-menu.first._value',
icon: 'mdi:menu' icon: 'mdi:menu'
} }
} }
], ],
meta: { meta: {
title: '多级菜单', title: '多级菜单',
i18nTitle: 'message.routes.multi-menu._value',
icon: 'carbon:menu', icon: 'carbon:menu',
order: 7 order: 8
} }
}, },
{ {
@@ -1116,10 +1251,12 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self', component: 'self',
meta: { meta: {
title: '关于', title: '关于',
i18nTitle: 'message.routes.about',
requiresAuth: true, requiresAuth: true,
keepAlive: true,
singleLayout: 'basic', singleLayout: 'basic',
icon: 'fluent:book-information-24-regular', icon: 'fluent:book-information-24-regular',
order: 8 order: 10
} }
} }
] ]

View File

@@ -1,6 +1,6 @@
{ {
"name": "soybean-admin", "name": "soybean-admin",
"version": "0.9.9", "version": "0.10.2",
"description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。", "description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
"author": { "author": {
"name": "Soybean", "name": "Soybean",
@@ -44,86 +44,79 @@
"build:vercel": "cross-env VITE_HASH_ROUTE=Y VITE_VERCEL=Y vite build", "build:vercel": "cross-env VITE_HASH_ROUTE=Y VITE_VERCEL=Y vite build",
"preview": "vite preview", "preview": "vite preview",
"typecheck": "vue-tsc --noEmit --skipLibCheck", "typecheck": "vue-tsc --noEmit --skipLibCheck",
"lint": "eslint . --fix --ext .js,.jsx,.mjs,.json,.ts,.tsx,.vue", "lint": "eslint . --fix",
"format": "soy prettier-format", "format": "soy prettier-format",
"commit": "soy git-commit", "commit": "soy git-commit",
"cleanup": "soy cleanup", "cleanup": "soy cleanup",
"update-pkg": "soy update-pkg", "update-pkg": "soy update-pkg",
"tsx": "tsx", "tsx": "tsx",
"logo": "tsx ./scripts/logo.ts", "logo": "tsx ./scripts/logo.ts",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", "update-version": "bumpp --commit --push --tag",
"release": "standard-version",
"prepare": "soy init-git-hooks" "prepare": "soy init-git-hooks"
}, },
"dependencies": { "dependencies": {
"@antv/data-set": "^0.11.8", "@antv/data-set": "0.11.8",
"@antv/g2": "^4.2.9", "@antv/g2": "4.2.10",
"@better-scroll/core": "^2.5.0", "@better-scroll/core": "2.5.1",
"@soybeanjs/vue-admin-layout": "^1.1.1", "@soybeanjs/vue-materials": "0.2.0",
"@soybeanjs/vue-admin-tab": "^1.0.5", "@vueuse/core": "10.1.2",
"@soybeanjs/vue-materials": "^0.1.8", "axios": "1.4.0",
"@vueuse/core": "^9.13.0", "clipboard": "2.0.11",
"axios": "0.27.2", "colord": "2.9.3",
"clipboard": "^2.0.11", "crypto-js": "4.1.1",
"colord": "^2.9.3", "dayjs": "1.11.7",
"crypto-js": "^4.1.1", "echarts": "5.4.2",
"dayjs": "^1.11.7", "form-data": "4.0.0",
"echarts": "^5.4.1", "lodash-es": "4.17.21",
"form-data": "^4.0.0", "naive-ui": "2.34.4",
"lodash-es": "^4.17.21", "pinia": "2.1.3",
"naive-ui": "2.34.3", "print-js": "1.6.0",
"pinia": "^2.0.33", "qs": "6.11.2",
"print-js": "^1.6.0", "swiper": "9.3.2",
"qs": "^6.11.1", "ua-parser-js": "1.0.35",
"swiper": "^9.1.0", "vditor": "3.9.3",
"ua-parser-js": "^1.0.34", "vue": "3.3.4",
"vditor": "^3.9.0", "vue-i18n": "9.2.2",
"vue": "3.2.47", "vue-router": "4.2.2",
"vue-i18n": "^9.2.2", "vuedraggable": "4.1.0",
"vue-router": "^4.1.6", "wangeditor": "4.7.15",
"vuedraggable": "^4.1.0", "xgplayer": "3.0.2"
"wangeditor": "^4.7.15",
"xgplayer": "^2.32.2"
}, },
"devDependencies": { "devDependencies": {
"@amap/amap-jsapi-types": "^0.0.13", "@amap/amap-jsapi-types": "0.0.13",
"@iconify/json": "^2.2.33", "@iconify/json": "2.2.72",
"@iconify/vue": "^4.1.0", "@iconify/vue": "4.1.1",
"@soybeanjs/cli": "^0.1.7", "@soybeanjs/cli": "0.4.1",
"@soybeanjs/vite-plugin-vue-page-route": "^0.0.5", "@soybeanjs/vite-plugin-vue-page-route": "0.0.5",
"@types/bmapgl": "^0.0.5", "@types/bmapgl": "0.0.7",
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "4.1.1",
"@types/node": "18.15.0", "@types/node": "20.2.5",
"@types/qs": "^6.9.7", "@types/qs": "6.9.7",
"@types/ua-parser-js": "^0.7.36", "@types/ua-parser-js": "0.7.36",
"@unocss/preset-uno": "^0.50.4", "@unocss/preset-uno": "0.52.7",
"@unocss/transformer-directives": "^0.50.4", "@unocss/transformer-directives": "0.52.7",
"@unocss/vite": "^0.50.4", "@unocss/vite": "0.52.7",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.0", "@vitejs/plugin-vue-jsx": "3.0.1",
"conventional-changelog": "^3.1.25", "bumpp": "9.1.0",
"cross-env": "^7.0.3", "cross-env": "7.0.3",
"eslint": "^8.36.0", "eslint": "8.41.0",
"eslint-config-soybeanjs": "^0.3.1", "eslint-config-soybeanjs": "0.4.8",
"lint-staged": "12.5.0", "mockjs": "1.1.0",
"mockjs": "^1.1.0", "rollup-plugin-visualizer": "5.9.0",
"rollup-plugin-visualizer": "^5.9.0", "sass": "1.62.1",
"sass": "^1.59.2", "simple-git-hooks": "2.8.1",
"simple-git-hooks": "^2.8.1", "tsx": "3.12.7",
"standard-version": "^9.5.0", "typescript": "5.0.4",
"tsx": "^3.12.4", "unplugin-icons": "0.16.1",
"typescript": "4.9.5", "unplugin-vue-components": "0.25.0",
"unplugin-icons": "^0.15.3", "vite": "4.3.9",
"unplugin-vue-components": "0.24.1", "vite-plugin-compression": "0.5.1",
"unplugin-vue-macros": "1.6.4", "vite-plugin-mock": "2.9.8",
"vite": "^4.1.4", "vite-plugin-progress": "0.0.7",
"vite-plugin-compression": "^0.5.1", "vite-plugin-pwa": "0.15.2",
"vite-plugin-html": "^3.2.0", "vite-plugin-svg-icons": "2.0.1",
"vite-plugin-mock": "^2.9.6", "vue-tsc": "1.6.5"
"vite-plugin-progress": "^0.0.6",
"vite-plugin-pwa": "^0.14.4",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^1.2.0"
}, },
"pnpm": { "pnpm": {
"patchedDependencies": { "patchedDependencies": {
@@ -132,9 +125,6 @@
}, },
"simple-git-hooks": { "simple-git-hooks": {
"commit-msg": "pnpm soy git-commit-verify", "commit-msg": "pnpm soy git-commit-verify",
"pre-commit": "pnpm typecheck && pnpm lint-staged" "pre-commit": "pnpm typecheck && pnpm soy lint-staged"
},
"lint-staged": {
"*.{js,jsx,mjs,json,ts,tsx,vue}": "eslint . --fix"
} }
} }

7845
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -54,7 +54,7 @@ const props = withDefaults(defineProps<Props>(), {
placeholderClass: 'bg-white dark:bg-dark transition-background-color duration-300 ease-in-out', placeholderClass: 'bg-white dark:bg-dark transition-background-color duration-300 ease-in-out',
emptyDesc: '暂无数据', emptyDesc: '暂无数据',
iconClass: 'text-320px text-primary', iconClass: 'text-320px text-primary',
descClass: 'text-16px text-[#666]', descClass: 'text-16px text-#666',
showNetworkReload: false showNetworkReload: false
}); });

View File

@@ -1,7 +1,7 @@
<template> <template>
<div <div
class="dark:bg-[#18181c] dark:text-white dark:text-opacity-82 transition-all" class="dark:bg-dark dark:text-white dark:text-opacity-82 transition-all"
:class="inverted ? 'bg-[#001428] text-white' : 'bg-white text-[#333639]'" :class="inverted ? 'bg-#001428 text-white' : 'bg-white text-#333639'"
> >
<slot></slot> <slot></slot>
</div> </div>

View File

@@ -34,9 +34,51 @@ const darkMode = computed({
} }
}); });
function handleSwitch() { function handleSwitch(event: MouseEvent) {
darkMode.value = !darkMode.value; const x = event.clientX;
const y = event.clientY;
const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
// @ts-expect-error: Transition API
if (!document.startViewTransition) {
darkMode.value = !darkMode.value;
return;
}
// @ts-expect-error: Transition API
const transition = document.startViewTransition(() => {
darkMode.value = !darkMode.value;
});
transition.ready.then(() => {
const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`];
document.documentElement.animate(
{
clipPath: darkMode.value ? clipPath : [...clipPath].reverse()
},
{
duration: 300,
easing: 'ease-in',
pseudoElement: darkMode.value ? '::view-transition-new(root)' : '::view-transition-old(root)'
}
);
});
} }
</script> </script>
<style scoped></style> <style>
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-old(root) {
z-index: 9999;
}
::view-transition-new(root) {
z-index: 1;
}
.dark::view-transition-old(root) {
z-index: 1;
}
.dark::view-transition-new(root) {
z-index: 9999;
}
</style>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="flex-col-center wh-full"> <div class="flex-col-center gap-24px min-h-520px wh-full overflow-hidden">
<div class="text-400px text-primary"> <div class="flex text-400px text-primary">
<icon-local-no-permission v-if="type === '403'" /> <icon-local-no-permission v-if="type === '403'" />
<icon-local-not-found v-if="type === '404'" /> <icon-local-not-found v-if="type === '404'" />
<icon-local-service-error v-if="type === '500'" /> <icon-local-service-error v-if="type === '500'" />

View File

@@ -2,14 +2,14 @@
<div v-if="showTooltip"> <div v-if="showTooltip">
<n-tooltip :placement="placement" trigger="hover"> <n-tooltip :placement="placement" trigger="hover">
<template #trigger> <template #trigger>
<div class="flex-center h-full cursor-pointer dark:hover:bg-[#333]" :class="contentClassName"> <div class="flex-center h-full cursor-pointer dark:hover:bg-#333" :class="contentClassName">
<slot></slot> <slot></slot>
</div> </div>
</template> </template>
{{ tooltipContent }} {{ tooltipContent }}
</n-tooltip> </n-tooltip>
</div> </div>
<div v-else class="flex-center cursor-pointer dark:hover:bg-[#333]" :class="contentClassName"> <div v-else class="flex-center cursor-pointer dark:hover:bg-#333" :class="contentClassName">
<slot></slot> <slot></slot>
</div> </div>
</template> </template>
@@ -41,7 +41,7 @@ const props = withDefaults(defineProps<Props>(), {
const showTooltip = computed(() => Boolean(props.tooltipContent)); const showTooltip = computed(() => Boolean(props.tooltipContent));
const contentClassName = computed( const contentClassName = computed(
() => `${props.contentClass} ${props.inverted ? 'hover:bg-primary' : 'hover:bg-[#f6f6f6]'}` () => `${props.contentClass} ${props.inverted ? 'hover:bg-primary' : 'hover:bg-#f6f6f6'}`
); );
</script> </script>

View File

@@ -14,7 +14,7 @@
<span v-for="iconItem in iconsList" :key="iconItem" @click="handleChange(iconItem)"> <span v-for="iconItem in iconsList" :key="iconItem" @click="handleChange(iconItem)">
<svg-icon <svg-icon
:icon="iconItem" :icon="iconItem"
class="border-1px border-[#d9d9d9] text-30px m-2px p-5px cursor-pointer" class="border-1px border-#d9d9d9 text-30px m-2px p-5px cursor-pointer"
:class="{ 'border-primary': modelValue === iconItem }" :class="{ 'border-primary': modelValue === iconItem }"
/> />
</span> </span>

View File

@@ -1,14 +1,34 @@
import { effectScope, onScopeDispose, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useEventListener } from '@vueuse/core'; import { useEventListener } from '@vueuse/core';
import { useI18n } from 'vue-i18n';
import { useTabStore, useThemeStore } from '@/store'; import { useTabStore, useThemeStore } from '@/store';
/** 全局事件 */ /** 全局事件 */
export function useGlobalEvents() { export function useGlobalEvents() {
const theme = useThemeStore(); const theme = useThemeStore();
const tab = useTabStore(); const tab = useTabStore();
const route = useRoute();
const { locale, t } = useI18n();
const scope = effectScope();
/** 页面离开时缓存多页签数据 */ /** 页面离开时缓存多页签数据 */
useEventListener(window, 'beforeunload', () => { useEventListener(window, 'beforeunload', () => {
theme.cacheThemeSettings(); theme.cacheThemeSettings();
tab.cacheTabRoutes(); tab.cacheTabRoutes();
}); });
scope.run(() => {
// 国际化切换时更新浏览器标签文本
watch(
() => locale.value,
() => {
document.title = route.meta.i18nTitle ? t(route.meta.i18nTitle) : route.meta.title;
}
);
});
onScopeDispose(() => {
scope.stop();
});
} }

View File

@@ -1,4 +1,4 @@
import { computed } from 'vue'; import { computed, watch } from 'vue';
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'; import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
import { useAppStore, useThemeStore } from '@/store'; import { useAppStore, useThemeStore } from '@/store';
@@ -63,6 +63,16 @@ export function useBasicLayout() {
return w; return w;
}); });
watch(
isMobile,
newValue => {
if (newValue) {
app.setSiderCollapse(true);
}
},
{ immediate: true }
);
return { return {
mode, mode,
isMobile, isMobile,

View File

@@ -1,10 +1,42 @@
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import type { DataTableBaseColumn, DataTableSelectionColumn, DataTableExpandColumn, PaginationProps } from 'naive-ui'; import type { DataTableBaseColumn, DataTableSelectionColumn, DataTableExpandColumn, PaginationProps } from 'naive-ui';
import type { MaybeComputedRef } from '@vueuse/core';
import type { TableColumnGroup, InternalRowData } from 'naive-ui/es/data-table/src/interface'; import type { TableColumnGroup, InternalRowData } from 'naive-ui/es/data-table/src/interface';
import { useLoadingEmpty } from '../common'; import { useLoadingEmpty } from '../common';
/**
* 表格分页参数
*/
type PaginationParams = Pick<PaginationProps, 'page' | 'pageSize'>;
/**
* 表格请求接口的参数
*/
type ApiParams = Record<string, unknown> & PaginationParams;
/**
* 表格请求接口的结果
* @description 这里用属性list来表示后端接口返回的表格数据
*/
type ApiData<TableData = Record<string, unknown>> = Record<string, unknown> & { list: TableData[] };
/**
* 表格接口的请求函数
*/
type ApiFn<Params = ApiParams, TableData = Record<string, unknown>> = (
params: Params
) => Promise<Service.RequestResult<ApiData<TableData>>>;
/**
* 表格接口请求后转换后的数据
*/
type TransformedTableData<TableData = Record<string, unknown>> = {
data: TableData[];
pageNum: number;
pageSize: number;
total: number;
};
/** /**
* 表格的列 * 表格的列
*/ */
@@ -15,60 +47,45 @@ type DataTableColumn<T = InternalRowData> =
| DataTableExpandColumn<T>; | DataTableExpandColumn<T>;
/** /**
* 表格分页参 * 表格数据转换器
* @description 将不同接口的表格数据转换成统一的类型
*/ */
type TablePaginationParams = Pick<PaginationProps, 'page' | 'pageSize'>; type Transformer<TableData = Record<string, unknown>> = (
apiData: ApiData<TableData>
) => TransformedTableData<TableData>;
/** type TableParams<TableData = Record<string, unknown>, Params = ApiParams> = {
* 表格接口的请求参数 apiFn: ApiFn<Params, TableData>;
*/ apiParams: Params;
type TableApiParams = Record<string, unknown> & TablePaginationParams; transformer: Transformer<TableData>;
columns: DataTableColumn<TableData>[];
/** pagination?: PaginationProps;
* 表格接口的请求数据
*/
type TableApiData<T = InternalRowData> = {
data: T[];
pageNum: number;
pageSize: number;
total: number;
}; };
/** export function useTable<TableData extends Record<string, unknown>, Params extends ApiParams>(
* 表格接口的请求函数 params: TableParams<TableData, Params>,
*/ immediate = true
type TableApiFn<P extends TableApiParams, T extends InternalRowData> = (
params: P
) => Promise<Service.SuccessResult<TableApiData<T>>>;
export function useNaiveTable<TableData extends InternalRowData, P extends TableApiParams>(
apiFn: TableApiFn<P, TableData>,
apiParams: P,
columns: MaybeComputedRef<DataTableColumn<TableData>[]>
) { ) {
const { loading, startLoading, endLoading, empty, setEmpty } = useLoadingEmpty(); const { loading, startLoading, endLoading, empty, setEmpty } = useLoadingEmpty();
const data: Ref<TableData[]> = ref([]);
const tableData: Ref<TableData[]> = ref([]); function updateData(update: TableData[]) {
data.value = update;
async function getTableData(paginationParams?: TablePaginationParams) {
startLoading();
const params = { ...apiParams, ...paginationParams };
const { data } = await apiFn(params);
if (data) {
tableData.value = data.data;
setEmpty(data.data.length === 0);
}
endLoading();
} }
const pagination: PaginationProps = reactive({ let dataSource: TableData[] = [];
page: 1, function setDataSource(source: TableData[]) {
pageSize: 10, dataSource = source;
showSizePicker: true, }
pageSizes: [10, 15, 20, 25, 30],
function resetData() {
data.value = dataSource;
}
const columns = ref(params.columns) as Ref<DataTableColumn<TableData>[]>;
const pagination = reactive({
...getPagination(params.pagination),
onChange: (page: number) => { onChange: (page: number) => {
pagination.page = page; pagination.page = page;
}, },
@@ -76,14 +93,59 @@ export function useNaiveTable<TableData extends InternalRowData, P extends Table
pagination.pageSize = pageSize; pagination.pageSize = pageSize;
pagination.page = 1; pagination.page = 1;
} }
}); }) as PaginationProps;
function updatePagination(update: Partial<PaginationProps>) {
Object.assign(pagination, update);
}
async function getData() {
const apiParams: Params = { ...params.apiParams };
apiParams.page = apiParams.page || pagination.page;
apiParams.pageSize = apiParams.pageSize || pagination.pageSize;
startLoading();
const { data: apiData } = await params.apiFn(apiParams);
if (apiData) {
const transformedData = params.transformer(apiData);
updateData(transformedData.data);
setDataSource(transformedData.data);
setEmpty(transformedData.data.length === 0);
updatePagination({ page: transformedData.pageNum, pageSize: transformedData.pageSize });
}
endLoading();
}
if (immediate) {
getData();
}
return { return {
tableData, data,
columns, columns,
loading, loading,
empty, empty,
pagination, pagination,
start: getTableData getData,
updateData,
resetData
}; };
} }
function getPagination(pagination?: Partial<PaginationProps>) {
const defaultPagination: Partial<PaginationProps> = {
page: 1,
pageSize: 10,
showSizePicker: true,
pageSizes: [10, 15, 20, 25, 30]
};
Object.assign(defaultPagination, pagination);
return defaultPagination;
}

View File

@@ -1,6 +1,7 @@
<template> <template>
<admin-layout <admin-layout
:mode="mode" :mode="mode"
:is-mobile="isMobile"
:scroll-mode="theme.scrollMode" :scroll-mode="theme.scrollMode"
:scroll-el-id="app.scrollElId" :scroll-el-id="app.scrollElId"
:full-content="app.contentFull" :full-content="app.contentFull"
@@ -15,6 +16,8 @@
:sider-collapsed-width="siderCollapsedWidth" :sider-collapsed-width="siderCollapsedWidth"
:footer-visible="theme.footer.visible" :footer-visible="theme.footer.visible"
:fixed-footer="theme.footer.fixed" :fixed-footer="theme.footer.fixed"
:right-footer="theme.footer.right"
@click-mobile-sider-mask="app.setSiderCollapse(true)"
> >
<template #header> <template #header>
<global-header v-bind="headerProps" /> <global-header v-bind="headerProps" />
@@ -30,7 +33,7 @@
<global-footer /> <global-footer />
</template> </template>
</admin-layout> </admin-layout>
<global-back-top /> <n-back-top :key="theme.scrollMode" :listen-to="`#${app.scrollElId}`" class="z-100" />
<setting-drawer /> <setting-drawer />
</template> </template>
@@ -38,22 +41,22 @@
import { AdminLayout } from '@soybeanjs/vue-materials'; import { AdminLayout } from '@soybeanjs/vue-materials';
import { useAppStore, useThemeStore } from '@/store'; import { useAppStore, useThemeStore } from '@/store';
import { useBasicLayout } from '@/composables'; import { useBasicLayout } from '@/composables';
import { import { GlobalContent, GlobalFooter, GlobalHeader, GlobalSider, GlobalTab, SettingDrawer } from '../common';
GlobalBackTop,
GlobalContent,
GlobalFooter,
GlobalHeader,
GlobalSider,
GlobalTab,
SettingDrawer
} from '../common';
defineOptions({ name: 'BasicLayout' }); defineOptions({ name: 'BasicLayout' });
const app = useAppStore(); const app = useAppStore();
const theme = useThemeStore(); const theme = useThemeStore();
const { mode, headerProps, siderVisible, siderWidth, siderCollapsedWidth } = useBasicLayout(); const { mode, isMobile, headerProps, siderVisible, siderWidth, siderCollapsedWidth } = useBasicLayout();
</script> </script>
<style scoped></style> <style lang="scss">
#__SCROLL_EL_ID__ {
@include scrollbar(8px, #e1e1e1);
}
.dark #__SCROLL_EL_ID__ {
@include scrollbar(8px, #555);
}
</style>

View File

@@ -1,15 +0,0 @@
<template>
<n-back-top :show="show" class="z-1000" />
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { useWindowScroll } from '@vueuse/core';
defineOptions({ name: 'GlobalBackTop' });
const { y: scrollY } = useWindowScroll();
const show = computed(() => scrollY.value > 180);
</script>
<style scoped></style>

View File

@@ -13,7 +13,7 @@
v-if="app.reloadFlag" v-if="app.reloadFlag"
:key="route.fullPath" :key="route.fullPath"
:class="{ 'p-16px': showPadding }" :class="{ 'p-16px': showPadding }"
class="flex-grow bg-[#f6f9f8] dark:bg-[#101014] transition duration-300 ease-in-out" class="flex-grow bg-#f6f9f8 dark:bg-#101014 transition duration-300 ease-in-out"
/> />
</keep-alive> </keep-alive>
</transition> </transition>

View File

@@ -1,11 +1,15 @@
<template> <template>
<dark-mode-container class="flex-center h-full"> <dark-mode-container class="flex-center h-full" :inverted="theme.footer.inverted">
<span>Copyright ©2021 Soybean Admin</span> <span>Copyright ©2021 Soybean Admin</span>
</dark-mode-container> </dark-mode-container>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useThemeStore } from '@/store';
defineOptions({ name: 'GlobalFooter' }); defineOptions({ name: 'GlobalFooter' });
const theme = useThemeStore();
</script> </script>
<style scoped></style> <style scoped></style>

View File

@@ -2,7 +2,7 @@
<n-breadcrumb class="px-12px"> <n-breadcrumb class="px-12px">
<template v-for="breadcrumb in breadcrumbs" :key="breadcrumb.key"> <template v-for="breadcrumb in breadcrumbs" :key="breadcrumb.key">
<n-breadcrumb-item> <n-breadcrumb-item>
<n-dropdown v-if="breadcrumb.hasChildren" :options="breadcrumb.children" @select="dropdownSelect"> <n-dropdown v-if="breadcrumb.hasChildren" :options="breadcrumb.options" @select="dropdownSelect">
<span> <span>
<component <component
:is="breadcrumb.icon" :is="breadcrumb.icon"
@@ -19,7 +19,9 @@
class="inline-block align-text-bottom mr-4px text-16px" class="inline-block align-text-bottom mr-4px text-16px"
:class="{ 'text-#BBBBBB': theme.header.inverted }" :class="{ 'text-#BBBBBB': theme.header.inverted }"
/> />
<span :class="{ 'text-#BBBBBB': theme.header.inverted }">{{ breadcrumb.label }}</span> <span :class="{ 'text-#BBBBBB': theme.header.inverted }">
{{ breadcrumb.label }}
</span>
</template> </template>
</n-breadcrumb-item> </n-breadcrumb-item>
</template> </template>
@@ -33,6 +35,7 @@ import { routePath } from '@/router';
import { useRouteStore, useThemeStore } from '@/store'; import { useRouteStore, useThemeStore } from '@/store';
import { useRouterPush } from '@/composables'; import { useRouterPush } from '@/composables';
import { getBreadcrumbByRouteKey } from '@/utils'; import { getBreadcrumbByRouteKey } from '@/utils';
import { t } from '@/locales';
defineOptions({ name: 'GlobalBreadcrumb' }); defineOptions({ name: 'GlobalBreadcrumb' });
@@ -42,7 +45,13 @@ const routeStore = useRouteStore();
const { routerPush } = useRouterPush(); const { routerPush } = useRouterPush();
const breadcrumbs = computed(() => const breadcrumbs = computed(() =>
getBreadcrumbByRouteKey(route.name as string, routeStore.menus as App.GlobalMenuOption[], routePath('root')) getBreadcrumbByRouteKey(route.name as string, routeStore.menus as App.GlobalMenuOption[], routePath('root')).map(
item => ({
...item,
label: item.i18nTitle ? t(item.i18nTitle) : item.label,
options: item.options?.map(oItem => ({ ...oItem, label: oItem.i18nTitle ? t(oItem.i18nTitle) : oItem.label }))
})
)
); );
function dropdownSelect(key: string) { function dropdownSelect(key: string) {

View File

@@ -20,6 +20,7 @@ import { useRoute } from 'vue-router';
import type { MenuOption } from 'naive-ui'; import type { MenuOption } from 'naive-ui';
import { useRouteStore, useThemeStore } from '@/store'; import { useRouteStore, useThemeStore } from '@/store';
import { useRouterPush } from '@/composables'; import { useRouterPush } from '@/composables';
import { translateMenuLabel } from '@/utils';
defineOptions({ name: 'HeaderMenu' }); defineOptions({ name: 'HeaderMenu' });
@@ -28,7 +29,7 @@ const routeStore = useRouteStore();
const theme = useThemeStore(); const theme = useThemeStore();
const { routerPush } = useRouterPush(); const { routerPush } = useRouterPush();
const menus = computed(() => routeStore.menus as App.GlobalMenuOption[]); const menus = computed(() => translateMenuLabel(routeStore.menus as App.GlobalMenuOption[]));
const activeKey = computed(() => (route.meta?.activeMenu ? route.meta.activeMenu : route.name) as string); const activeKey = computed(() => (route.meta?.activeMenu ? route.meta.activeMenu : route.name) as string);
function handleUpdateMenu(_key: string, item: MenuOption) { function handleUpdateMenu(_key: string, item: MenuOption) {

View File

@@ -7,6 +7,7 @@ import ThemeMode from './theme-mode.vue';
import UserAvatar from './user-avatar.vue'; import UserAvatar from './user-avatar.vue';
import SystemMessage from './system-message.vue'; import SystemMessage from './system-message.vue';
import SettingButton from './setting-button.vue'; import SettingButton from './setting-button.vue';
import ToggleLang from './toggle-lang.vue';
export { export {
MenuCollapse, MenuCollapse,
@@ -17,5 +18,6 @@ export {
ThemeMode, ThemeMode,
UserAvatar, UserAvatar,
SystemMessage, SystemMessage,
SettingButton SettingButton,
ToggleLang
}; };

View File

@@ -4,7 +4,7 @@
<n-list-item <n-list-item
v-for="(item, index) in list" v-for="(item, index) in list"
:key="item.id" :key="item.id"
class="hover:bg-[#f6f6f6] dark:hover:bg-dark cursor-pointer" class="hover:bg-#f6f6f6 dark:hover:bg-dark cursor-pointer"
@click="handleRead(index)" @click="handleRead(index)"
> >
<n-thing class="px-15px" :class="{ 'opacity-30': item.isRead }"> <n-thing class="px-15px" :class="{ 'opacity-30': item.isRead }">

View File

@@ -0,0 +1,37 @@
<template>
<hover-container class="w-40px h-full">
<n-dropdown :options="options" trigger="hover" :value="language" @select="handleSelect">
<icon-cil:language class="text-18px outline-transparent" />
</n-dropdown>
</hover-container>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { localStg } from '@/utils';
const { locale } = useI18n();
const language = ref<I18nType.langType>(localStg.get('lang') || 'zh-CN');
const options = [
{
label: '中文',
key: 'zh-CN'
},
{
label: 'English',
key: 'en'
},
{
label: 'ភាសាខ្មែរ',
key: 'km-KH'
}
];
const handleSelect = (key: string) => {
language.value = key as I18nType.langType;
locale.value = key;
localStg.set('lang', key as I18nType.langType);
};
</script>
<style scoped></style>

View File

@@ -11,6 +11,7 @@
<github-site /> <github-site />
<full-screen /> <full-screen />
<theme-mode /> <theme-mode />
<toggle-lang />
<system-message /> <system-message />
<setting-button v-if="showButton" /> <setting-button v-if="showButton" />
<user-avatar /> <user-avatar />
@@ -32,7 +33,8 @@ import {
SettingButton, SettingButton,
SystemMessage, SystemMessage,
ThemeMode, ThemeMode,
UserAvatar UserAvatar,
ToggleLang
} from './components'; } from './components';
defineOptions({ name: 'GlobalHeader' }); defineOptions({ name: 'GlobalHeader' });

View File

@@ -1,11 +1,7 @@
<template> <template>
<router-link :to="routeHomePath" class="flex-center w-full nowrap-hidden"> <router-link :to="routeHomePath" class="flex-center w-full nowrap-hidden">
<system-logo class="text-32px text-primary" /> <system-logo class="text-32px text-primary" />
<h2 <h2 v-show="showTitle" class="pl-8px text-16px font-bold text-primary transition duration-300 ease-in-out">
v-show="showTitle"
class="pl-8px text-16px font-bold text-primary transition duration-300 ease-in-out"
@click="toggleLocal"
>
{{ t('message.system.title') }} {{ t('message.system.title') }}
</h2> </h2>
</router-link> </router-link>
@@ -13,7 +9,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { routePath } from '@/router'; import { routePath } from '@/router';
import { t, setLocale } from '@/locales'; import { t } from '@/locales';
defineOptions({ name: 'GlobalLogo' }); defineOptions({ name: 'GlobalLogo' });
@@ -25,12 +21,6 @@ interface Props {
defineProps<Props>(); defineProps<Props>();
const routeHomePath = routePath('root'); const routeHomePath = routePath('root');
let flag = true;
function toggleLocal() {
flag = !flag;
setLocale(flag ? 'en' : 'zh-CN');
}
</script> </script>
<style scoped></style> <style scoped></style>

View File

@@ -12,7 +12,7 @@
<n-input-group> <n-input-group>
<n-input ref="inputRef" v-model:value="keyword" clearable placeholder="请输入关键词搜索" @input="handleSearch"> <n-input ref="inputRef" v-model:value="keyword" clearable placeholder="请输入关键词搜索" @input="handleSearch">
<template #prefix> <template #prefix>
<icon-uil-search class="text-15px text-[#c2c2c2]" /> <icon-uil-search class="text-15px text-#c2c2c2" />
</template> </template>
</n-input> </n-input>
<n-button v-if="isMobile" type="primary" ghost @click="handleClose">取消</n-button> <n-button v-if="isMobile" type="primary" ghost @click="handleClose">取消</n-button>

View File

@@ -3,7 +3,7 @@
<div class="pb-12px"> <div class="pb-12px">
<template v-for="item in options" :key="item.path"> <template v-for="item in options" :key="item.path">
<div <div
class="bg-[#e5e7eb] dark:bg-dark h-56px mt-8px px-14px rounded-4px cursor-pointer flex-y-center justify-between" class="bg-#e5e7eb dark:bg-dark h-56px mt-8px px-14px rounded-4px cursor-pointer flex-y-center justify-between"
:style="{ :style="{
background: item.path === active ? theme.themeColor : '', background: item.path === active ? theme.themeColor : '',
color: item.path === active ? '#fff' : '' color: item.path === active ? '#fff' : ''

View File

@@ -6,7 +6,7 @@
> >
<component :is="icon" :class="[isMini ? 'text-16px' : 'text-20px']" /> <component :is="icon" :class="[isMini ? 'text-16px' : 'text-20px']" />
<p <p
class="text-12px overflow-hidden transition-height duration-300 ease-in-out" class="w-full text-center ellipsis-text text-12px transition-height duration-300 ease-in-out"
:class="[isMini ? 'h-0 pt-0' : 'h-24px pt-4px']" :class="[isMini ? 'h-0 pt-0' : 'h-24px pt-4px']"
> >
{{ label }} {{ label }}
@@ -17,7 +17,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
import type { VNodeChild } from 'vue'; import type { Component } from 'vue';
import { useBoolean } from '@/hooks'; import { useBoolean } from '@/hooks';
defineOptions({ name: 'MixMenuDetail' }); defineOptions({ name: 'MixMenuDetail' });
@@ -30,7 +30,7 @@ interface Props {
/** 当前激活状态的理由名称 */ /** 当前激活状态的理由名称 */
activeRouteName: string; activeRouteName: string;
/** 路由图标 */ /** 路由图标 */
icon?: () => VNodeChild; icon?: Component;
/** mini尺寸的路由 */ /** mini尺寸的路由 */
isMini?: boolean; isMini?: boolean;
} }

View File

@@ -5,6 +5,7 @@
> >
<dark-mode-container <dark-mode-container
class="drawer-shadow absolute-lt flex-col-stretch h-full nowrap-hidden" class="drawer-shadow absolute-lt flex-col-stretch h-full nowrap-hidden"
:inverted="theme.sider.inverted"
:style="{ width: showDrawer ? theme.sider.mixChildMenuWidth + 'px' : '0px' }" :style="{ width: showDrawer ? theme.sider.mixChildMenuWidth + 'px' : '0px' }"
> >
<header class="header-height flex-y-center justify-between" :style="{ height: theme.header.height + 'px' }"> <header class="header-height flex-y-center justify-between" :style="{ height: theme.header.height + 'px' }">
@@ -20,6 +21,7 @@
:options="menus" :options="menus"
:expanded-keys="expandedKeys" :expanded-keys="expandedKeys"
:indent="18" :indent="18"
:inverted="!theme.darkMode && theme.sider.inverted"
@update:value="handleUpdateMenu" @update:value="handleUpdateMenu"
@update:expanded-keys="handleUpdateExpandedKeys" @update:expanded-keys="handleUpdateExpandedKeys"
/> />

View File

@@ -1,6 +1,6 @@
<template> <template>
<dark-mode-container class="flex h-full" :inverted="theme.sider.inverted" @mouseleave="resetFirstDegreeMenus"> <dark-mode-container class="flex h-full" :inverted="theme.sider.inverted" @mouseleave="resetFirstDegreeMenus">
<div class="flex-1 flex-col-stretch h-full"> <div class="flex-1-hidden flex-col-stretch h-full">
<global-logo :show-title="false" :style="{ height: theme.header.height + 'px' }" /> <global-logo :show-title="false" :style="{ height: theme.header.height + 'px' }" />
<n-scrollbar class="flex-1-hidden"> <n-scrollbar class="flex-1-hidden">
<mix-menu-detail <mix-menu-detail
@@ -26,7 +26,9 @@ import { useRoute } from 'vue-router';
import { useAppStore, useRouteStore, useThemeStore } from '@/store'; import { useAppStore, useRouteStore, useThemeStore } from '@/store';
import { useRouterPush } from '@/composables'; import { useRouterPush } from '@/composables';
import { useBoolean } from '@/hooks'; import { useBoolean } from '@/hooks';
import { translateMenuLabel } from '@/utils';
import { GlobalLogo } from '@/layouts/common'; import { GlobalLogo } from '@/layouts/common';
import { t } from '@/locales';
import { MixMenuCollapse, MixMenuDetail, MixMenuDrawer } from './components'; import { MixMenuCollapse, MixMenuDetail, MixMenuDrawer } from './components';
defineOptions({ name: 'VerticalMixSider' }); defineOptions({ name: 'VerticalMixSider' });
@@ -45,13 +47,13 @@ function setActiveParentRouteName(routeName: string) {
const firstDegreeMenus = computed(() => const firstDegreeMenus = computed(() =>
routeStore.menus.map(item => { routeStore.menus.map(item => {
const { routeName, label } = item; const { routeName, label, i18nTitle } = item;
const icon = item?.icon; const icon = item?.icon;
const hasChildren = Boolean(item.children && item.children.length); const hasChildren = Boolean(item.children && item.children.length);
return { return {
routeName, routeName,
label, label: i18nTitle ? t(i18nTitle) : label,
icon, icon,
hasChildren hasChildren
}; };
@@ -88,7 +90,7 @@ const activeChildMenus = computed(() => {
routeStore.menus.some(item => { routeStore.menus.some(item => {
const flag = item.routeName === activeParentRouteName.value && Boolean(item.children?.length); const flag = item.routeName === activeParentRouteName.value && Boolean(item.children?.length);
if (flag) { if (flag) {
menus.push(...(item.children || [])); menus.push(...translateMenuLabel((item.children || []) as App.GlobalMenuOption[]));
} }
return flag; return flag;
}); });

View File

@@ -8,7 +8,7 @@
:options="menus" :options="menus"
:expanded-keys="expandedKeys" :expanded-keys="expandedKeys"
:indent="18" :indent="18"
:inverted="theme.sider.inverted" :inverted="!theme.darkMode && theme.sider.inverted"
@update:value="handleUpdateMenu" @update:value="handleUpdateMenu"
@update:expanded-keys="handleUpdateExpandedKeys" @update:expanded-keys="handleUpdateExpandedKeys"
/> />
@@ -21,7 +21,7 @@ import { useRoute } from 'vue-router';
import type { MenuOption } from 'naive-ui'; import type { MenuOption } from 'naive-ui';
import { useAppStore, useRouteStore, useThemeStore } from '@/store'; import { useAppStore, useRouteStore, useThemeStore } from '@/store';
import { useRouterPush } from '@/composables'; import { useRouterPush } from '@/composables';
import { getActiveKeyPathsOfMenus } from '@/utils'; import { getActiveKeyPathsOfMenus, translateMenuLabel } from '@/utils';
defineOptions({ name: 'VerticalMenu' }); defineOptions({ name: 'VerticalMenu' });
@@ -31,7 +31,7 @@ const theme = useThemeStore();
const routeStore = useRouteStore(); const routeStore = useRouteStore();
const { routerPush } = useRouterPush(); const { routerPush } = useRouterPush();
const menus = computed(() => routeStore.menus as App.GlobalMenuOption[]); const menus = computed(() => translateMenuLabel(routeStore.menus as App.GlobalMenuOption[]));
const activeKey = computed(() => (route.meta?.activeMenu ? route.meta.activeMenu : route.name) as string); const activeKey = computed(() => (route.meta?.activeMenu ? route.meta.activeMenu : route.name) as string);
const expandedKeys = ref<string[]>([]); const expandedKeys = ref<string[]>([]);

View File

@@ -1,6 +1,6 @@
<template> <template>
<div ref="tabRef" class="flex h-full pr-18px" :class="[isChromeMode ? 'items-end' : 'items-center gap-12px']"> <div ref="tabRef" class="flex h-full pr-18px" :class="[isChromeMode ? 'items-end' : 'items-center gap-12px']">
<AdminTab <PageTab
v-for="item in tab.tabs" v-for="item in tab.tabs"
:key="item.fullPath" :key="item.fullPath"
:mode="theme.tab.mode" :mode="theme.tab.mode"
@@ -19,8 +19,8 @@
class="inline-block align-text-bottom text-16px" class="inline-block align-text-bottom text-16px"
/> />
</template> </template>
{{ item.meta.title }} {{ item.meta.i18nTitle ? t(item.meta.i18nTitle) : item.meta.title }}
</AdminTab> </PageTab>
</div> </div>
<context-menu <context-menu
:visible="dropdown.visible" :visible="dropdown.visible"
@@ -34,8 +34,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, reactive, ref, watch } from 'vue'; import { computed, nextTick, reactive, ref, watch } from 'vue';
import { AdminTab } from '@soybeanjs/vue-materials'; import { PageTab } from '@soybeanjs/vue-materials';
import { useTabStore, useThemeStore } from '@/store'; import { useTabStore, useThemeStore } from '@/store';
import { t } from '@/locales';
import { ContextMenu } from './components'; import { ContextMenu } from './components';
defineOptions({ name: 'TabDetail' }); defineOptions({ name: 'TabDetail' });

View File

@@ -5,6 +5,5 @@ import GlobalSider from './global-sider/index.vue';
import GlobalContent from './global-content/index.vue'; import GlobalContent from './global-content/index.vue';
import GlobalFooter from './global-footer/index.vue'; import GlobalFooter from './global-footer/index.vue';
import GlobalLogo from './global-logo/index.vue'; import GlobalLogo from './global-logo/index.vue';
import GlobalBackTop from './global-back-top/index.vue';
export { SettingDrawer, GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter, GlobalLogo, GlobalBackTop }; export { SettingDrawer, GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter, GlobalLogo };

View File

@@ -21,12 +21,15 @@
</template> </template>
</n-switch> </n-switch>
</setting-menu> </setting-menu>
<setting-menu label="侧边栏深色主题"> <setting-menu label="侧边栏深色">
<n-switch :value="theme.sider.inverted" @update:value="theme.setSiderInverted" /> <n-switch :value="theme.sider.inverted" @update:value="theme.setSiderInverted" />
</setting-menu> </setting-menu>
<setting-menu label="头部深色主题"> <setting-menu label="头部深色">
<n-switch :value="theme.header.inverted" @update:value="theme.setHeaderInverted" /> <n-switch :value="theme.header.inverted" @update:value="theme.setHeaderInverted" />
</setting-menu> </setting-menu>
<setting-menu label="底部深色">
<n-switch :value="theme.footer.inverted" @update:value="theme.setFooterInverted" />
</setting-menu>
</n-space> </n-space>
</template> </template>

View File

@@ -6,8 +6,8 @@
<n-tooltip :placement="activeConfig.placement" trigger="hover"> <n-tooltip :placement="activeConfig.placement" trigger="hover">
<template #trigger> <template #trigger>
<div class="layout-checkbox__shadow relative w-56px h-48px bg-white rounded-4px overflow-hidden"> <div class="layout-checkbox__shadow relative w-56px h-48px bg-white rounded-4px overflow-hidden">
<div class="absolute-lt bg-[#273352]" :class="activeConfig.menuClass"></div> <div class="absolute-lt bg-#273352" :class="activeConfig.menuClass"></div>
<div class="absolute-rb bg-[#f0f2f5]" :class="activeConfig.mainClass"></div> <div class="absolute-rb bg-#f0f2f5" :class="activeConfig.mainClass"></div>
</div> </div>
</template> </template>
<span>{{ label }}</span> <span>{{ label }}</span>

View File

@@ -61,11 +61,14 @@
@update:value="theme.setMixSiderWidth" @update:value="theme.setMixSiderWidth"
/> />
</setting-menu> </setting-menu>
<setting-menu label="显示底部">
<n-switch :value="theme.footer.visible" @update:value="theme.setFooterVisible" />
</setting-menu>
<setting-menu label="固定底部"> <setting-menu label="固定底部">
<n-switch :value="theme.footer.fixed" @update:value="theme.setFooterIsFixed" /> <n-switch :value="theme.footer.fixed" @update:value="theme.setFooterIsFixed" />
</setting-menu> </setting-menu>
<setting-menu label="显示底部"> <setting-menu label="底部居右">
<n-switch :value="theme.footer.visible" @update:value="theme.setFooterVisible" /> <n-switch :value="theme.footer.right" @update:value="theme.setFooterIsRight" />
</setting-menu> </setting-menu>
</n-space> </n-space>
</template> </template>

View File

@@ -1,12 +1,14 @@
import type { App } from 'vue'; import type { App } from 'vue';
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
import { localStg } from '@/utils';
import messages from './lang'; import messages from './lang';
import type { LocaleKey } from './lang'; import type { LocaleKey } from './lang';
const i18n = createI18n({ const i18n = createI18n({
locale: 'zh-CN', locale: localStg.get('lang') || 'zh-CN',
fallbackLocale: 'en', fallbackLocale: 'en',
messages messages,
legacy: false
}); });
export function setupI18n(app: App) { export function setupI18n(app: App) {
@@ -18,5 +20,5 @@ export function t(key: string) {
} }
export function setLocale(locale: LocaleKey) { export function setLocale(locale: LocaleKey) {
i18n.global.locale = locale; i18n.global.locale.value = locale;
} }

View File

@@ -7,13 +7,77 @@ const locale: LocaleMessages<I18nType.Schema> = {
}, },
routes: { routes: {
dashboard: { dashboard: {
dashboard: 'Dashboard', _value: 'Dashboard',
analysis: 'Analysis', analysis: 'Analysis',
workbench: 'Workbench' workbench: 'Workbench'
}, },
about: { document: {
about: 'About' _value: 'Document',
} vue: 'Vue Document',
vite: 'Vite Document',
naive: 'NaiveUI Document',
project: 'Project Document',
'project-link': 'Project Document(href)'
},
component: {
_value: 'Component',
button: 'Button',
card: 'Card',
table: 'Table'
},
plugin: {
_value: 'Plugin',
charts: {
_value: 'Chart',
echarts: 'ECharts',
antv: 'AntV'
},
copy: 'Copy',
editor: {
_value: 'Editor',
quill: 'Quill',
markdown: 'Markdown'
},
icon: 'Icon',
map: 'Map',
print: 'Print',
swiper: 'Swiper',
video: 'Video'
},
'auth-demo': {
_value: 'Auth Demo',
permission: 'Toggle Permission',
super: 'Super Auth'
},
function: {
_value: 'Function',
tab: 'System Tab'
},
exception: {
_value: 'Exception',
403: '403',
404: '404',
500: '500'
},
'multi-menu': {
_value: 'Multi Degree Menu',
first: {
_value: 'First Degree',
second: 'Second Degree',
'second-new': {
_value: 'Second Degree With Children',
third: 'Third Degree'
}
}
},
management: {
_value: 'System Management',
auth: 'Auth',
role: 'Role',
route: 'Route',
user: 'User'
},
about: 'About'
} }
} }
}; };

View File

@@ -1,9 +1,11 @@
import zhCN from './zh-cn'; import zhCN from './zh-cn';
import en from './en'; import en from './en';
import kmKH from './km-KH';
const locales = { const locales = {
'zh-CN': zhCN, 'zh-CN': zhCN,
en en,
'km-KH': kmKH
}; };
export type LocaleKey = keyof typeof locales; export type LocaleKey = keyof typeof locales;

85
src/locales/lang/km-KH.ts Normal file
View File

@@ -0,0 +1,85 @@
import type { LocaleMessages } from 'vue-i18n';
const locale: LocaleMessages<I18nType.Schema> = {
message: {
system: {
title: 'ប្រព័ន្ធគ្រប់គ្រង'
},
routes: {
dashboard: {
_value: 'ផ្ទាំងទិន្នន័យ',
analysis: 'ផ្ទាំងវិភាគ',
workbench: 'ផ្ទាំងការងារ'
},
document: {
_value: 'ឯកសារ',
vue: 'ឯកសារ​ Vue',
vite: 'ឯកសារ​ Vite',
naive: 'ឯកសារ NaiveUI',
project: 'ឯកសារគម្រោង',
'project-link': 'ឯកសារគម្រោង(href)'
},
component: {
_value: 'សមាស​ភាគ',
button: 'ប៊ូតុង',
card: 'កាត',
table: 'តារាង'
},
plugin: {
_value: 'មុខងារជំនួយ',
charts: {
_value: 'តារាង​ Chart',
echarts: 'តារាង ECharts',
antv: 'AntV'
},
copy: 'ចម្លង',
editor: {
_value: 'កែប្រែ',
quill: 'Quill',
markdown: 'Markdown'
},
icon: 'អាយខន',
map: 'ផែនទី',
print: 'បោះពុម្ភ',
swiper: 'Swiper',
video: 'វីដេអូ'
},
'auth-demo': {
_value: 'ឌីមូ Auth',
permission: 'បិទ/បើកការអនុញ្ញាត',
super: 'Super Auth'
},
function: {
_value: 'មុខងារ',
tab: 'ថេបប្រព័ន្ធ'
},
exception: {
_value: 'ករណីពិេសស',
403: '403',
404: '404',
500: '500'
},
'multi-menu': {
_value: 'ម៉ឺនុយពហុដឺក្រេ',
first: {
_value: 'ដឺក្រេទី១',
second: 'ដែក្រេទី២',
'second-new': {
_value: 'ដឺក្រេទី២មានអនុក្រោម',
third: 'ដឺក្រេទី៣'
}
}
},
management: {
_value: 'ការគ្រប់គ្រងប្រព័ន្ធ',
auth: 'Auth',
role: 'សិទ្ធី',
route: 'ផ្លូវប្រព័ន្ធ',
user: 'អ្នកប្រើប្រាស់'
},
about: 'អំពីប្រព័ន្ធ'
}
}
};
export default locale;

View File

@@ -7,13 +7,77 @@ const locale: LocaleMessages<I18nType.Schema> = {
}, },
routes: { routes: {
dashboard: { dashboard: {
dashboard: '仪表盘', _value: '仪表盘',
analysis: '分析页', analysis: '分析页',
workbench: '工作台' workbench: '工作台'
}, },
about: { document: {
about: '关于' _value: '文档',
} vue: 'Vue文档',
vite: 'Vite文档',
naive: 'NaiveUI文档',
project: '项目文档',
'project-link': '项目文档(外链)'
},
component: {
_value: '组件示例',
button: '按钮',
card: '卡片',
table: '表格'
},
plugin: {
_value: '插件示例',
charts: {
_value: '图表',
echarts: 'ECharts',
antv: 'AntV'
},
copy: '剪贴板',
editor: {
_value: '编辑器',
quill: '富文本',
markdown: 'Markdown'
},
icon: '图标',
map: '地图',
print: '打印',
swiper: 'Swiper',
video: '视频'
},
'auth-demo': {
_value: '权限示例',
permission: '切换权限',
super: '超级管理员可见'
},
function: {
_value: '功能',
tab: 'Tab页签'
},
exception: {
_value: '异常页',
403: '403',
404: '404',
500: '500'
},
'multi-menu': {
_value: '多级菜单',
first: {
_value: '一级菜单',
second: '二级菜单',
'second-new': {
_value: '二级菜单(有子菜单)',
third: '三级菜单'
}
}
},
management: {
_value: '系统管理',
auth: '权限管理',
role: '角色管理',
route: '路由管理',
user: '用户管理'
},
about: '关于'
} }
} }
}; };

View File

@@ -29,6 +29,8 @@ async function setupApp() {
setupI18n(app); setupI18n(app);
appLoading.unmount();
// mount app // mount app
app.mount('#app'); app.mount('#app');
} }

View File

@@ -1,5 +1,6 @@
import type { Router } from 'vue-router'; import type { Router } from 'vue-router';
import { useTitle } from '@vueuse/core'; import { useTitle } from '@vueuse/core';
import { t } from '@/locales';
import { createPermissionGuard } from './permission'; import { createPermissionGuard } from './permission';
/** /**
@@ -15,7 +16,7 @@ export function createRouterGuard(router: Router) {
}); });
router.afterEach(to => { router.afterEach(to => {
// 设置document title // 设置document title
useTitle(to.meta.title); useTitle(to.meta.i18nTitle ? t(to.meta.i18nTitle) : to.meta.title);
// 结束 loadingBar // 结束 loadingBar
window.$loadingBar?.finish(); window.$loadingBar?.finish();
}); });

View File

@@ -4,6 +4,7 @@ const about1: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '关于', title: '关于',
i18nTitle: 'message.routes.about',
requiresAuth: true, requiresAuth: true,
keepAlive: true, keepAlive: true,
singleLayout: 'basic', singleLayout: 'basic',

View File

@@ -9,6 +9,7 @@ const authDemo: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '权限切换', title: '权限切换',
i18nTitle: 'message.routes.auth-demo.permission',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-construction' icon: 'ic:round-construction'
} }
@@ -19,6 +20,7 @@ const authDemo: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '超级管理员可见', title: '超级管理员可见',
i18nTitle: 'message.routes.auth-demo.super',
requiresAuth: true, requiresAuth: true,
permissions: ['super'], permissions: ['super'],
icon: 'ic:round-supervisor-account' icon: 'ic:round-supervisor-account'
@@ -27,6 +29,7 @@ const authDemo: AuthRoute.Route = {
], ],
meta: { meta: {
title: '权限示例', title: '权限示例',
i18nTitle: 'message.routes.auth-demo._value',
icon: 'ic:baseline-security', icon: 'ic:baseline-security',
order: 5 order: 5
} }

View File

@@ -9,6 +9,7 @@ const component: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '按钮', title: '按钮',
i18nTitle: 'message.routes.component.button',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:button-cursor' icon: 'mdi:button-cursor'
} }
@@ -19,6 +20,7 @@ const component: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '卡片', title: '卡片',
i18nTitle: 'message.routes.component.card',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:card-outline' icon: 'mdi:card-outline'
} }
@@ -29,6 +31,7 @@ const component: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '表格', title: '表格',
i18nTitle: 'message.routes.component.table',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:table-large' icon: 'mdi:table-large'
} }
@@ -36,6 +39,7 @@ const component: AuthRoute.Route = {
], ],
meta: { meta: {
title: '组件示例', title: '组件示例',
i18nTitle: 'message.routes.component._value',
icon: 'cib:app-store', icon: 'cib:app-store',
order: 3 order: 3
} }

View File

@@ -10,7 +10,8 @@ const dashboard: AuthRoute.Route = {
meta: { meta: {
title: '分析页', title: '分析页',
requiresAuth: true, requiresAuth: true,
icon: 'icon-park-outline:analysis' icon: 'icon-park-outline:analysis',
i18nTitle: 'message.routes.dashboard.analysis'
} }
}, },
{ {
@@ -20,14 +21,16 @@ const dashboard: AuthRoute.Route = {
meta: { meta: {
title: '工作台', title: '工作台',
requiresAuth: true, requiresAuth: true,
icon: 'icon-park-outline:workbench' icon: 'icon-park-outline:workbench',
i18nTitle: 'message.routes.dashboard.workbench'
} }
} }
], ],
meta: { meta: {
title: '仪表盘', title: '仪表盘',
icon: 'mdi:monitor-dashboard', icon: 'mdi:monitor-dashboard',
order: 1 order: 1,
i18nTitle: 'message.routes.dashboard._value'
} }
}; };

View File

@@ -9,6 +9,7 @@ const document: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: 'vue文档', title: 'vue文档',
i18nTitle: 'message.routes.document.vue',
requiresAuth: true, requiresAuth: true,
icon: 'logos:vue' icon: 'logos:vue'
} }
@@ -19,6 +20,7 @@ const document: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: 'vite文档', title: 'vite文档',
i18nTitle: 'message.routes.document.vite',
requiresAuth: true, requiresAuth: true,
icon: 'logos:vitejs' icon: 'logos:vitejs'
} }
@@ -29,6 +31,7 @@ const document: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: 'naive文档', title: 'naive文档',
i18nTitle: 'message.routes.document.naive',
requiresAuth: true, requiresAuth: true,
icon: 'logos:naiveui' icon: 'logos:naiveui'
} }
@@ -39,6 +42,7 @@ const document: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '项目文档', title: '项目文档',
i18nTitle: 'message.routes.document.project',
requiresAuth: true, requiresAuth: true,
localIcon: 'logo' localIcon: 'logo'
} }
@@ -48,6 +52,7 @@ const document: AuthRoute.Route = {
path: '/document/project-link', path: '/document/project-link',
meta: { meta: {
title: '项目文档(外链)', title: '项目文档(外链)',
i18nTitle: 'message.routes.document.project-link',
requiresAuth: true, requiresAuth: true,
localIcon: 'logo', localIcon: 'logo',
href: 'https://docs.soybean.pro/' href: 'https://docs.soybean.pro/'
@@ -56,6 +61,7 @@ const document: AuthRoute.Route = {
], ],
meta: { meta: {
title: '文档', title: '文档',
i18nTitle: 'message.routes.document._value',
icon: 'mdi:file-document-multiple-outline', icon: 'mdi:file-document-multiple-outline',
order: 2 order: 2
} }

View File

@@ -9,6 +9,7 @@ const exception: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '异常页403', title: '异常页403',
i18nTitle: 'message.routes.exception.403',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-block' icon: 'ic:baseline-block'
} }
@@ -19,6 +20,7 @@ const exception: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '异常页404', title: '异常页404',
i18nTitle: 'message.routes.exception.404',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-web-asset-off' icon: 'ic:baseline-web-asset-off'
} }
@@ -29,12 +31,14 @@ const exception: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '异常页500', title: '异常页500',
i18nTitle: 'message.routes.exception.500',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-wifi-off' icon: 'ic:baseline-wifi-off'
} }
} }
], ],
meta: { meta: {
i18nTitle: 'message.routes.exception._value',
title: '异常页', title: '异常页',
icon: 'ant-design:exception-outlined', icon: 'ant-design:exception-outlined',
order: 7 order: 7

View File

@@ -9,6 +9,7 @@ const functionRoute: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: 'Tab', title: 'Tab',
i18nTitle: 'message.routes.function.tab',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-tab' icon: 'ic:round-tab'
} }
@@ -41,6 +42,7 @@ const functionRoute: AuthRoute.Route = {
], ],
meta: { meta: {
title: '功能', title: '功能',
i18nTitle: 'message.routes.function._value',
icon: 'icon-park-outline:all-application', icon: 'icon-park-outline:all-application',
order: 6 order: 6
} }

View File

@@ -9,6 +9,7 @@ const management: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '权限管理', title: '权限管理',
i18nTitle: 'message.routes.management.auth',
requiresAuth: true, requiresAuth: true,
icon: 'ic:baseline-security' icon: 'ic:baseline-security'
} }
@@ -19,6 +20,7 @@ const management: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '角色管理', title: '角色管理',
i18nTitle: 'message.routes.management.role',
requiresAuth: true, requiresAuth: true,
icon: 'carbon:user-role' icon: 'carbon:user-role'
} }
@@ -29,6 +31,7 @@ const management: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '用户管理', title: '用户管理',
i18nTitle: 'message.routes.management.user',
requiresAuth: true, requiresAuth: true,
icon: 'ic:round-manage-accounts' icon: 'ic:round-manage-accounts'
} }
@@ -39,6 +42,7 @@ const management: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '路由管理', title: '路由管理',
i18nTitle: 'message.routes.management.route',
requiresAuth: true, requiresAuth: true,
icon: 'material-symbols:route' icon: 'material-symbols:route'
} }
@@ -46,6 +50,7 @@ const management: AuthRoute.Route = {
], ],
meta: { meta: {
title: '系统管理', title: '系统管理',
i18nTitle: 'message.routes.management._value',
icon: 'carbon:cloud-service-management', icon: 'carbon:cloud-service-management',
order: 9 order: 9
} }

View File

@@ -14,6 +14,7 @@ const multiMenu: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '二级菜单', title: '二级菜单',
i18nTitle: 'message.routes.multi-menu.first.second',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:menu' icon: 'mdi:menu'
} }
@@ -29,6 +30,7 @@ const multiMenu: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '三级菜单', title: '三级菜单',
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:menu' icon: 'mdi:menu'
} }
@@ -36,18 +38,21 @@ const multiMenu: AuthRoute.Route = {
], ],
meta: { meta: {
title: '二级菜单(有子菜单)', title: '二级菜单(有子菜单)',
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
icon: 'mdi:menu' icon: 'mdi:menu'
} }
} }
], ],
meta: { meta: {
title: '一级菜单', title: '一级菜单',
i18nTitle: 'message.routes.multi-menu.first._value',
icon: 'mdi:menu' icon: 'mdi:menu'
} }
} }
], ],
meta: { meta: {
title: '多级菜单', title: '多级菜单',
i18nTitle: 'message.routes.multi-menu._value',
icon: 'carbon:menu', icon: 'carbon:menu',
order: 8 order: 8
} }

View File

@@ -14,6 +14,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: 'ECharts', title: 'ECharts',
i18nTitle: 'message.routes.plugin.charts.echarts',
requiresAuth: true, requiresAuth: true,
icon: 'simple-icons:apacheecharts' icon: 'simple-icons:apacheecharts'
} }
@@ -24,6 +25,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: 'AntV', title: 'AntV',
i18nTitle: 'message.routes.plugin.charts.antv',
requiresAuth: true, requiresAuth: true,
icon: 'simple-icons:antdesign' icon: 'simple-icons:antdesign'
} }
@@ -31,6 +33,7 @@ const plugin: AuthRoute.Route = {
], ],
meta: { meta: {
title: '图表', title: '图表',
i18nTitle: 'message.routes.plugin.charts._value',
icon: 'mdi:chart-areaspline' icon: 'mdi:chart-areaspline'
} }
}, },
@@ -40,6 +43,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '地图', title: '地图',
i18nTitle: 'message.routes.plugin.map',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:map' icon: 'mdi:map'
} }
@@ -50,6 +54,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '视频', title: '视频',
i18nTitle: 'message.routes.plugin.video',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:video' icon: 'mdi:video'
} }
@@ -65,6 +70,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '富文本编辑器', title: '富文本编辑器',
i18nTitle: 'message.routes.plugin.editor.quill',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:file-document-edit-outline' icon: 'mdi:file-document-edit-outline'
} }
@@ -75,6 +81,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: 'markdown编辑器', title: 'markdown编辑器',
i18nTitle: 'message.routes.plugin.editor.markdown',
requiresAuth: true, requiresAuth: true,
icon: 'ri:markdown-line' icon: 'ri:markdown-line'
} }
@@ -82,6 +89,7 @@ const plugin: AuthRoute.Route = {
], ],
meta: { meta: {
title: '编辑器', title: '编辑器',
i18nTitle: 'message.routes.plugin.editor._value',
icon: 'icon-park-outline:editor' icon: 'icon-park-outline:editor'
} }
}, },
@@ -91,6 +99,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: 'Swiper插件', title: 'Swiper插件',
i18nTitle: 'message.routes.plugin.swiper',
requiresAuth: true, requiresAuth: true,
icon: 'simple-icons:swiper' icon: 'simple-icons:swiper'
} }
@@ -101,6 +110,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '剪贴板', title: '剪贴板',
i18nTitle: 'message.routes.plugin.copy',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:clipboard-outline' icon: 'mdi:clipboard-outline'
} }
@@ -111,6 +121,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '图标', title: '图标',
i18nTitle: 'message.routes.plugin.icon',
requiresAuth: true, requiresAuth: true,
localIcon: 'custom-icon' localIcon: 'custom-icon'
} }
@@ -121,6 +132,7 @@ const plugin: AuthRoute.Route = {
component: 'self', component: 'self',
meta: { meta: {
title: '打印', title: '打印',
i18nTitle: 'message.routes.plugin.print',
requiresAuth: true, requiresAuth: true,
icon: 'mdi:printer' icon: 'mdi:printer'
} }
@@ -128,6 +140,7 @@ const plugin: AuthRoute.Route = {
], ],
meta: { meta: {
title: '插件示例', title: '插件示例',
i18nTitle: 'message.routes.plugin._value',
icon: 'clarity:plugin-line', icon: 'clarity:plugin-line',
order: 4 order: 4
} }

View File

@@ -1,5 +1,5 @@
import axios from 'axios'; import axios from 'axios';
import type { AxiosError, AxiosInstance, AxiosRequestConfig } from 'axios'; import type { AxiosResponse, AxiosError, AxiosInstance, AxiosRequestConfig } from 'axios';
import { REFRESH_TOKEN_CODE } from '@/config'; import { REFRESH_TOKEN_CODE } from '@/config';
import { import {
localStg, localStg,
@@ -59,7 +59,7 @@ export default class CustomAxiosInstance {
} }
); );
this.instance.interceptors.response.use( this.instance.interceptors.response.use(
async response => { (async response => {
const { status } = response; const { status } = response;
if (status === 200 || status < 300 || status === 304) { if (status === 200 || status < 300 || status === 304) {
const backend = response.data; const backend = response.data;
@@ -82,7 +82,7 @@ export default class CustomAxiosInstance {
} }
const error = handleResponseError(response); const error = handleResponseError(response);
return handleServiceResult(error, null); return handleServiceResult(error, null);
}, }) as (response: AxiosResponse<any, any>) => Promise<AxiosResponse<any, any>>,
(axiosError: AxiosError) => { (axiosError: AxiosError) => {
const error = handleAxiosError(axiosError); const error = handleAxiosError(axiosError);
return handleServiceResult(error, null); return handleServiceResult(error, null);

View File

@@ -120,9 +120,11 @@
] ]
}, },
"footer": { "footer": {
"visible": true,
"fixed": false, "fixed": false,
"right": true,
"height": 48, "height": 48,
"visible": true "inverted": false
}, },
"page": { "page": {
"animate": true, "animate": true,

View File

@@ -83,9 +83,11 @@ const defaultThemeSetting: Theme.Setting = {
horizontalPositionList: themeHorizontalMenuPositionOptions horizontalPositionList: themeHorizontalMenuPositionOptions
}, },
footer: { footer: {
visible: true,
fixed: false, fixed: false,
right: true,
height: 48, height: 48,
visible: true inverted: false
}, },
page: { page: {
animate: true, animate: true,

View File

@@ -1,6 +1,6 @@
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { SCROLL_EL_ID } from '@soybeanjs/vue-materials'; import { LAYOUT_SCROLL_EL_ID } from '@soybeanjs/vue-materials';
interface AppState { interface AppState {
/** 滚动元素的id */ /** 滚动元素的id */
@@ -21,7 +21,7 @@ interface AppState {
export const useAppStore = defineStore('app-store', { export const useAppStore = defineStore('app-store', {
state: (): AppState => ({ state: (): AppState => ({
scrollElId: SCROLL_EL_ID, scrollElId: LAYOUT_SCROLL_EL_ID,
contentFull: false, contentFull: false,
disableMainXScroll: false, disableMainXScroll: false,
reloadFlag: true, reloadFlag: true,

View File

@@ -107,6 +107,7 @@ export const useRouteStore = defineStore('route-store', {
}, },
/** 初始化动态路由 */ /** 初始化动态路由 */
async initDynamicRoute() { async initDynamicRoute() {
const { resetAuthStore } = useAuthStore();
const { initHomeTab } = useTabStore(); const { initHomeTab } = useTabStore();
const { userId } = localStg.get('userInfo') || {}; const { userId } = localStg.get('userInfo') || {};
@@ -125,6 +126,8 @@ export const useRouteStore = defineStore('route-store', {
initHomeTab(data.home, router); initHomeTab(data.home, router);
this.isInitAuthRoute = true; this.isInitAuthRoute = true;
} else {
resetAuthStore();
} }
}, },
/** 初始化静态路由 */ /** 初始化静态路由 */

View File

@@ -7,7 +7,6 @@ import { localStg } from '@/utils';
*/ */
export function getTabRouteByVueRoute(route: RouteRecordNormalized | RouteLocationNormalizedLoaded) { export function getTabRouteByVueRoute(route: RouteRecordNormalized | RouteLocationNormalizedLoaded) {
const fullPath = hasFullPath(route) ? route.fullPath : route.path; const fullPath = hasFullPath(route) ? route.fullPath : route.path;
const tabRoute: App.GlobalTabRoute = { const tabRoute: App.GlobalTabRoute = {
name: route.name, name: route.name,
fullPath, fullPath,

View File

@@ -145,17 +145,25 @@ export const useThemeStore = defineStore('theme-store', {
setHorizontalMenuPosition(position: UnionKey.ThemeHorizontalMenuPosition) { setHorizontalMenuPosition(position: UnionKey.ThemeHorizontalMenuPosition) {
this.menu.horizontalPosition = position; this.menu.horizontalPosition = position;
}, },
/** 设置底部是否显示 */
setFooterVisible(isVisible: boolean) {
this.footer.visible = isVisible;
},
/** 设置底部是否固定 */ /** 设置底部是否固定 */
setFooterIsFixed(isFixed: boolean) { setFooterIsFixed(isFixed: boolean) {
this.footer.fixed = isFixed; this.footer.fixed = isFixed;
}, },
/** 设置底部是否固定 */
setFooterIsRight(right: boolean) {
this.footer.right = right;
},
/** 设置底部高度 */ /** 设置底部高度 */
setFooterHeight(height: number) { setFooterHeight(height: number) {
this.footer.height = height; this.footer.height = height;
}, },
/** 设置底部是否显示 */ /** 设置底部高度 */
setFooterVisible(isVisible: boolean) { setFooterInverted(inverted: boolean) {
this.footer.visible = isVisible; this.footer.inverted = inverted;
}, },
/** 设置切换页面时是否过渡动画 */ /** 设置切换页面时是否过渡动画 */
setPageIsAnimate(animate: boolean) { setPageIsAnimate(animate: boolean) {

View File

@@ -1,6 +1,5 @@
@import "./transition.css"; @import "./transition.css";
@import "./reset.css"; @import "./reset.css";
@import "./scrollbar.css";
html, html,
body, body,

View File

@@ -1,52 +0,0 @@
html {
scrollbar-width: thin;
scrollbar-color: #e1e1e1 transparent;
}
/*---滚动条默认显示样式--*/
html::-webkit-scrollbar-thumb {
background-color: #e1e1e1;
border-radius: 8px;
}
/*---鼠标点击滚动条显示样式--*/
html::-webkit-scrollbar-thumb:hover {
background-color: #e1e1e1;
border-radius: 8px;
}
/*---滚动条大小--*/
html::-webkit-scrollbar {
width: 8px;
height: 8px;
}
/*---滚动框背景样式--*/
html::-webkit-scrollbar-track-piece {
background-color: rgba(0, 0, 0, 0);
border-radius: 0;
}
html.dark {
scrollbar-width: thin;
scrollbar-color: #555 transparent;
}
/*---滚动条默认显示样式--*/
html.dark::-webkit-scrollbar-thumb {
background-color: #555;
border-radius: 8px;
}
/*---鼠标点击滚动条显示样式--*/
html.dark::-webkit-scrollbar-thumb:hover {
background-color: #555;
border-radius: 8px;
}
/*---滚动条大小--*/
html.dark::-webkit-scrollbar {
width: 8px;
height: 8px;
}
/*---滚动框背景样式--*/
html.dark::-webkit-scrollbar-track-piece {
background-color: rgba(0, 0, 0, 0);
border-radius: 0;
}

View File

@@ -1,4 +1,7 @@
@mixin scrollbar($size: 8px, $color: #d9d9d9) { @mixin scrollbar($size: 8px, $color: #d9d9d9) {
scrollbar-width: thin;
scrollbar-color: $color transparent;
&::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
background-color: $color; background-color: $color;
border-radius: $size; border-radius: $size;

View File

@@ -59,8 +59,15 @@ interface ImportMetaEnv {
readonly VITE_COMPRESS_TYPE?: 'gzip' | 'brotliCompress' | 'deflate' | 'deflateRaw'; readonly VITE_COMPRESS_TYPE?: 'gzip' | 'brotliCompress' | 'deflate' | 'deflateRaw';
/** 是否应用pwa */ /** 是否应用pwa */
readonly VITE_PWA?: 'Y' | 'N'; readonly VITE_PWA?: 'Y' | 'N';
/**
* 是否开启生产模式下的mock
* @description 生产模式下会拦截XHR导致无法获取response不使用mock请求时设置为N
*/
readonly VITE_PROD_MOCK?: 'Y' | 'N';
/** hash路由模式 */ /** hash路由模式 */
readonly VITE_HASH_ROUTE?: 'Y' | 'N'; readonly VITE_HASH_ROUTE?: 'Y' | 'N';
/** 是否应用自动生成路由的插件 */
readonly VITE_SOYBEAN_ROUTE_PLUGIN?: 'Y' | 'N';
/** 是否是部署的vercel */ /** 是否是部署的vercel */
readonly VITE_VERCEL?: 'Y' | 'N'; readonly VITE_VERCEL?: 'Y' | 'N';
} }

View File

@@ -7,3 +7,9 @@ declare namespace BMap {
} }
declare const TMap: any; declare const TMap: any;
declare module 'unplugin-vue-define-options/vite' {
const plugin: (options?: import('unplugin-vue-define-options/dist/unplugin.d-59ddef99').B) => import('vite').Plugin;
export default plugin;
}

View File

@@ -31,6 +31,8 @@ declare namespace AuthRoute {
interface RouteMeta<K extends AuthRoute.RoutePath> { interface RouteMeta<K extends AuthRoute.RoutePath> {
/** 路由标题(可用来作document.title或者菜单的名称) */ /** 路由标题(可用来作document.title或者菜单的名称) */
title: string; title: string;
/** 用来支持多国语言 如果i18nTitle和title同时存在优先使用i18nTitle */
i18nTitle?: string;
/** 路由的动态路径(需要动态路径的页面需要将path添加进范型参数) */ /** 路由的动态路径(需要动态路径的页面需要将path添加进范型参数) */
dynamicPath?: AuthRouteUtils.GetDynamicPath<K>; dynamicPath?: AuthRouteUtils.GetDynamicPath<K>;
/** 作为单级路由的父级路由布局组件 */ /** 作为单级路由的父级路由布局组件 */

View File

@@ -18,5 +18,7 @@ declare namespace StorageInterface {
themeSettings: Theme.Setting; themeSettings: Theme.Setting;
/** 多页签路由信息 */ /** 多页签路由信息 */
multiTabRoutes: App.GlobalTabRoute[]; multiTabRoutes: App.GlobalTabRoute[];
/** 本地语言缓存 */
lang: I18nType.langType;
} }
} }

View File

@@ -200,12 +200,16 @@ declare namespace Theme {
/** 底部样式 */ /** 底部样式 */
interface Footer { interface Footer {
/** 是否固定底部 */
fixed: boolean;
/** 底部高度 */
height: number;
/* 底部是否可见 */ /* 底部是否可见 */
visible: boolean; visible: boolean;
/** 是否固定底部 */
fixed: boolean;
/** 底部是否居右(顶部混合菜单模式有效) */
right: boolean;
/** 底部高度 */
height: number;
/** 底部反转色 */
inverted: boolean;
} }
/** 页面样式 */ /** 页面样式 */
@@ -238,16 +242,19 @@ declare namespace App {
routePath: string; routePath: string;
icon?: () => import('vue').VNodeChild; icon?: () => import('vue').VNodeChild;
children?: GlobalMenuOption[]; children?: GlobalMenuOption[];
i18nTitle?: string;
}; };
/** 面包屑 */ /** 面包屑 */
type GlobalBreadcrumb = import('naive-ui').DropdownOption & { type GlobalBreadcrumb = Omit<import('naive-ui').DropdownOption, 'icon'> & {
key: string; key: string;
label: string; label: string;
disabled: boolean; disabled: boolean;
routeName: string; routeName: string;
hasChildren: boolean; hasChildren: boolean;
children?: GlobalBreadcrumb[]; icon?: import('vue').Component;
i18nTitle?: string;
options?: (import('naive-ui/es/dropdown/src/interface').DropdownMixedOption & { i18nTitle?: string })[];
}; };
/** 多页签Tab的路由 */ /** 多页签Tab的路由 */
@@ -295,19 +302,85 @@ declare namespace App {
} }
declare namespace I18nType { declare namespace I18nType {
type langType = 'en' | 'zh-CN' | 'km-KH';
interface Schema { interface Schema {
system: { system: {
title: string; title: string;
}; };
routes: { routes: {
dashboard: { dashboard: {
dashboard: string; _value: string;
analysis: string; analysis: string;
workbench: string; workbench: string;
}; };
about: { document: {
about: string; _value: string;
vue: string;
vite: string;
naive: string;
project: string;
'project-link': string;
}; };
component: {
_value: string;
button: string;
card: string;
table: string;
};
plugin: {
_value: string;
charts: {
_value: string;
antv: string;
echarts: string;
};
copy: string;
editor: {
_value: string;
markdown: string;
quill: string;
};
icon: string;
map: string;
print: string;
swiper: string;
video: string;
};
'auth-demo': {
_value: string;
permission: string;
super: string;
};
function: {
_value: string;
tab: string;
};
exception: {
_value: string;
403: string;
404: string;
500: string;
};
'multi-menu': {
_value: string;
first: {
_value: string;
second: string;
'second-new': {
_value: string;
third: string;
};
};
};
management: {
_value: string;
auth: string;
role: string;
route: string;
user: string;
};
about: string;
}; };
} }
} }

View File

@@ -59,15 +59,16 @@ function transformBreadcrumbMenuToBreadcrumb(menu: App.GlobalMenuOption, rootPat
label: menu.label as string, label: menu.label as string,
routeName: menu.routeName, routeName: menu.routeName,
disabled: menu.routePath === rootPath, disabled: menu.routePath === rootPath,
hasChildren hasChildren,
i18nTitle: menu.i18nTitle
}; };
if (menu.icon) { if (menu.icon) {
breadcrumb.icon = menu.icon; breadcrumb.icon = menu.icon;
} }
if (hasChildren) { if (hasChildren) {
breadcrumb.children = menu.children?.map(item => breadcrumb.options = menu.children?.map(item =>
transformBreadcrumbMenuToBreadcrumb(item as App.GlobalMenuOption, rootPath) transformBreadcrumbMenuToBreadcrumb(item as App.GlobalMenuOption, rootPath)
); ) as NonNullable<App.GlobalBreadcrumb['options']>;
} }
return breadcrumb; return breadcrumb;
} }

View File

@@ -1,4 +1,5 @@
import { useIconRender } from '@/composables'; import { useIconRender } from '@/composables';
import { t } from '@/locales';
/** /**
* 将权限路由转换成菜单 * 将权限路由转换成菜单
@@ -18,7 +19,8 @@ export function transformAuthRouteToMenu(routes: AuthRoute.Route[]): App.GlobalM
key: routeName, key: routeName,
label: meta.title, label: meta.title,
routeName, routeName,
routePath: path routePath: path,
i18nTitle: meta.i18nTitle
}, },
icon: meta.icon, icon: meta.icon,
localIcon: meta.localIcon, localIcon: meta.localIcon,
@@ -33,6 +35,28 @@ export function transformAuthRouteToMenu(routes: AuthRoute.Route[]): App.GlobalM
return globalMenu; return globalMenu;
} }
/**
* 翻译菜单
* @param menus
* @returns
*/
export function translateMenuLabel(menus: App.GlobalMenuOption[]): App.GlobalMenuOption[] {
const globalMenu: App.GlobalMenuOption[] = [];
menus.forEach(menu => {
let menuChildren: App.GlobalMenuOption[] | undefined;
if (menu.children && menu.children.length > 0) {
menuChildren = translateMenuLabel(menu.children);
}
const menuItem: App.GlobalMenuOption = {
...menu,
children: menuChildren,
label: menu.i18nTitle ? t(menu.i18nTitle) : menu.label
};
globalMenu.push(menuItem);
});
return globalMenu;
}
/** /**
* 获取当前路由所在菜单数据的paths * 获取当前路由所在菜单数据的paths
* @param activeKey - 当前路由的key * @param activeKey - 当前路由的key

View File

@@ -1,6 +1,6 @@
<template> <template>
<n-space :vertical="true"> <n-space :vertical="true">
<n-divider class="!mb-0 text-14px text-[#666]">其他账户登录</n-divider> <n-divider class="!mb-0 text-14px text-#666">其他账户登录</n-divider>
<n-space justify="center"> <n-space justify="center">
<n-button <n-button
v-for="item in accounts" v-for="item in accounts"

View File

@@ -1,9 +1,9 @@
<template> <template>
<n-space :vertical="true"> <n-space :vertical="true">
<n-divider class="!mb-0 text-14px text-[#666]">其他登录方式</n-divider> <n-divider class="!mb-0 text-14px text-#666">其他登录方式</n-divider>
<div class="flex-center"> <div class="flex-center">
<n-button :text="true"> <n-button :text="true">
<icon-mdi-wechat class="text-22px text-[#888] hover:text-[#52BF5E]" /> <icon-mdi-wechat class="text-22px text-#888 hover:text-#52BF5E" />
</n-button> </n-button>
</div> </div>
</n-space> </n-space>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="h-full"> <div class="h-full">
<n-card title="当前页面只有super才能看到" class="h-full shadow-sm rounded-16px"> </n-card> <n-card title="当前页面只有super才能看到" class="h-full shadow-sm rounded-16px"></n-card>
</div> </div>
</template> </template>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="h-full overflow-hidden">
<n-card title="表格" class="h-full shadow-sm rounded-16px"> <n-card title="表格" class="h-full shadow-sm rounded-16px">
<n-space :vertical="true"> <n-space :vertical="true">
<n-space> <n-space>

View File

@@ -1,14 +1,14 @@
<template> <template>
<n-grid :x-gap="16" :y-gap="16" :item-responsive="true"> <n-grid :x-gap="16" :y-gap="16" :item-responsive="true">
<n-grid-item span="0:24 640:24 1024:8"> <n-grid-item span="0:24 640:24 1024:8">
<n-card title="时间线" :bordered="false" class="rounded-16px shadow-sm"> <n-card title="时间线" :bordered="false" class="h-full rounded-16px shadow-sm">
<n-timeline> <n-timeline>
<n-timeline-item v-for="item in timelines" :key="item.type" v-bind="item" /> <n-timeline-item v-for="item in timelines" :key="item.type" v-bind="item" />
</n-timeline> </n-timeline>
</n-card> </n-card>
</n-grid-item> </n-grid-item>
<n-grid-item span="0:24 640:24 1024:16"> <n-grid-item span="0:24 640:24 1024:16">
<n-card title="表格" :bordered="false" class="rounded-16px shadow-sm"> <n-card title="表格" :bordered="false" class="h-full rounded-16px shadow-sm">
<n-data-table size="small" :columns="columns" :data="tableData" /> <n-data-table size="small" :columns="columns" :data="tableData" />
</n-card> </n-card>
</n-grid-item> </n-grid-item>

View File

@@ -4,15 +4,15 @@
<n-card :bordered="false" class="rounded-16px shadow-sm"> <n-card :bordered="false" class="rounded-16px shadow-sm">
<div class="w-full h-360px py-12px"> <div class="w-full h-360px py-12px">
<h3 class="text-16px font-bold">Dashboard</h3> <h3 class="text-16px font-bold">Dashboard</h3>
<p class="text-[#aaa]">Overview Of Lasted Month</p> <p class="text-#aaa">Overview Of Lasted Month</p>
<h3 class="pt-32px text-24px font-bold"> <h3 class="pt-32px text-24px font-bold">
<count-to prefix="$" :start-value="0" :end-value="7754" /> <count-to prefix="$" :start-value="0" :end-value="7754" />
</h3> </h3>
<p class="text-[#aaa]">Current Month Earnings</p> <p class="text-#aaa">Current Month Earnings</p>
<h3 class="pt-32px text-24px font-bold"> <h3 class="pt-32px text-24px font-bold">
<count-to :start-value="0" :end-value="1234" /> <count-to :start-value="0" :end-value="1234" />
</h3> </h3>
<p class="text-[#aaa]">Current Month Sales</p> <p class="text-#aaa">Current Month Sales</p>
<n-button class="mt-24px whitespace-pre-wrap" type="primary">Last Month Summary</n-button> <n-button class="mt-24px whitespace-pre-wrap" type="primary">Last Month Summary</n-button>
</div> </div>
</n-card> </n-card>

View File

@@ -5,7 +5,7 @@
<icon-local-avatar class="text-70px" /> <icon-local-avatar class="text-70px" />
<div class="pl-12px"> <div class="pl-12px">
<h3 class="text-18px font-semibold">早安{{ auth.userInfo.userName }}, 今天又是充满活力的一天</h3> <h3 class="text-18px font-semibold">早安{{ auth.userInfo.userName }}, 今天又是充满活力的一天</h3>
<p class="leading-30px text-[#999]">今日多云转晴20 - 25</p> <p class="leading-30px text-#999">今日多云转晴20 - 25</p>
</div> </div>
</div> </div>
<n-space :size="24" :wrap="false"> <n-space :size="24" :wrap="false">

View File

@@ -1,6 +1,6 @@
<template> <template>
<div <div
class="flex-col-center h-120px p-12px border-1px border-[#efeff5] dark:border-[#ffffff17] rounded-4px hover:shadow-sm cursor-pointer" class="flex-col-center h-120px p-12px border-1px border-#efeff5 dark:border-#ffffff17 rounded-4px hover:shadow-sm cursor-pointer"
> >
<svg-icon :icon="icon" :style="{ color: iconColor }" class="text-30px" /> <svg-icon :icon="icon" :style="{ color: iconColor }" class="text-30px" />
<p class="py-8px text-16px">{{ label }}</p> <p class="py-8px text-16px">{{ label }}</p>

View File

@@ -1,13 +1,13 @@
<template> <template>
<div <div
class="h-120px p-4px border-1px border-[#efeff5] dark:border-[#ffffff17] rounded-4px hover:shadow-sm cursor-pointer" class="h-120px p-4px border-1px border-#efeff5 dark:border-#ffffff17 rounded-4px hover:shadow-sm cursor-pointer"
@click="handleOpenSite" @click="handleOpenSite"
> >
<header class="flex-y-center"> <header class="flex-y-center">
<svg-icon :icon="icon" :style="{ color: iconColor }" class="text-30px" /> <svg-icon :icon="icon" :style="{ color: iconColor }" class="text-30px" />
<h3 class="pl-12px text-18px font-semibold">{{ name }}</h3> <h3 class="pl-12px text-18px font-semibold">{{ name }}</h3>
</header> </header>
<p class="py-8px h-56px text-[#999]">{{ description }}</p> <p class="py-8px h-56px text-#999">{{ description }}</p>
<div class="flex justify-end"> <div class="flex justify-end">
<span>{{ author }}</span> <span>{{ author }}</span>
</div> </div>

View File

@@ -1,31 +1,33 @@
<template> <template>
<n-card title="用户管理" :bordered="false" class="rounded-16px shadow-sm"> <div class="h-full overflow-hidden">
<n-space class="pb-12px" justify="space-between"> <n-card title="用户管理" :bordered="false" class="rounded-16px shadow-sm">
<n-space> <n-space class="pb-12px" justify="space-between">
<n-button type="primary" @click="handleAddTable"> <n-space>
<icon-ic-round-plus class="mr-4px text-20px" /> <n-button type="primary" @click="handleAddTable">
新增 <icon-ic-round-plus class="mr-4px text-20px" />
</n-button> 新增
<n-button type="error"> </n-button>
<icon-ic-round-delete class="mr-4px text-20px" /> <n-button type="error">
删除 <icon-ic-round-delete class="mr-4px text-20px" />
</n-button> 删除
<n-button type="success"> </n-button>
<icon-uil:export class="mr-4px text-20px" /> <n-button type="success">
导出Excel <icon-uil:export class="mr-4px text-20px" />
</n-button> 导出Excel
</n-button>
</n-space>
<n-space align="center" :size="18">
<n-button size="small" type="primary" @click="getTableData">
<icon-mdi-refresh class="mr-4px text-16px" :class="{ 'animate-spin': loading }" />
刷新表格
</n-button>
<column-setting v-model:columns="columns" />
</n-space>
</n-space> </n-space>
<n-space align="center" :size="18"> <n-data-table :columns="columns" :data="tableData" :loading="loading" :pagination="pagination" />
<n-button size="small" type="primary" @click="getTableData"> <table-action-modal v-model:visible="visible" :type="modalType" :edit-data="editData" />
<icon-mdi-refresh class="mr-4px text-16px" :class="{ 'animate-spin': loading }" /> </n-card>
刷新表格 </div>
</n-button>
<column-setting v-model:columns="columns" />
</n-space>
</n-space>
<n-data-table :columns="columns" :data="tableData" :loading="loading" :pagination="pagination" />
<table-action-modal v-model:visible="visible" :type="modalType" :edit-data="editData" />
</n-card>
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx">

View File

@@ -11,7 +11,7 @@
<h3 class="py-24px text-24px font-bold">{{ item.label }}</h3> <h3 class="py-24px text-24px font-bold">{{ item.label }}</h3>
<swiper v-bind="item.options"> <swiper v-bind="item.options">
<swiper-slide v-for="i in 5" :key="i"> <swiper-slide v-for="i in 5" :key="i">
<div class="flex-center h-240px border-1px border-[#999] text-18px font-bold">Slide{{ i }}</div> <div class="flex-center h-240px border-1px border-#999 text-18px font-bold">Slide{{ i }}</div>
</swiper-slide> </swiper-slide>
</swiper> </swiper>
</div> </div>

View File

@@ -9,6 +9,7 @@
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"jsx": "preserve", "jsx": "preserve",
"moduleResolution": "node", "moduleResolution": "node",
"isolatedModules": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"strictNullChecks": true, "strictNullChecks": true,
@@ -17,7 +18,7 @@
"~/*": ["./*"], "~/*": ["./*"],
"@/*": ["./src/*"] "@/*": ["./src/*"]
}, },
"types": ["vite/client", "node", "unplugin-icons/types/vue", "naive-ui/volar", "unplugin-vue-macros/macros-global"] "types": ["vite/client", "node", "unplugin-icons/types/vue", "naive-ui/volar"]
}, },
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]
} }