feat: 整合登录

This commit is contained in:
xlsea
2024-08-16 16:33:11 +08:00
parent e6aa25e9f8
commit 243de247f7
49 changed files with 889 additions and 3882 deletions
+6 -6
View File
@@ -1,8 +1,8 @@
VITE_BASE_URL=/
VITE_APP_TITLE=SoybeanAdmin
VITE_APP_TITLE=RuoYi-Vue-Plus
VITE_APP_DESC=SoybeanAdmin is a fresh and elegant admin template
VITE_APP_DESC=RuoYi-Vue-Plus多租户管理系统
# the prefix of the icon name
VITE_ICON_PREFIX=icon
@@ -27,13 +27,13 @@ VITE_HTTP_PROXY=Y
VITE_ROUTER_HISTORY_MODE=history
# success code of backend service, when the code is received, the request is successful
VITE_SERVICE_SUCCESS_CODE=0000
VITE_SERVICE_SUCCESS_CODE=200
# logout codes of backend service, when the code is received, the user will be logged out and redirected to login page
VITE_SERVICE_LOGOUT_CODES=8888,8889
VITE_SERVICE_LOGOUT_CODES=401
# modal logout codes of backend service, when the code is received, the user will be logged out by displaying a modal
VITE_SERVICE_MODAL_LOGOUT_CODES=7777,7778
VITE_SERVICE_MODAL_LOGOUT_CODES=401
# token expired codes of backend service, when the code is received, it will refresh the token and resend the request
VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998
@@ -45,4 +45,4 @@ VITE_STATIC_SUPER_ROLE=R_SUPER
VITE_SOURCE_MAP=N
# Used to differentiate storage across different domains
VITE_STORAGE_PREFIX=SOY_
VITE_STORAGE_PREFIX=ry_
+14 -5
View File
@@ -1,7 +1,16 @@
# backend service base url, test environment
VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
VITE_SERVICE_BASE_URL=http://127.0.0.1:8080
# other backend service base url, test environment
VITE_OTHER_SERVICE_BASE_URL= `{
"demo": "http://localhost:9528"
}`
VITE_APP_BASE_API=/dev-api
VITE_APP_WEBSOCKET=true
# app client id
VITE_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
VITE_APP_ENCRYPT=true
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY='MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
VITE_APP_RSA_PRIVATE_KEY='MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
-50
View File
@@ -1,50 +0,0 @@
首先,感谢你的贡献! 😄
新特性请提交至 feature 分支,其余可提交至 main 分支。在一个维护者审核通过后合并。请确保填写以下 pull request 的信息,谢谢!~
[[English Template / 英文模板](./pr_en.md)]
### 这个变动的性质是
- [ ] 新特性提交
- [ ] 日常 bug 修复
- [ ] 站点、文档改进
- [ ] 组件样式改进
- [ ] TypeScript 定义更新
- [ ] 重构
- [ ] 代码风格优化
- [ ] 分支合并
- [ ] 其他改动(是关于什么的改动?)
### 需求背景
> 1. 描述相关需求的来源。
> 2. 要解决的问题。
> 3. 相关的 issue 讨论链接。
### 实现方案和 API(非新功能可选)
> 1. 基本的解决思路和其他可选方案。
> 2. 列出最终的 API 实现和用法。
> 3. 涉及 UI/交互变动需要有截图或 GIF。
### 对用户的影响和可能的风险(非新功能可选)
> 1. 这个改动对用户端是否有影响?影响的方面有哪些?
> 2. 是否有可能隐含的 break change 和其他风险?
### Changelog 描述(非新功能可选)
> 1. 英文描述
> 2. 中文描述(可选)
### 请求合并前的自查清单
- [ ] 文档已补充或无须补充
- [ ] 代码演示已提供或无须提供
- [ ] TypeScript 定义已补充或无须补充
- [ ] Changelog 已提供或无须提供
### 后续计划(非新功能可选)
> 如果这个提交后面还有相关的其他提交和跟进信息,可以写在这里。
-51
View File
@@ -1,51 +0,0 @@
First of all, thank you for your contribution! 😄
New feature please send pull request to feature branch, and rest to main branch. Pull request will be merged after one of collaborators approve. Please makes sure that these form are filled before submitting your pull request, thank you!
[[中文版模板 / Chinese template](./pr_cn.md)]
### This is a ...
- [ ] New feature
- [ ] Bug fix
- [ ] Site / document update
- [ ] Component style update
- [ ] TypeScript definition update
- [ ] Refactoring
- [ ] Code style optimization
- [ ] Branch merge
- [ ] Other (about what?)
### What's the background?
> 1. Describe the source of requirement.
> 2. Resolve what problem.
> 3. Related issue link.
### API Realization (Optional if not new feature)
> 1. Basic thought of solution and other optional proposal.
> 2. List final API realization and usage sample.
> 3. GIF or snapshot should be provided if includes UI/interactive modification.
### What's the effect? (Optional if not new feature)
> 1. Does this PR affect user? Which part will be affected?
> 2. What will say in changelog?
> 3. Does this PR contains potential break change or other risk?
### Changelog description (Optional if not new feature)
> 1. English description
> 2. Chinese description (optional)
### Self Check before Merge
- [ ] Doc is updated/provided or not needed
- [ ] Demo is updated/provided or not needed
- [ ] TypeScript definition is updated/provided or not needed
- [ ] Changelog is provided or not needed
### Additional Plan? (Optional if not new feature)
> If this PR related with other PR or following info. You can type here.
-2578
View File
File diff suppressed because it is too large Load Diff
-547
View File
@@ -1,547 +0,0 @@
# 更新日志
## [v1.3.2](https://github.com/honghuangdc/soybean-admin/compare/v1.3.1...v1.3.2) (2024-07-30)
###    🚀 新功能
- **项目**:
- 添加颜色渐变模式。关闭 #567 &nbsp;-&nbsp; 由 @Azir-11 在 https://github.com/honghuangdc/soybean-admin/issues/569 和 https://github.com/honghuangdc/soybean-admin/issues/567 [<samp>(4dde4)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4dde4c2)
- 添加全屏水印。关闭 #571 &nbsp;-&nbsp; 由 @paynezhuang 在 https://github.com/honghuangdc/soybean-admin/issues/573 和 https://github.com/honghuangdc/soybean-admin/issues/571 [<samp>(ea8aa)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ea8aa6c)
### &nbsp;&nbsp;&nbsp;🐞 Bug 修复
- **项目**: 修复 vertical-mix 菜单选中问题 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(59f07)</samp>](https://github.com/honghuangdc/soybean-admin/commit/59f07d8)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**:
- 添加类型 WatermarkProps &nbsp;-&nbsp; 由 @honghuangdc [<samp>(f26d0)</samp>](https://github.com/honghuangdc/soybean-admin/commit/f26d0a6)
- 移除 home NAlert 可关闭 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(98b75)</samp>](https://github.com/honghuangdc/soybean-admin/commit/98b75c2)
- 优化响应代码比较 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(cf67d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/cf67d55)
- **类型**:
- 移除无用类型。 &nbsp;-&nbsp; 由 **Azir** [<samp>(eed61)</samp>](https://github.com/honghuangdc/soybean-admin/commit/eed617f)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新更新日志 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(d3759)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d37591d)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **依赖**: 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(993e9)</samp>](https://github.com/honghuangdc/soybean-admin/commit/993e9ca)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;[![paynezhuang](https://github.com/paynezhuang.png?size=48)](https://github.com/paynezhuang)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;
[Azir](mailto:2075125282@qq.com),&nbsp;
## [v1.3.1](https://github.com/honghuangdc/soybean-admin/compare/v1.3.0...v1.3.1) (2024-07-22)
### &nbsp;&nbsp;&nbsp;🐞 Bug 修复
- **项目**:
- 修复顶部菜单混合和反向模式下侧边栏宽度异常的问题 &nbsp;-&nbsp; 由 @Azir-11 在 https://github.com/honghuangdc/soybean-admin/issues/562 修复 [<samp>(c4695)</samp>](https://github.com/honghuangdc/soybean-admin/commit/c469512)
- 修复水平混合菜单反转问题。修复 #563 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/563 修复 [<samp>(4e55b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4e55b0e)
- 修复当布局来自移动端时垂直菜单不渲染的问题 &nbsp;-&nbsp; 由 @honghuangdc 修复 [<samp>(84027)</samp>](https://github.com/honghuangdc/soybean-admin/commit/8402734)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新更新日志 &nbsp;-&nbsp; 由 @honghuangdc 更新 [<samp>(613c8)</samp>](https://github.com/honghuangdc/soybean-admin/commit/613c836)
### &nbsp;&nbsp;&nbsp;🎨 样式
- **其他**: 修改灰度模式的中文名称 &nbsp;-&nbsp; 由 **Azir** 修改 [<samp>(53770)</samp>](https://github.com/honghuangdc/soybean-admin/commit/5377002)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;
[Azir](mailto:2075125282@qq.com),&nbsp;
## [v1.3.0](https://github.com/soybeanjs/soybean-admin/compare/v1.2.8...v1.3.0) (2024-07-22)
### &nbsp;&nbsp;&nbsp;🚨 破坏性变更
- **项目**: 重构全局菜单 & 支持 `reversed-horizontal-mix-menu`。关闭 #365 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/365 提出 [<samp>(087e5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/087e532)
### &nbsp;&nbsp;&nbsp;🚀 功能
- **包**:
- `@sa/scripts`: 命令 `gitCommit` 支持中文 &nbsp;-&nbsp; 由 @mmdapl 在 https://github.com/soybeanjs/soybean-admin/issues/548 提出 [<samp>(06971)</samp>](https://github.com/soybeanjs/soybean-admin/commit/06971f3)
- @sa/axios: 用 AbortController 替换 CancelTokenSource。关闭 #530, 关闭 #532 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/530 和 https://github.com/soybeanjs/soybean-admin/issues/532 提出 [<samp>(527fd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/527fd79)
- @sa/scripts: 为命令 `gitCommitVerify` 添加忽略模式列表。关闭 #504 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/504 提出 [<samp>(958d0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/958d0ba)
- **项目**:
- 使分支 `main` 更精简 & 修改请求重试次数为 0 &nbsp;-&nbsp; 由 @Azir-11 提出 [<samp>(793b1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/793b16e)
### &nbsp;&nbsp;&nbsp;🐞 修复
- **钩子**: 当 pagesize 返回 0 时防止程序冻结 &nbsp;-&nbsp; 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/545 提出 [<samp>(f4eeb)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f4eeb2e)
### &nbsp;&nbsp;&nbsp;💅 重构
- **项目**:
- 合并 `theme tokens` 和 `theme settings`。关闭 #379 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/379 提出 [<samp>(1d1b1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1d1b148)
- 将 css 变量挂载到 root &nbsp;-&nbsp; 由 @honghuangdc 提出 [<samp>(00f41)</samp>](https://github.com/soybeanjs/soybean-admin/commit/00f41dd)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新更新日志 &nbsp;-&nbsp; 由 @honghuangdc 提出 [<samp>(a0b76)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a0b76da)
### &nbsp;&nbsp;&nbsp;🏡 杂务
- **依赖**: 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc 提出 [<samp>(f6bd6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f6bd6b8)
- **项目**: 添加脚本 `czh` &nbsp;-&nbsp; 由 @honghuangdc 提出 [<samp>(02069)</samp>](https://github.com/soybeanjs/soybean-admin/commit/0206969)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;[![mmdapl](https://github.com/mmdapl.png?size=48)](https://github.com/mmdapl)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;
## [v1.2.8](https://github.com/soybeanjs/soybean-admin/compare/v1.2.7...v1.2.8) (2024-07-20)
### &nbsp;&nbsp;&nbsp;🐞 修复
- **包**:
- @sa/hooks: 修复 useHookTable 的 searchParams。修复了 #552 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/552 提出 [<samp>(96c10)</samp>](https://github.com/soybeanjs/soybean-admin/commit/96c1044)
- **类型**:
- 修复了引用类型错误 &nbsp;-&nbsp; 由 **dodu2014** 在 https://github.com/soybeanjs/soybean-admin/issues/551 提出 [<samp>(3e2a9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3e2a993)
- 修复 useHookTable 的数据类型 &nbsp;-&nbsp; 由 @honghuangdc 提出 [<samp>(276ea)</samp>](https://github.com/soybeanjs/soybean-admin/commit/276ea7f)
### &nbsp;&nbsp;&nbsp;💅 重构
- **项目**: 用 `klona` 替换 `lodash-es` 的 `cloneDeep` &nbsp;-&nbsp; 由 @honghuangdc 提出 [<samp>(a9133)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a91335d)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新更新日志 &nbsp;-&nbsp; 由 @honghuangdc 提出 [<samp>(58fc0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/58fc096)
### &nbsp;&nbsp;&nbsp;🏡 杂务
- **依赖**: 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc 提出 [<samp>(cf019)</samp>](https://github.com/soybeanjs/soybean-admin/commit/cf0192a)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
[dodu2014](mailto:dodu@live.cn)
## [v1.2.7](https://github.com/honghuangdc/soybean-admin/compare/v1.2.6...v1.2.7) (2024-07-12)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**: 支持自定义菜单图标大小 &nbsp;-&nbsp; 由 @wynn-w 在 https://github.com/honghuangdc/soybean-admin/issues/534 提出 [<samp>(e035e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/e035eab)
### &nbsp;&nbsp;&nbsp;🏡 杂务
- **依赖**:
- 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(72ede)</samp>](https://github.com/honghuangdc/soybean-admin/commit/72ede8b)
- 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(be13c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/be13ca2)
- 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(752ec)</samp>](https://github.com/honghuangdc/soybean-admin/commit/752ec1e)
- **项目**:
- 修复已弃用的配置配置 &nbsp;-&nbsp; 由 @paynezhuang 在 https://github.com/honghuangdc/soybean-admin/issues/524 提出 [<samp>(0d20e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/0d20e4c)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;[![wynn-w](https://github.com/wynn-w.png?size=48)](https://github.com/wynn-w)&nbsp;&nbsp;[![paynezhuang](https://github.com/paynezhuang.png?size=48)](https://github.com/paynezhuang)&nbsp;&nbsp;
## [v1.2.6](https://github.com/honghuangdc/soybean-admin/compare/v1.2.5...v1.2.6) (2024-06-21)
### &nbsp;&nbsp;&nbsp;🐞 修复
- **项目**:
- 请求模态标题使用 i18n。修复 #507 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/507 提出 [<samp>(f7de3)</samp>](https://github.com/honghuangdc/soybean-admin/commit/f7de3fd)
- 为 `useTable` 添加 `getDataByPage`。修复 #499 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/499 提出 [<samp>(425c6)</samp>](https://github.com/honghuangdc/soybean-admin/commit/425c69a)
- 修复登录重定向到 routeHome 时,动态路由的 routeHome 与静态路由不同的问题。修复 #511 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/511 提出 [<samp>(49f60)</samp>](https://github.com/honghuangdc/soybean-admin/commit/49f60b2)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**: 优化 `getRouteQueryOfLoginRoute` &nbsp;-&nbsp; 由 @honghuangdc [<samp>(693f7)</samp>](https://github.com/honghuangdc/soybean-admin/commit/693f704)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**:
- 更新更新日志 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(5c67d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/5c67d06)
- 更新自述文件 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(1e67a)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1e67ae8)
### &nbsp;&nbsp;&nbsp;🏡 杂务
- **依赖**:
- 更新依赖。关闭 #510 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/510 提出 [<samp>(53143)</samp>](https://github.com/honghuangdc/soybean-admin/commit/531432d)
- 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(c7f6f)</samp>](https://github.com/honghuangdc/soybean-admin/commit/c7f6f2a)
### &nbsp;&nbsp;&nbsp;🤖 CI
- **项目**:
- 添加 github 问题模板 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(b5027)</samp>](https://github.com/honghuangdc/soybean-admin/commit/b5027c8)
- 更新 github 问题模板 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(ff1d5)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ff1d504)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
## [v1.2.5](https://github.com/soybeanjs/soybean-admin/compare/v1.2.4...v1.2.5) (2024-06-15)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **项目**: 修复登出后标签页异常缓存的问题。已修复 #495 &nbsp;-&nbsp; 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/495 [<samp>(3eeac)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3eeace9)
### &nbsp;&nbsp;&nbsp;🔥 性能
- **项目**: 初始化静态路由功能不需要异步 &nbsp;-&nbsp; 由 **CHENZL** 在 https://github.com/soybeanjs/soybean-admin/issues/493 [<samp>(2198b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2198b98)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**: 优化代码 &nbsp;-&nbsp; 由 @soybeanjs [<samp>(b94ba)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b94baa1)
- **类型**: 提高全局类型的兼容性 &nbsp;-&nbsp; 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/494 [<samp>(cd9d5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/cd9d58d)
- **工具**: 减少代码缩进,提高可读性 &nbsp;-&nbsp; 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/496 [<samp>(ad2f2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ad2f247)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新CHANGELOG &nbsp;-&nbsp; 由 @soybeanjs [<samp>(f70d2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f70d29b)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **依赖**: 更新依赖 &nbsp;-&nbsp; 由 @soybeanjs [<samp>(b094d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b094d68)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;
[CHENZL](mailto:zlong5568863@qq.com)
## [v1.2.4](https://github.com/soybeanjs/soybean-admin/compare/v1.2.3...v1.2.4) (2024-06-14)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**:
- 优化 `setupAppVersionNotification` &nbsp;-&nbsp; 由 @soybeanjs 提交 [<samp>(b5a72)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b5a723c)
- 获取 'Asia/Shanghai' 时区的构建时间 &nbsp;-&nbsp; 由 @soybeanjs 提交 [<samp>(069fa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/069fa8a)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
## [v1.2.3](https://github.com/soybeanjs/soybean-admin/compare/v1.2.2...v1.2.3) (2024-06-13)
### &nbsp;&nbsp;&nbsp;🐞 修复错误
- **项目**:
- 通过在index.html中添加color-scheme元标签修复移动浏览器主题问题 &nbsp;-&nbsp; 由 @KickCashew 在 https://github.com/soybeanjs/soybean-admin/issues/488 中提交 [<samp>(c2125)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c212565)
- 修复二级目录组件为空 &nbsp;-&nbsp; 由 @paynezhuang 在 https://github.com/soybeanjs/soybean-admin/issues/491 中提交 [<samp>(aabb2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/aabb2a4)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**:
- 修复超链接指向错误 &nbsp;-&nbsp; 由 **Azir** 提交 [<samp>(20a81)</samp>](https://github.com/soybeanjs/soybean-admin/commit/20a8127)
- 更新 README &nbsp;-&nbsp; 由 @soybeanjs 提交 [<samp>(70261)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7026126)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **依赖**:
- 更新依赖 &nbsp;-&nbsp; 由 @soybeanjs 提交 [<samp>(813d8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/813d8ce)
- 更新依赖 &nbsp;-&nbsp; 由 @soybeanjs 提交 [<samp>(bf718)</samp>](https://github.com/soybeanjs/soybean-admin/commit/bf71837)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![paynezhuang](https://github.com/paynezhuang.png?size=48)](https://github.com/paynezhuang)&nbsp;&nbsp;[![KickCashew](https://github.com/KickCashew.png?size=48)](https://github.com/KickCashew)&nbsp;&nbsp;
[Azir](mailto:2075125282@qq.com)
## [v1.2.2](https://github.com/honghuangdc/soybean-admin/compare/v1.2.1...v1.2.2) (2024-06-12)
### &nbsp;&nbsp;&nbsp;🚀 特性
- **项目**: 切换标签时重置滚动位置 &nbsp;-&nbsp; 由 @soybeanjs 提交 [<samp>(9094b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/9094b21)
### &nbsp;&nbsp;&nbsp;🐞 修复错误
- **项目**:
- 在DEV模式下隐藏AppVersionNotification &nbsp;-&nbsp; 由 @sigma-plus 在 https://github.com/honghuangdc/soybean-admin/issues/482 中提交 [<samp>(62592)</samp>](https://github.com/honghuangdc/soybean-admin/commit/6259287)
- 修复在移动布局中隐藏menu-toggler。fixed #483 &nbsp;-&nbsp; 由 @soybeanjs 在 https://github.com/honghuangdc/soybean-admin/issues/483 中提交 [<samp>(4470c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4470cb4)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新 README &nbsp;-&nbsp; 由 @soybeanjs 提交 [<samp>(8f9a7)</samp>](https://github.com/honghuangdc/soybean-admin/commit/8f9a705)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![sigma-plus](https://github.com/sigma-plus.png?size=48)](https://github.com/sigma-plus)&nbsp;&nbsp;
## [v1.2.1](https://github.com/honghuangdc/soybean-admin/compare/v1.2.0...v1.2.1) (2024-06-07)
### &nbsp;&nbsp;&nbsp;🐞 修复错误
- **项目**:
- 修复页面重新加载时获取用户信息 &nbsp;-&nbsp; 由 @soybeanjs 提交 [<samp>(ff51b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ff51b72)
- 修复setupAppVersionNotification渲染 &nbsp;-&nbsp; 由 @soybeanjs 提交 [<samp>(6a6eb)</samp>](https://github.com/honghuangdc/soybean-admin/commit/6a6eb9a)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新CHANGELOG &nbsp;-&nbsp; 由 @soybeanjs [<samp>(fe06b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/fe06b8c)完成
### &nbsp;&nbsp;&nbsp;🏡 日常任务
- **依赖**: 更新依赖 &nbsp;-&nbsp; 由 @soybeanjs [<samp>(08827)</samp>](https://github.com/honghuangdc/soybean-admin/commit/08827a4)完成
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
## [v1.2.0](https://github.com/soybeanjs/soybean-admin/compare/v1.1.5...v1.2.0) (2024-06-06)
### &nbsp;&nbsp;&nbsp;🚀 功能
- **项目**:
- 支持系统新版本更新通知。关闭 #420 &nbsp;-&nbsp; 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/420 [<samp>(584cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/584cd54)
- 在路由守卫中获取用户信息并从localStorage中移除。关闭 #459 &nbsp;-&nbsp; 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/459 [<samp>(5531a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5531a68)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新CHANGELOG &nbsp;-&nbsp; 由 @soybeanjs [<samp>(2bec8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2bec899)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
## [v1.1.5](https://github.com/soybeanjs/soybean-admin/compare/v1.1.4...v1.1.5) (2024-06-06)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **项目**: 修复注册组件名,CodeLogin => Register &nbsp;-&nbsp; 由 @m-xlsea 在 https://github.com/soybeanjs/soybean-admin/issues/478 [<samp>(ddf38)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ddf3823)
### &nbsp;&nbsp;&nbsp;🏡 杂务
- **依赖**: 更新依赖 &nbsp;-&nbsp; 由 @soybeanjs [<samp>(060c0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/060c0a9)
- **项目**: 更新 vscode 设置: vue 官方 &nbsp;-&nbsp; 由 @soybeanjs [<samp>(76649)</samp>](https://github.com/soybeanjs/soybean-admin/commit/76649e2)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![m-xlsea](https://github.com/m-xlsea.png?size=48)](https://github.com/m-xlsea)&nbsp;&nbsp;
## [v1.1.4](https://github.com/honghuangdc/soybean-admin/compare/v1.1.3...v1.1.4) (2024-06-06)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **utils**: 修复了按esc键时modalLogout的错误 &nbsp;-&nbsp; 由 @sigma-plus 在 https://github.com/honghuangdc/soybean-admin/issues/470 中提出 [<samp>(bd69c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/bd69c00)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **projects**: 优化了RouteMeta的备注 &nbsp;-&nbsp; 由 @soybeanjs 提出 [<samp>(ffb48)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ffb48b1)
### &nbsp;&nbsp;&nbsp;📖 文档
- **projects**:
- 更新了CHANGELOG &nbsp;-&nbsp; 由 @soybeanjs 提出 [<samp>(756f8)</samp>](https://github.com/honghuangdc/soybean-admin/commit/756f84a)
- 更新了Node&pnpm版本 &nbsp;-&nbsp; 由 @Azir-11 在 https://github.com/honghuangdc/soybean-admin/issues/472 中提出 [<samp>(9b05d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/9b05d73)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **deps**:
- 更新了依赖 &nbsp;-&nbsp; 由 @soybeanjs 提出 [<samp>(d0380)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d0380ce)
- 更新了依赖 &nbsp;-&nbsp; 由 @soybeanjs 提出 [<samp>(1f464)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1f4647b)
- **projects**:
- 关闭了http代理 &nbsp;-&nbsp; 由 @soybeanjs 提出 [<samp>(d08a3)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d08a381)
- 更新了mock url &nbsp;-&nbsp; 由 @soybeanjs 提出 [<samp>(e6086)</samp>](https://github.com/honghuangdc/soybean-admin/commit/e6086f0)
- 更新了vscode设置 &nbsp;-&nbsp; 由 @soybeanjs 提出 [<samp>(910df)</samp>](https://github.com/honghuangdc/soybean-admin/commit/910dfca)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;[![sigma-plus](https://github.com/sigma-plus.png?size=48)](https://github.com/sigma-plus)&nbsp;&nbsp;
## [v1.1.3](https://github.com/soybeanjs/soybean-admin/compare/v1.1.2...v1.1.3) (2024-06-02)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **组件**:
- 修复了由于回车导致搜索框反复弹出的问题 &nbsp;-&nbsp; 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/468 中修复 [<samp>(5bd96)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5bd96b8)
- **项目**:
- 修复点击菜单搜索。修复了 #466,关闭 #467 &nbsp;-&nbsp; 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/466 和 https://github.com/soybeanjs/soybean-admin/issues/467 中修复 [<samp>(8efdb)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8efdb10)
- 修复 reCacheRoute。修复了 #464 &nbsp;-&nbsp; 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/464 中修复 [<samp>(59faf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/59faf15)
- **样式**:
- 修复 FirstLevelMenu 样式。修复了 #450 &nbsp;-&nbsp; 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/450 中修复 [<samp>(db64b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/db64b0e)
- 修复 PinToggler 样式。修复了 #451 &nbsp;-&nbsp; 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/451 中修复 [<samp>(42b12)</samp>](https://github.com/soybeanjs/soybean-admin/commit/42b121a)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **组件**: 提高 TableColumnSetting 的拖动区域精度,带有动画 &nbsp;-&nbsp; 由 @orangelckc 在 https://github.com/soybeanjs/soybean-admin/issues/465 中优化 [<samp>(2aa85)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2aa85c6)
- **项目**: unocss 边框快捷方式 &nbsp;-&nbsp; 由 @soybeanjs 优化 [<samp>(40d0f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/40d0f8a)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新 CHANGELOG &nbsp;-&nbsp; 由 @soybeanjs 更新 [<samp>(87b18)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87b1838)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **其他**:
- 纠正拼写错误 &nbsp;-&nbsp; 由 @orangelckc 在 https://github.com/soybeanjs/soybean-admin/issues/460 中纠正 [<samp>(086ba)</samp>](https://github.com/soybeanjs/soybean-admin/commit/086bad4)
- 纠正拼写错误 &nbsp;-&nbsp; 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/462 中纠正 [<samp>(f1850)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f185041)
- **项目**:
- 更新 vscode launch.json &nbsp;-&nbsp; 由 @soybeanjs 更新 [<samp>(4c1c7)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4c1c7e6)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;[![orangelckc](https://github.com/orangelckc.png?size=48)](https://github.com/orangelckc)&nbsp;&nbsp;
## [v1.1.2](https://github.com/soybeanjs/soybean-admin/compare/v1.1.1...v1.1.2) (2024-05-24)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **项目**:
- 修复头部样式 & 修复点击全局标签时按钮高亮。修复了 #446 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/446 [<samp>(64fc0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/64fc099)
- 修复多标签页只渲染一次。修复了 #441 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/441 [<samp>(e379d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e379d6c)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**: 优化代码 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(bc8dc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/bc8dc47)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
## [v1.1.1](https://github.com/soybeanjs/soybean-admin/compare/v1.1.0...v1.1.1) (2024-05-20)
### &nbsp;&nbsp;&nbsp;🚀 功能
- **hooks**: 为 useEcharts 添加 setOptions &nbsp;-&nbsp; 由 @honghuangdc 提交 [<samp>(e4d53)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e4d53aa)
### &nbsp;&nbsp;&nbsp;🐞 修复错误
- **projects**:
- 修复 useRouter。修复了 #436 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/436 提交 [<samp>(0774a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/0774a51)
- 在动态路由模式下获取路由时添加错误处理。修复了 440 &nbsp;-&nbsp; 由 @honghuangdc 提交 [<samp>(57b4a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/57b4a9d)
- **styles**:
- 修复 useTable 类型 &nbsp;-&nbsp; 由 @honghuangdc 提交 [<samp>(07124)</samp>](https://github.com/soybeanjs/soybean-admin/commit/071241f)
### &nbsp;&nbsp;&nbsp;📖 文档
- **projects**:
- 更新 CHANGELOG &nbsp;-&nbsp; 由 @honghuangdc 提交 [<samp>(19783)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1978397)
- 更新 README.md &nbsp;-&nbsp; 由 @honghuangdc 提交 [<samp>(fa56e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/fa56e9c)
- 更新 README.md &nbsp;-&nbsp; 由 @honghuangdc 提交 [<samp>(419ea)</samp>](https://github.com/soybeanjs/soybean-admin/commit/419ea42)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **projects**:
- 更新依赖并修复 TS 错误 &nbsp;-&nbsp; 由 @honghuangdc 提交 [<samp>(4ea9c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4ea9c85)
- 更新 eslint-config 并修复代码 &nbsp;-&nbsp; 由 @honghuangdc 提交 [<samp>(68ea9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/68ea974)
- 更新 @elegant-router/vue 并为 resolve route 添加错误处理。修复了 #442 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/442 提交 [<samp>(24ff8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/24ff852)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
## [v1.1.0](https://github.com/honghuangdc/soybean-admin/compare/v1.0.9...v1.1.0) (2024-05-07)
### &nbsp;&nbsp;&nbsp;🚀 功能
- **项目**:
- 支持灰度。修复了 #385 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/385 [<samp>(d335d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d335df6)
- 添加前缀到本地存储 &nbsp;-&nbsp; 由 **Azir** [<samp>(1fc34)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1fc34cc)
- 添加表格显示总数选项 &nbsp;-&nbsp; 由 **paynezhuang** [<samp>(3e61e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3e61eab)
- 添加推荐颜色切换。关闭了 #388 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/388 [<samp>(a1920)</samp>](https://github.com/honghuangdc/soybean-admin/commit/a1920fc)
- 添加菜单路由字段 &nbsp;-&nbsp; 由 **paynezhuang** [<samp>(dbe31)</samp>](https://github.com/honghuangdc/soybean-admin/commit/dbe31eb)
- 支持短时间内重复请求错误只出现一次。关闭了 #368, 关闭了 #369 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/368 和 https://github.com/honghuangdc/soybean-admin/issues/369 [<samp>(e3bd3)</samp>](https://github.com/honghuangdc/soybean-admin/commit/e3bd397)
- 通过鼠标滚轮按钮点击关闭标签 &nbsp;-&nbsp; 由 **JianJroh** [<samp>(d3849)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d3849ba)
- 页面:支持管理菜单更多选项。关闭了 #366 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/366 [<samp>(c4b5c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/c4b5c65)
- useTable 添加展开显示 &nbsp;-&nbsp; 由 **paynezhuang** [<samp>(0a90d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/0a90dd3)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **项目**:
- 菜单 fixedIndexInTab 默认为 null &nbsp;-&nbsp; 由 **paynezhuang** [<samp>(3d10e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3d10ef1)
- 修复菜单切换器 zIndex &nbsp;-&nbsp; 由 @honghuangdc [<samp>(7bd43)</samp>](https://github.com/honghuangdc/soybean-admin/commit/7bd43df)
- 修复管理菜单模态样式 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(60f3b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/60f3b14)
- 当角色改变时修复菜单数据。修复了 #391 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/391 [<samp>(3b47b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3b47b5a)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**: 移除废弃的代码 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(72ccb)</samp>](https://github.com/honghuangdc/soybean-admin/commit/72ccb6b)
### &nbsp;&nbsp;&nbsp;💅 重构
- **项目**:
- 重构 @sa/color-palette => @sa/color & 优化 @sa/utils 代码 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(34999)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3499997)
- menu-operate-drawer => menu-operate-modal &nbsp;-&nbsp; 由 @honghuangdc [<samp>(003e1)</samp>](https://github.com/honghuangdc/soybean-admin/commit/003e145)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**:
- 添加 CHANGELOG.zh_CN.md &nbsp;-&nbsp; 由 @honghuangdc [<samp>(18b3f)</samp>](https://github.com/honghuangdc/soybean-admin/commit/18b3f05)
- 更新 CHANGELOG &nbsp;-&nbsp; 由 @honghuangdc [<samp>(4d17c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4d17cfd)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **依赖**:
- 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(1cb38)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1cb3816)
- 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(599b4)</samp>](https://github.com/honghuangdc/soybean-admin/commit/599b4e1)
- **项目**:
- 合并 main 到 v1.1.0 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(ebe55)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ebe55af)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
[paynezhuang](mailto:paynezhuang@gmail.com),&nbsp;[JianJroh](mailto:rhjian@foxmail.com),&nbsp;[Azir](mailto:2075125282@qq.com)
## [v1.1.0-beta.2](https://github.com/honghuangdc/soybean-admin/compare/v1.1.0-beta.1...v1.1.0-beta.2) (2024-05-07)
### &nbsp;&nbsp;&nbsp;🚀 特性
- **项目**: useTable添加展开以显示 &nbsp;-&nbsp; 由 **paynezhuang** [<samp>(0a90d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/0a90dd3) 提供
### &nbsp;&nbsp;&nbsp;🐞 修复的错误
- **项目**:
- 修复 manage_menu 模态样式 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(60f3b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/60f3b14) 提供
- 当角色改变时修复菜单数据。修复了 #391 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/391 [<samp>(3b47b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3b47b5a) 提供
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**: 删除废弃的代码 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(72ccb)</samp>](https://github.com/honghuangdc/soybean-admin/commit/72ccb6b) 提供
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 添加 CHANGELOG.zh_CN.md &nbsp;-&nbsp; 由 @honghuangdc [<samp>(18b3f)</samp>](https://github.com/honghuangdc/soybean-admin/commit/18b3f05) 提供
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
[paynezhuang](mailto:paynezhuang@gmail.com)
## [v1.1.0-beta.1](https://github.com/soybeanjs/soybean-admin/compare/v1.0.9...v1.1.0-beta.1) (2024-05-07)
### &nbsp;&nbsp;&nbsp;🚀 功能
- **项目**:
- 支持灰度。修复了 #385 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/385 [<samp>(d335d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d335df6)
- 添加前缀到本地存储 &nbsp;-&nbsp; 由 **Azir** [<samp>(1fc34)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1fc34cc)
- 添加表格 showTotal 选项 &nbsp;-&nbsp; 由 **paynezhuang** [<samp>(3e61e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3e61eab)
- 添加推荐颜色切换。关闭了 #388 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/388 [<samp>(a1920)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a1920fc)
- 添加菜单路由字段 &nbsp;-&nbsp; 由 **paynezhuang** [<samp>(dbe31)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dbe31eb)
- 支持短时间内重复请求错误只发生一次。关闭了 #368, 关闭了 #369 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/368 和 https://github.com/soybeanjs/soybean-admin/issues/369 [<samp>(e3bd3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e3bd397)
- 通过鼠标滚轮按钮点击关闭标签 &nbsp;-&nbsp; 由 **JianJroh** [<samp>(d3849)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d3849ba)
- 页面:支持更多的 manage_menu 选项。关闭了 #366 &nbsp;-&nbsp; 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/366 [<samp>(c4b5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c4b5c65)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **项目**:
- 修复菜单 fixedIndexInTab 默认为 null &nbsp;-&nbsp; 由 **paynezhuang** [<samp>(3d10e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3d10ef1)完成
- 修复菜单切换器 zIndex &nbsp;-&nbsp; 由 @honghuangdc [<samp>(7bd43)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7bd43df)完成
### &nbsp;&nbsp;&nbsp;💅 重构
- **项目**:
- 重构 @sa/color-palette => @sa/color & 性能优化 @sa/utils &nbsp;-&nbsp; 由 @honghuangdc [<samp>(34999)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3499997)完成
- menu-operate-drawer => menu-operate-modal &nbsp;-&nbsp; 由 @honghuangdc [<samp>(003e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/003e145)完成
### &nbsp;&nbsp;&nbsp;🏡 杂务
- **依赖**:
- 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(1cb38)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1cb3816)完成
- 更新依赖 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(599b4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/599b4e1)完成
- **项目**:
- 合并主分支到 v1.1.0 &nbsp;-&nbsp; 由 @honghuangdc [<samp>(ebe55)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ebe55af)完成
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
[JianJroh](mailto:rhjian@foxmail.com),&nbsp;[paynezhuang](mailto:paynezhuang@gmail.com),&nbsp;[Azir](mailto:2075125282@qq.com)
-179
View File
@@ -1,179 +0,0 @@
<div align="center">
<img src="./public/favicon.svg" width="160" />
<h1>SoybeanAdmin</h1>
<span>English | <a href="./README.zh_CN.md">中文</a></span>
</div>
---
[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
[![github stars](https://img.shields.io/github/stars/soybeanjs/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
[![github forks](https://img.shields.io/github/forks/soybeanjs/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
[![gitee stars](https://gitee.com/honghuangdc/soybean-admin/badge/star.svg)](https://gitee.com/honghuangdc/soybean-admin)
<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
> [!NOTE]
> If you think `SoybeanAdmin` is helpful to you, or you like our project, please give us a ⭐️ on GitHub. Your support is the driving force for us to continue to improve and add new features! Thank you for your support!
## Introduction
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) is a clean, elegant, beautiful and powerful admin template, based on the latest front-end technology stack, including Vue3, Vite5, TypeScript, Pinia and UnoCSS. It has built-in rich theme configuration and components, strict code specifications, and an automated file routing system. In addition, it also uses the online mock data solution based on ApiFox. `SoybeanAdmin` provides you with a one-stop admin solution, no additional configuration, and out of the box. It is also a best practice for learning cutting-edge technologies quickly.
## Features
- **Cutting-edge technology application**: using the latest popular technology stack such as Vue3, Vite5, TypeScript, Pinia and UnoCSS.
- **Clear project architecture**: using pnpm monorepo architecture, clear structure, elegant and easy to understand.
- **Strict code specifications**: follow the [SoybeanJS specification](https://docs.soybeanjs.cn/standard), integrate eslint, prettier and simple-git-hooks to ensure the code is standardized.
- **TypeScript**: support strict type checking to improve code maintainability.
- **Rich theme configuration**: built-in a variety of theme configurations, perfectly integrated with UnoCSS.
- **Built-in internationalization solution**: easily realize multi-language support.
- **Automated file routing system**: automatically generate route import, declaration and type. For more details, please refer to [Elegant Router](https://github.com/soybeanjs/elegant-router).
- **Flexible permission routing**: support both front-end static routing and back-end dynamic routing.
- **Rich page components**: built-in a variety of pages and components, including 403, 404, 500 pages, as well as layout components, tag components, theme configuration components, etc.
- **Command line tool**: built-in efficient command line tool, git commit, delete file, release, etc.
- **Mobile adaptation**: perfectly support mobile terminal to realize adaptive layout.
## Version
- **NaiveUI Version:**
- [Preview Link](https://naive.soybeanjs.cn/)
- [Github Repository](https://github.com/soybeanjs/soybean-admin)
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin)
- **AntDesignVue Version:**
- [Preview Link](https://antd.soybeanjs.cn/)
- [Github Repository](https://github.com/soybeanjs/soybean-admin-antd)
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin-antd)
- **Legacy Version:**
- [Preview Link](https://legacy.soybeanjs.cn/)
- [Github Repository](https://github.com/soybeanjs/soybean-admin/tree/legacy)
## Documentation
- [Link](https://docs.soybeanjs.cn)
- [Legacy Docs](https://legacy-docs.soybeanjs.cn)
## Example Images
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-01.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-02.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-03.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-04.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-05.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-06.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-07.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-08.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-09.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-10.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-mobile.png)
## Usage
**Environment Preparation**
Make sure your environment meets the following requirements:
- **git**: you need git to clone and manage project versions.
- **NodeJS**: >=18.12.0, recommended 18.19.0 or higher.
- **pnpm**: >= 8.7.0, recommended 8.14.0 or higher.
**Clone Project**
```bash
git clone https://github.com/soybeanjs/soybean-admin.git
```
**Install Dependencies**
```bash
pnpm i
```
> Since this project uses the pnpm monorepo management method, please do not use npm or yarn to install dependencies.
**Start Project**
```bash
pnpm dev
```
**Build Project**
```bash
pnpm build
```
## Ecosystem
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): A Mock Api management system that helps front-end developers quickly implement interface mocks.
- [T-Shell](https://github.com/TheBlindM/T-Shell): A terminal emulator and SSH client with configurable command prompts.
- [pea](https://github.com/haitang1894/pea) : Adopting SpringBoot3.2 + JDK21, MyBatis-Plus, SpringSecurity security framework, etc., suitable for the simple permission system developed by [soybean-admin](https://gitee.com/honghuangdc/soybean-admin).
- [MalusAdmin](https://github.com/pridejoy/MalusAdmin): A backend management framework developed based on Vue3/TypeScript/NaiveUI and NET7 & Sqlsugar. It is implemented in the most original and simplest way, with a fresh and elegant front-end, a clear and elegant backend structure, and powerful functions.
- [PanisAdmin](https://github.com/paynezhuang/panis-admin): Adopting SpringBoot 3, SaToken, MySQL and other frameworks to develop and modify [soybean-admin](https://github.com/soybeanjs/soybean-admin) for the second time, adapting dynamic menu/button-level authorization. Retaining the original flavor, fresh and elegant, high-value back-end management system scaffold.
- [snail-job](https://github.com/aizuda/snail-job): A distributed task retry and task scheduling platform with "high performance, high value and high activity".
- [SuperApi](https://github.com/TmmTop/SuperApi): Quickly turn your idea into an online stable product! Entity-less library and table building, add, delete, change and check entity-less library table, support 15 kinds of condition query, as well as paging, list, unlimited tree list and other functions of the API deployment! With interface documentation, Auth authorisation, interface flow restriction, access to the client's real IP, advanced server caching components, dynamic APIs and other features, we look forward to your experience!
- [FastSoyAdmin](https://github.com/sleep1223/fast-soy-admin): A modern Management Platform based on FastAPI+Vue3+Naive UI.
## How to Contribute
We warmly welcome and appreciate all forms of contributions. If you have any ideas or suggestions, please feel free to share them by submitting [pull requests](https://github.com/soybeanjs/soybean-admin/pulls) or creating GitHub [issue](https://github.com/soybeanjs/soybean-admin/issues/new).
## Git Commit Guidelines
This project has built-in `commit` command, you can execute `pnpm commit` to generate commit information that conforms to [Conventional Commits](https://www.conventionalcommits.org/) specification. When submitting PR, please be sure to use `commit` command to create commit information to ensure the standardization of information.
## Browser Support
It is recommended to use the latest version of Chrome in development for a better experience.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) |
| --- | --- | --- | --- | --- |
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## OpenSource Author
[Soybean](https://github.com/honghuangdc)
## Contributors
Thanks the following people for their contributions. If you want to contribute to this project, please refer to [How to Contribute](#how-to-contribute).
<a href="https://github.com/soybeanjs/soybean-admin/graphs/contributors">
<img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" />
</a>
## Communication
`SoybeanAdmin` is a completely open source and free project, helping developers to develop medium and large-scale management systems more conveniently. It also provides WeChat and QQ communication groups. If you have any questions, please feel free to ask in the group.
<div>
<p>QQ Group</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-2.jpg" style="width:200px" />
</div>
<!-- <div>
<p>WeChat Group</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/picgo/soybean-admin-wechat-0620.jpg" style="width:200px" />
</div> -->
<div>
<p>Add the following WeChat to invite to the WeChat group</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybeanjs.jpg" style="width:200px" />
</div>
<div>
<p>Add Soybean's WeChat for business consultation, cooperation, project architecture, one-on-one guidance, etc.</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybean.jpg" style="width:200px" />
</div>
## Star Trend
[![Star History Chart](https://api.star-history.com/svg?repos=soybeanjs/soybean-admin&type=Date)](https://star-history.com/#soybeanjs/soybean-admin&Date)
## License
This project is based on the [MIT © 2021 Soybean](./LICENSE) protocol, for learning purposes only, please retain the author's copyright information for commercial use, the author does not guarantee and is not responsible for the software.
-179
View File
@@ -1,179 +0,0 @@
<div align="center">
<img src="./public/favicon.svg" width="160" />
<h1>SoybeanAdmin</h1>
<span><a href="./README.md">English</a> | 中文</span>
</div>
---
[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
[![github stars](https://img.shields.io/github/stars/honghuangdc/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
[![github forks](https://img.shields.io/github/forks/honghuangdc/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
[![gitee stars](https://gitee.com/honghuangdc/soybean-admin/badge/star.svg)](https://gitee.com/honghuangdc/soybean-admin)
<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
> [!NOTE]
> 如果您觉得 `SoybeanAdmin`对您有所帮助,或者您喜欢我们的项目,请在 GitHub 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持!
## 简介
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件,代码规范严谨,实现了自动化的文件路由系统。此外,它还采用了基于 ApiFox 的在线Mock数据方案。`SoybeanAdmin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
## 特性
- **前沿技术应用**:采用 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS 等最新流行的技术栈。
- **清晰的项目架构**:采用 pnpm monorepo 架构,结构清晰,优雅易懂。
- **严格的代码规范**:遵循 [SoybeanJS 规范](https://docs.soybeanjs.cn/zh/standard),集成了eslint, prettier 和 simple-git-hooks,保证代码的规范性。
- **TypeScript**: 支持严格的类型检查,提高代码的可维护性。
- **丰富的主题配置**:内置多样的主题配置,与 UnoCSS 完美结合。
- **内置国际化方案**:轻松实现多语言支持。
- **自动化文件路由系统**:自动生成路由导入、声明和类型。更多细节请查看 [Elegant Router](https://github.com/soybeanjs/elegant-router)。
- **灵活的权限路由**:同时支持前端静态路由和后端动态路由。
- **丰富的页面组件**:内置多样页面和组件,包括403、404、500页面,以及布局组件、标签组件、主题配置组件等。
- **命令行工具**:内置高效的命令行工具,git提交、删除文件、发布等。
- **移动端适配**:完美支持移动端,实现自适应布局。
## 版本
- **NaiveUI 版本:**
- [预览地址](https://naive.soybeanjs.cn/)
- [Github 仓库](https://github.com/soybeanjs/soybean-admin)
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin)
- **AntDesignVue 版本:**
- [预览地址](https://antd.soybeanjs.cn/)
- [Github 仓库](https://github.com/soybeanjs/soybean-admin-antd)
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin-antd)
- **旧版:**
- [预览地址](https://legacy.soybeanjs.cn/)
- [Github 仓库](https://github.com/soybeanjs/soybean-admin/tree/legacy)
## 文档
- [地址](https://docs.soybeanjs.cn)
- [旧版文档](https://legacy-docs.soybeanjs.cn)
## 示例图片
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-01.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-02.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-03.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-04.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-05.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-06.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-07.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-08.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-09.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-10.png)
![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-mobile.png)
## 使用
**环境准备**
确保你的环境满足以下要求:
- **git**: 你需要git来克隆和管理项目版本。
- **NodeJS**: >=18.12.0,推荐 18.19.0 或更高。
- **pnpm**: >= 8.7.0,推荐 8.14.0 或更高。
**克隆项目**
```bash
git clone https://github.com/soybeanjs/soybean-admin.git
```
**安装依赖**
```bash
pnpm i
```
> 由于本项目采用了 pnpm monorepo 的管理方式,因此请不要使用 npm 或 yarn 来安装依赖。
**启动项目**
```bash
pnpm dev
```
**构建项目**
```bash
pnpm build
```
## 周边生态
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。
- [pea](https://github.com/haitang1894/pea) : 采用SpringBoot3.2 + JDK21、MyBatis-Plus、SpringSecurity安全框架等,适配 [soybean-admin](https://gitee.com/honghuangdc/soybean-admin) 开发的简单权限系统。
- [MalusAdmin](https://github.com/pridejoy/MalusAdmin): 基于 Vue3/TypeScript/NaiveUI 和 NET7 & Sqlsugar 开发的后台管理框架。采用最原生最简洁的方式来实现, 前端清新优雅高颜值,后端 结构清晰,优雅易懂,功能强大。
- [PanisAdmin](https://github.com/paynezhuang/panis-admin): 采用SpringBoot3、SaToken、MySQL等框架开发,二次修改 [soybean-admin](https://github.com/soybeanjs/soybean-admin),适配动态菜单/按钮级别的鉴权,保留原汁原味、清新优雅、高颜值的后台管理系统脚手架。
- [snail-job](https://github.com/aizuda/snail-job): 一款兼具 “高性能、高颜值、高活跃” 的分布式任务重试和分布式任务调度平台。
- [SuperApi](https://github.com/TmmTop/SuperApi): 快速将你的 idea 变成线上稳定运行的产品! 无实体建库建表,对无实体库表进行增删改查,支持 15 种条件查询,以及分页,列表,无限级树形列表 等功能的 API 部署! 拥有接口文档,Auth 授权,接口限流,获取客户端真实 IP,先进的服务器缓存组件,动态 API 等功能,期待您的体验!
- [FastSoyAdmin](https://github.com/sleep1223/fast-soy-admin): 基于 FastAPI+Vue3+Naive UI 的现代化轻量管理平台.
## 如何贡献
我们热烈欢迎并感谢所有形式的贡献。如果您有任何想法或建议,欢迎通过提交 [pull requests](https://github.com/soybeanjs/soybean-admin/pulls) 或创建 GitHub [issue](https://github.com/soybeanjs/soybean-admin/issues/new) 来分享。
## Git 提交规范
本项目已内置 `commit` 命令,您可以通过执行 `pnpm commit` 来生成符合 [Conventional Commits]([conventionalcommits](https://www.conventionalcommits.org/)) 规范的提交信息。在提交PR时,请务必使用 `commit` 命令来创建提交信息,以确保信息的规范性。
## 浏览器支持
推荐使用最新版的 Chrome 浏览器进行开发,以获得更好的体验。
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) |
| --- | --- | --- | --- | --- |
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## 开源作者
[Soybean](https://github.com/honghuangdc)
## 贡献者
感谢以下贡献者的贡献。如果您想为本项目做出贡献,请参考 [如何贡献](#如何贡献)。
<a href="https://github.com/soybeanjs/soybean-admin/graphs/contributors">
<img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" />
</a>
## 交流
`SoybeanAdmin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。
<div>
<p>QQ交流群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-2.jpg" style="width:200px" />
</div>
<!-- <div>
<p>微信群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/picgo/soybean-admin-wechat-0620.jpg" style="width:200px" />
</div> -->
<div>
<p>添加下面微信邀请进微信群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybeanjs.jpg" style="width:200px" />
</div>
<div>
<p>添加 Soybean 的微信,业务咨询、合作、项目架构、一对一指导等</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybean.jpg" style="width:200px" />
</div>
## Star 趋势
[![Star History Chart](https://api.star-history.com/svg?repos=soybeanjs/soybean-admin&type=Date)](https://star-history.com/#soybeanjs/soybean-admin&Date)
## 开源协议
项目基于 [MIT © 2021 Soybean](./LICENSE) 协议,仅供学习参考,商业使用请保留作者版权信息,作者不保证也不承担任何软件的使用风险。
+1
View File
@@ -29,6 +29,7 @@ function createProxyItem(item: App.Service.ServiceConfigItem) {
proxy[item.proxyPattern] = {
target: item.baseURL,
changeOrigin: true,
ws: item.ws,
rewrite: path => path.replace(new RegExp(`^${item.proxyPattern}`), '')
};
+1 -1
View File
@@ -2,7 +2,7 @@
<html lang="zh-cmn-Hans">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.svg" />
<link rel="icon" href="/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<title>%VITE_APP_TITLE%</title>
+5 -18
View File
@@ -1,29 +1,16 @@
{
"name": "soybean-admin",
"name": "ruoyi-vue-plus",
"type": "module",
"version": "1.3.4",
"description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
"author": {
"name": "Soybean",
"email": "soybeanjs@outlook.com",
"url": "https://github.com/soybeanjs"
},
"license": "MIT",
"homepage": "https://github.com/soybeanjs/soybean-admin",
"repository": {
"url": "https://github.com/soybeanjs/soybean-admin.git"
},
"bugs": {
"url": "https://github.com/soybeanjs/soybean-admin/issues"
},
"description": "RuoYi-Vue-Plus多租户管理系统",
"keywords": [
"RuoYi-Vue-Plus",
"Vue3 admin ",
"vue-admin-template",
"Vite5",
"TypeScript",
"naive-ui",
"naive-ui-admin",
"ant-design-vue v4",
"UnoCSS"
],
"engines": {
@@ -58,6 +45,7 @@
"clipboard": "2.0.11",
"dayjs": "1.11.12",
"echarts": "5.5.1",
"jsencrypt": "^3.3.2",
"naive-ui": "2.39.0",
"nprogress": "0.2.0",
"pinia": "2.2.0",
@@ -105,6 +93,5 @@
},
"lint-staged": {
"*": "eslint --fix"
},
"website": "https://admin.soybeanjs.cn"
}
}
+8
View File
@@ -41,6 +41,9 @@ importers:
echarts:
specifier: 5.5.1
version: 5.5.1
jsencrypt:
specifier: ^3.3.2
version: 3.3.2
naive-ui:
specifier: 2.39.0
version: 2.39.0(vue@3.4.35(typescript@5.5.4))
@@ -2660,6 +2663,9 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
jsencrypt@3.3.2:
resolution: {integrity: sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A==}
jsesc@0.5.0:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
hasBin: true
@@ -6739,6 +6745,8 @@ snapshots:
dependencies:
argparse: 2.0.1
jsencrypt@3.3.2: {}
jsesc@0.5.0: {}
jsesc@2.5.2: {}
Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

-1
View File
@@ -1 +0,0 @@
<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg"><path d="M81.28 55.9c-.1-11.67-2.93-22.55-9.37-32.38-1-1.5-2.14-2.86-2.5-4.71a8.1 8.1 0 014-8.61 7.89 7.89 0 019.3 1.23 35.999 35.999 0 015.9 8.83 75.18 75.18 0 018.44 28.58 83.211 83.211 0 01-5.23 36.74 102.983 102.983 0 01-3 7.28 1.2 1.2 0 000 1.41c9.58 13.3 21.76 23 37.85 27.24a54.37 54.37 0 0019.68 1.57 7.72 7.72 0 018.36 6.9 7.903 7.903 0 01-6.7 9 64.744 64.744 0 01-23-1.33 77.68 77.68 0 01-36.93-19.88 93.628 93.628 0 01-11.91-13.71 2.18 2.18 0 00-2.3-1.06 72.744 72.744 0 00-27.38 7.55c-11.6 6-20.67 14.58-26.4 26.45a10.134 10.134 0 01-3.7 4.7 8 8 0 01-9.19-.7 7.86 7.86 0 01-2.36-9.28 60.324 60.324 0 018.72-14.52c12.2-15.43 28.21-24.59 47.32-28.57A85.085 85.085 0 0173.07 87c.524.015 1-.307 1.18-.8a76.06 76.06 0 006.53-22.3c.351-2.652.518-5.325.5-8z" fill="#646cff"/><path d="M136.26 108.34a44.742 44.742 0 01-11.13-2.87 46.108 46.108 0 01-19.66-13.76 8 8 0 015.72-13.22 7.93 7.93 0 016.54 2.93 33.27 33.27 0 0018.87 10.75c1.546.155 3.058.553 4.48 1.18a8.08 8.08 0 013.84 9.21c-.92 3.52-4.13 5.81-8.66 5.78zm-80.6-75.02a7.61 7.61 0 016.64 5 49.139 49.139 0 013.64 17 46.33 46.33 0 01-2.46 17.28c-2 5.77-8.24 7.79-12.89 4.15a8.1 8.1 0 01-2.39-9 31.679 31.679 0 001.68-12.36 35.77 35.77 0 00-2.43-11c-2.1-5.45 1.75-11.07 8.21-11.07zm22.26 93.25a8 8 0 01-6.68 7.86 32.88 32.88 0 00-19.7 12.19 8.13 8.13 0 01-11.21 1.62 8 8 0 01-1.41-11.58A51.043 51.043 0 0154 123.81a45.842 45.842 0 0114-5.1c5.35-1.04 9.91 2.56 9.92 7.86z" fill="#646cff"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

-1
View File
@@ -1 +0,0 @@
<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg"><path d="M81.28 55.9c-.1-11.67-2.93-22.55-9.37-32.38-1-1.5-2.14-2.86-2.5-4.71a8.1 8.1 0 014-8.61 7.89 7.89 0 019.3 1.23 35.999 35.999 0 015.9 8.83 75.18 75.18 0 018.44 28.58 83.211 83.211 0 01-5.23 36.74 102.983 102.983 0 01-3 7.28 1.2 1.2 0 000 1.41c9.58 13.3 21.76 23 37.85 27.24a54.37 54.37 0 0019.68 1.57 7.72 7.72 0 018.36 6.9 7.903 7.903 0 01-6.7 9 64.744 64.744 0 01-23-1.33 77.68 77.68 0 01-36.93-19.88 93.628 93.628 0 01-11.91-13.71 2.18 2.18 0 00-2.3-1.06 72.744 72.744 0 00-27.38 7.55c-11.6 6-20.67 14.58-26.4 26.45a10.134 10.134 0 01-3.7 4.7 8 8 0 01-9.19-.7 7.86 7.86 0 01-2.36-9.28 60.324 60.324 0 018.72-14.52c12.2-15.43 28.21-24.59 47.32-28.57A85.085 85.085 0 0173.07 87c.524.015 1-.307 1.18-.8a76.06 76.06 0 006.53-22.3c.351-2.652.518-5.325.5-8z" fill="currentColor"/><path d="M136.26 108.34a44.742 44.742 0 01-11.13-2.87 46.108 46.108 0 01-19.66-13.76 8 8 0 015.72-13.22 7.93 7.93 0 016.54 2.93 33.27 33.27 0 0018.87 10.75c1.546.155 3.058.553 4.48 1.18a8.08 8.08 0 013.84 9.21c-.92 3.52-4.13 5.81-8.66 5.78zm-80.6-75.02a7.61 7.61 0 016.64 5 49.139 49.139 0 013.64 17 46.33 46.33 0 01-2.46 17.28c-2 5.77-8.24 7.79-12.89 4.15a8.1 8.1 0 01-2.39-9 31.679 31.679 0 001.68-12.36 35.77 35.77 0 00-2.43-11c-2.1-5.45 1.75-11.07 8.21-11.07zm22.26 93.25a8 8 0 01-6.68 7.86 32.88 32.88 0 00-19.7 12.19 8.13 8.13 0 01-11.21 1.62 8 8 0 01-1.41-11.58A51.043 51.043 0 0154 123.81a45.842 45.842 0 0114-5.1c5.35-1.04 9.91 2.56 9.92 7.86z" fill="currentColor"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

+1 -1
View File
@@ -3,7 +3,7 @@ defineOptions({ name: 'SystemLogo' });
</script>
<template>
<icon-local-logo />
<img src="@/assets/imgs/logo.png" />
</template>
<style scoped></style>
+2 -2
View File
@@ -9,10 +9,10 @@ export function useAuth() {
}
if (typeof codes === 'string') {
return authStore.userInfo.buttons.includes(codes);
return authStore.userInfo.permissions.includes(codes);
}
return codes.some(code => authStore.userInfo.buttons.includes(code));
return codes.some(code => authStore.userInfo.permissions.includes(code));
}
return {
+2 -1
View File
@@ -38,7 +38,8 @@ export function useFormRules() {
phone: [createRequiredRule($t('form.phone.required')), patternRules.phone],
pwd: [createRequiredRule($t('form.pwd.required')), patternRules.pwd],
code: [createRequiredRule($t('form.code.required')), patternRules.code],
email: [createRequiredRule($t('form.email.required')), patternRules.email]
email: [createRequiredRule($t('form.email.required')), patternRules.email],
tenantId: [createRequiredRule('请选择/输入公司名称')]
} satisfies Record<string, App.Global.FormRule[]>;
/** the default required rule */
+3 -3
View File
@@ -38,7 +38,7 @@ export function useTable<A extends NaiveUI.TableApiFn>(config: NaiveUI.NaiveTabl
apiParams,
columns: config.columns,
transformer: res => {
const { records = [], current = 1, size = 10, total = 0 } = res.data || {};
const { rows: records = [], pageNum: current = 1, pageSize: size = 10, total = 0 } = res.data || {};
// Ensure that the size is greater than 0, If it is less than 0, it will cause paging calculation errors.
const pageSize = size <= 0 ? 10 : size;
@@ -225,9 +225,9 @@ export function useTableOperate<T extends TableData = TableData>(data: Ref<T[]>,
/** the editing row data */
const editingData: Ref<T | null> = ref(null);
function handleEdit(id: T['id']) {
function handleEdit(field: keyof T, id: string) {
operateType.value = 'edit';
const findItem = data.value.find(item => item.id === id) || null;
const findItem = data.value.find(item => item[field] === id) || null;
editingData.value = jsonClone(findItem);
openDrawer();
+7 -1
View File
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { computed, defineAsyncComponent } from 'vue';
import { computed, defineAsyncComponent, onMounted } from 'vue';
import { AdminLayout, LAYOUT_SCROLL_EL_ID } from '@sa/materials';
import type { LayoutMode } from '@sa/materials';
import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
import { initWebSocket } from '@/utils/websocket';
import GlobalHeader from '../modules/global-header/index.vue';
import GlobalSider from '../modules/global-sider/index.vue';
import GlobalTab from '../modules/global-tab/index.vue';
@@ -100,6 +101,11 @@ function getSiderCollapsedWidth() {
return w;
}
onMounted(() => {
const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
initWebSocket(`${protocol + window.location.host + import.meta.env.VITE_APP_BASE_API}/resource/websocket`);
});
</script>
<template>
@@ -73,7 +73,7 @@ function handleDropdown(key: DropdownKey) {
<div>
<ButtonIcon>
<SvgIcon icon="ph:user-circle" class="text-icon-large" />
<span class="text-16px font-medium">{{ authStore.userInfo.userName }}</span>
<span class="text-16px font-medium">{{ authStore.userInfo.user?.userName }}</span>
</ButtonIcon>
</div>
</NDropdown>
+1 -1
View File
@@ -17,7 +17,7 @@ withDefaults(defineProps<Props>(), {
<template>
<RouterLink to="/" class="w-full flex-center nowrap-hidden">
<SystemLogo class="text-32px text-primary" />
<SystemLogo class="w-32px text-primary" />
<h2 v-show="showTitle" class="pl-8px text-16px text-primary font-bold transition duration-300 ease-in-out">
{{ $t('system.title') }}
</h2>
+1 -1
View File
@@ -1,6 +1,6 @@
const local: App.I18n.Schema = {
system: {
title: 'SoybeanAdmin',
title: 'RuoYi Vue Plus',
updateTitle: 'System Version Update Notification',
updateContent: 'A new version of the system has been detected. Do you want to refresh the page immediately?',
updateConfirm: 'Refresh immediately',
+1 -1
View File
@@ -1,6 +1,6 @@
const local: App.I18n.Schema = {
system: {
title: 'Soybean 管理系统',
title: 'RuoYi Vue Plus',
updateTitle: '系统版本更新通知',
updateContent: '检测到系统有新版本发布,是否立即刷新页面?',
updateConfirm: '立即刷新',
+4 -20
View File
@@ -2,36 +2,20 @@
import { getRgb } from '@sa/color';
import { $t } from '@/locales';
import { localStg } from '@/utils/storage';
import systemLogo from '@/assets/svg-icon/logo.svg?raw';
import SystemLogo from '@/assets/imgs/logo.png';
export function setupLoading() {
const themeColor = localStg.get('themeColor') || '#646cff';
const themeColor = localStg.get('themeColor') || '#2080f0';
const { r, g, b } = getRgb(themeColor);
const primaryColor = `--primary-color: ${r} ${g} ${b}`;
const loadingClasses = [
'left-0 top-0',
'left-0 bottom-0 animate-delay-500',
'right-0 top-0 animate-delay-1000',
'right-0 bottom-0 animate-delay-1500'
];
const logoWithClass = systemLogo.replace('<svg', `<svg class="size-128px text-primary"`);
const dot = loadingClasses
.map(item => {
return `<div class="absolute w-16px h-16px bg-primary rounded-8px animate-pulse ${item}"></div>`;
})
.join('\n');
const loading = `
<div class="fixed-center flex-col" style="${primaryColor}">
${logoWithClass}
<div class="w-56px h-56px my-36px">
<div class="w-120px h-120px my-36px">
<div class="relative h-full animate-spin">
${dot}
<img src="${SystemLogo}" width="120" />
</div>
</div>
<h2 class="text-28px font-500 text-#646464">${$t('system.title')}</h2>
+93
View File
@@ -30,6 +30,99 @@ export function createStaticRoutes() {
};
}
const dynamicConstantRoutes: ElegantRoute[] = [
{
name: 'home',
path: '/home',
component: 'layout.base$view.home',
meta: {
title: 'home',
i18nKey: 'route.home',
icon: 'mdi:monitor-dashboard',
order: 1
}
},
{
name: '403',
path: '/403',
component: 'layout.blank$view.403',
meta: {
title: '403',
i18nKey: 'route.403',
constant: true,
hideInMenu: true
}
},
{
name: '404',
path: '/404',
component: 'layout.blank$view.404',
meta: {
title: '404',
i18nKey: 'route.404',
constant: true,
hideInMenu: true
}
},
{
name: '500',
path: '/500',
component: 'layout.blank$view.500',
meta: {
title: '500',
i18nKey: 'route.500',
constant: true,
hideInMenu: true
}
},
{
name: 'login',
path: '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?',
component: 'layout.blank$view.login',
props: true,
meta: {
title: 'login',
i18nKey: 'route.login',
constant: true,
hideInMenu: true
}
},
{
name: 'iframe-page',
path: '/iframe-page/:url',
component: 'layout.base$view.iframe-page',
props: true,
meta: {
title: 'iframe-page',
i18nKey: 'route.iframe-page',
constant: true,
hideInMenu: true,
keepAlive: true,
icon: 'material-symbols:iframe-outline'
}
}
];
/** create routes when the auth route mode is static */
export function createDynamicRoutes() {
const constantRoutes: ElegantConstRoute[] = [];
const authRoutes: ElegantConstRoute[] = [];
[...customRoutes, ...dynamicConstantRoutes].forEach(item => {
if (item.meta?.constant) {
constantRoutes.push(item);
} else {
authRoutes.push(item);
}
});
return {
constantRoutes,
authRoutes
};
}
/**
* Get auth vue routes
*
+25 -32
View File
@@ -1,48 +1,41 @@
import { request } from '../request';
/** Get tenant list */
export function fetchTenantList() {
return request<Api.Auth.TenantList>({
url: '/auth/tenant/list',
method: 'get'
});
}
/** Get image code */
export function fetchCaptchaCode() {
return request<Api.Auth.CaptchaCode>({
url: '/auth/code',
method: 'get'
});
}
/**
* Login
*
* @param userName User name
* @param username User name
* @param password Password
*/
export function fetchLogin(userName: string, password: string) {
export function fetchLogin(data: Api.Auth.LoginData) {
return request<Api.Auth.LoginToken>({
url: '/auth/login',
method: 'post',
data: {
userName,
password
}
headers: {
isToken: false,
isEncrypt: true,
repeatSubmit: false
},
data
});
}
/** Get user info */
export function fetchGetUserInfo() {
return request<Api.Auth.UserInfo>({ url: '/auth/getUserInfo' });
}
/**
* Refresh token
*
* @param refreshToken Refresh token
*/
export function fetchRefreshToken(refreshToken: string) {
return request<Api.Auth.LoginToken>({
url: '/auth/refreshToken',
method: 'post',
data: {
refreshToken
}
});
}
/**
* return custom backend error
*
* @param code error code
* @param msg error message
*/
export function fetchCustomBackendError(code: string, msg: string) {
return request({ url: '/auth/error', params: { code, msg } });
return request<Api.Auth.UserInfo>({ url: '/system/user/getInfo' });
}
+4 -17
View File
@@ -1,20 +1,7 @@
import type { ElegantConstRoute } from '@elegant-router/types';
import { request } from '../request';
/** get constant routes */
export function fetchGetConstantRoutes() {
return request<Api.Route.MenuRoute[]>({ url: '/route/getConstantRoutes' });
}
/** get user routes */
export function fetchGetUserRoutes() {
return request<Api.Route.UserRoute>({ url: '/route/getUserRoutes' });
}
/**
* whether the route is exist
*
* @param routeName route name
*/
export function fetchIsRouteExist(routeName: string) {
return request<boolean>({ url: '/route/isRouteExist', params: { routeName } });
/** get routes */
export function fetchGetRoutes() {
return request<ElegantConstRoute[]>({ url: '/system/menu/getRouters' });
}
+10
View File
@@ -0,0 +1,10 @@
import { request } from '@/service/request';
/** get menu list */
export function fetchGetMenuList(params?: Api.System.MenuSearchParams) {
return request<Api.System.MenuList>({
url: '/system/menu/list',
method: 'get',
params
});
}
+90 -41
View File
@@ -1,30 +1,50 @@
import type { AxiosResponse } from 'axios';
import { BACKEND_ERROR_CODE, createFlatRequest, createRequest } from '@sa/axios';
import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { BACKEND_ERROR_CODE, createFlatRequest } from '@sa/axios';
import { useAuthStore } from '@/store/modules/auth';
import { $t } from '@/locales';
import { localStg } from '@/utils/storage';
import { localStg, sessionStg } from '@/utils/storage';
import { getServiceBaseURL } from '@/utils/service';
import { decryptBase64, decryptWithAes, encryptBase64, encryptWithAes, generateAesKey } from '@/utils/crypto';
import { decrypt, encrypt } from '@/utils/jsencrypt';
import { handleRefreshToken, showErrorMsg } from './shared';
import type { RequestInstanceState } from './type';
const encryptHeader = 'encrypt-key';
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
const { baseURL, otherBaseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
export const request = createFlatRequest<App.Service.Response, RequestInstanceState>(
{
baseURL,
headers: {
apifoxToken: 'XL299LiMEDZ0H5h3A29PxwQXdMJqWyY2'
'axios-retry': {
retries: 0
}
},
{
async onRequest(config) {
const { headers } = config;
// 对应国际化资源文件后缀
config.headers['Content-Language'] = 'zh_CN';
const isToken = config.headers?.isToken === false;
// set token
const token = localStg.get('token');
if (token && !isToken) {
config.headers.Clientid = import.meta.env.VITE_APP_CLIENT_ID;
const Authorization = token ? `Bearer ${token}` : null;
Object.assign(headers, { Authorization });
}
handleRepeatSubmit(config);
handleEncrypt(config);
// FormData数据去请求头Content-Type
if (config.data instanceof FormData) {
delete config.headers['Content-Type'];
}
return config;
},
@@ -98,6 +118,31 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
return null;
},
transformBackendResponse(response) {
if (import.meta.env.VITE_APP_ENCRYPT === 'true') {
// 加密后的 AES 秘钥
const keyStr = response.headers[encryptHeader];
// 加密
if (keyStr && keyStr !== '') {
const data = String(response.data);
// 请求体 AES 解密
const base64Str = decrypt(keyStr);
// base64 解码 得到请求头的 AES 秘钥
const aesKey = decryptBase64(base64Str.toString());
// aesKey 解码 data
const decryptData = decryptWithAes(data, aesKey);
// 将结果 (得到的是 JSON 字符串) 转为 JSON
response.data = JSON.parse(decryptData);
}
}
// 二进制数据则直接返回
if (response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer') {
return response.data;
}
if (response.data.rows) {
return response.data;
}
return response.data.data;
},
onError(error) {
@@ -129,44 +174,48 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
}
);
export const demoRequest = createRequest<App.Service.DemoResponse>(
{
baseURL: otherBaseURL.demo
},
{
async onRequest(config) {
const { headers } = config;
function handleRepeatSubmit(config: InternalAxiosRequestConfig) {
// 是否需要防止数据重复提交
const isRepeatSubmit = config.headers?.repeatSubmit === false;
// set token
const token = localStg.get('token');
const Authorization = token ? `Bearer ${token}` : null;
Object.assign(headers, { Authorization });
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url!,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
};
const sessionObj = sessionStg.get('sessionObj');
if (!sessionObj) {
sessionStg.set('sessionObj', requestObj);
} else {
const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 500; // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ${message}`);
throw new Error(message);
}
sessionStg.set('sessionObj', requestObj);
}
}
}
return config;
},
isBackendSuccess(response) {
// when the backend response code is "200", it means the request is success
// you can change this logic by yourself
return response.data.status === '200';
},
async onBackendFail(_response) {
// when the backend response code is not "200", it means the request is fail
// for example: the token is expired, refresh token and retry request
},
transformBackendResponse(response) {
return response.data.result;
},
onError(error) {
// when the request is fail, you can show error message
function handleEncrypt(config: InternalAxiosRequestConfig) {
// 是否需要加密
const isEncrypt = config.headers?.isEncrypt === 'true';
let message = error.message;
// show backend error message
if (error.code === BACKEND_ERROR_CODE) {
message = error.response?.data?.message || message;
if (import.meta.env.VITE_APP_ENCRYPT === 'true') {
// 当开启参数加密
if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
// 生成一个 AES 密钥
const aesKey = generateAesKey();
config.headers[encryptHeader] = encrypt(encryptBase64(aesKey));
config.data =
typeof config.data === 'object'
? encryptWithAes(JSON.stringify(config.data), aesKey)
: encryptWithAes(config.data, aesKey);
}
window.$message?.error(message);
}
}
);
+4 -16
View File
@@ -1,7 +1,6 @@
import type { AxiosRequestConfig } from 'axios';
import { useAuthStore } from '@/store/modules/auth';
import { localStg } from '@/utils/storage';
import { fetchRefreshToken } from '../api';
// import { localStg } from '@/utils/storage';
import type { RequestInstanceState } from './type';
/**
@@ -9,22 +8,11 @@ import type { RequestInstanceState } from './type';
*
* @param axiosConfig - request config when the token is expired
*/
export async function handleRefreshToken(axiosConfig: AxiosRequestConfig) {
export async function handleRefreshToken(_: AxiosRequestConfig) {
const { resetStore } = useAuthStore();
const refreshToken = localStg.get('refreshToken') || '';
const { error, data } = await fetchRefreshToken(refreshToken);
if (!error) {
localStg.set('token', data.token);
localStg.set('refreshToken', data.refreshToken);
const config = { ...axiosConfig };
if (config.headers) {
config.headers.Authorization = data.token;
}
return config;
}
// request
// const refreshToken = localStg.get('refreshToken') || '';
resetStore();
+23 -16
View File
@@ -6,7 +6,7 @@ import { SetupStoreId } from '@/enum';
import { useRouterPush } from '@/hooks/common/router';
import { fetchGetUserInfo, fetchLogin } from '@/service/api';
import { localStg } from '@/utils/storage';
import { $t } from '@/locales';
// import { $t } from '@/locales';
import { useRouteStore } from '../route';
import { useTabStore } from '../tab';
import { clearAuthStorage, getToken } from './shared';
@@ -21,10 +21,9 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
const token = ref(getToken());
const userInfo: Api.Auth.UserInfo = reactive({
userId: '',
userName: '',
user: undefined,
roles: [],
buttons: []
permissions: []
});
/** is super role in static route */
@@ -56,14 +55,20 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
/**
* Login
*
* @param userName User name
* @param password Password
* @param [redirect=true] Whether to redirect after login. Default is `true`
*/
async function login(userName: string, password: string, redirect = true) {
async function login(loginForm: Api.Auth.LoginForm, redirect = true) {
startLoading();
const { data: loginToken, error } = await fetchLogin(userName, password);
const { VITE_APP_CLIENT_ID } = import.meta.env;
const loginData: Api.Auth.LoginData = {
...loginForm,
clientId: VITE_APP_CLIENT_ID!,
grantType: 'password'
};
const { data: loginToken, error } = await fetchLogin(loginData);
if (!error) {
const pass = await loginByToken(loginToken);
@@ -76,11 +81,11 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
}
if (routeStore.isInitAuthRoute) {
window.$notification?.success({
title: $t('page.login.common.loginSuccess'),
content: $t('page.login.common.welcomeBack', { userName: userInfo.userName }),
duration: 4500
});
// window.$notification?.success({
// title: $t('page.login.common.loginSuccess'),
// content: $t('page.login.common.welcomeBack', { userName: userInfo.userName }),
// duration: 4500
// });
}
}
} else {
@@ -88,18 +93,20 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
}
endLoading();
return error ? Promise.reject(error) : Promise.resolve();
}
async function loginByToken(loginToken: Api.Auth.LoginToken) {
// 1. stored in the localStorage, the later requests need it in headers
localStg.set('token', loginToken.token);
localStg.set('refreshToken', loginToken.refreshToken);
localStg.set('token', loginToken.access_token);
localStg.set('refreshToken', loginToken.refresh_token);
// 2. get user info
const pass = await getUserInfo();
if (pass) {
token.value = loginToken.token;
token.value = loginToken.access_token;
return true;
}
+43
View File
@@ -0,0 +1,43 @@
import { defineStore } from 'pinia';
import { reactive } from 'vue';
interface NoticeItem {
title?: string;
read: boolean;
message: any;
time: string;
}
export const useNoticeStore = defineStore('notice', () => {
const state = reactive({
notices: [] as NoticeItem[]
});
const addNotice = (notice: NoticeItem) => {
state.notices.push(notice);
};
const removeNotice = (notice: NoticeItem) => {
state.notices.splice(state.notices.indexOf(notice), 1);
};
// 实现全部已读
const readAll = () => {
state.notices.forEach((item: any) => {
item.read = true;
});
};
const clearNotice = () => {
state.notices = [];
};
return {
state,
addNotice,
removeNotice,
readAll,
clearNotice
};
});
export default useNoticeStore;
+17 -21
View File
@@ -5,10 +5,10 @@ import { useBoolean } from '@sa/hooks';
import type { CustomRoute, ElegantConstRoute, LastLevelRouteKey, RouteKey, RouteMap } from '@elegant-router/types';
import { SetupStoreId } from '@/enum';
import { router } from '@/router';
import { createStaticRoutes, getAuthVueRoutes } from '@/router/routes';
import { createDynamicRoutes, createStaticRoutes, getAuthVueRoutes } from '@/router/routes';
import { ROOT_ROUTE } from '@/router/routes/builtin';
import { getRouteName, getRoutePath } from '@/router/elegant/transform';
import { fetchGetConstantRoutes, fetchGetUserRoutes, fetchIsRouteExist } from '@/service/api';
import { fetchGetRoutes } from '@/service/api';
import { useAppStore } from '../app';
import { useAuthStore } from '../auth';
import { useTabStore } from '../tab';
@@ -75,6 +75,17 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
authRoutesMap.set(route.name, route);
});
const dynamicRoutes = createDynamicRoutes();
dynamicRoutes.authRoutes.forEach(route => {
const parent = authRoutesMap.get(route.name);
if (parent && route.children) {
parent.children?.push(...route.children);
return;
}
authRoutesMap.set(route.name, route);
});
authRoutes.value = Array.from(authRoutesMap.values());
}
@@ -201,15 +212,8 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
if (authRouteMode.value === 'static') {
addConstantRoutes(staticRoute.constantRoutes);
} else {
const { data, error } = await fetchGetConstantRoutes();
if (!error) {
addConstantRoutes(data);
} else {
// if fetch constant routes failed, use static constant routes
addConstantRoutes(staticRoute.constantRoutes);
}
}
handleConstantAndAuthRoutes();
@@ -246,18 +250,16 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
/** Init dynamic auth route */
async function initDynamicAuthRoute() {
const { data, error } = await fetchGetUserRoutes();
const { data, error } = await fetchGetRoutes();
if (!error) {
const { routes, home } = data;
addAuthRoutes(routes);
addAuthRoutes(data);
handleConstantAndAuthRoutes();
setRouteHome(home);
setRouteHome('home');
handleUpdateRootRouteRedirect(home);
handleUpdateRootRouteRedirect('home');
setIsInitAuthRoute(true);
} else {
@@ -335,16 +337,10 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
return false;
}
if (authRouteMode.value === 'static') {
const { authRoutes: staticAuthRoutes } = createStaticRoutes();
return isRouteExistByRouteName(routeName, staticAuthRoutes);
}
const { data } = await fetchIsRouteExist(routeName);
return data;
}
/**
* Get selected menu key path
*
+1 -1
View File
@@ -4,7 +4,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
grayscale: false,
colourWeakness: false,
recommendColor: false,
themeColor: '#646cff',
themeColor: '#2080f0',
otherColor: {
info: '#2080f0',
success: '#52c41a',
+50 -15
View File
@@ -8,40 +8,38 @@ declare namespace Api {
/** common params of paginating */
interface PaginatingCommonParams {
/** current page number */
current: number;
pageNum: number;
/** page size */
size: number;
pageSize: number;
/** total count */
total: number;
}
/** common params of paginating query list data */
interface PaginatingQueryRecord<T = any> extends PaginatingCommonParams {
records: T[];
rows: T[];
}
/**
* enable status
*
* - "1": enabled
* - "2": disabled
* - "0": enabled
* - "1": disabled
*/
type EnableStatus = '1' | '2';
type EnableStatus = '0' | '1';
/** common record */
type CommonRecord<T = any> = {
/** record id */
id: number;
/** record creator */
createBy: string;
/** record dept */
createDept?: any;
/** record create time */
createTime: string;
/** record updater */
updateBy: string;
/** record update time */
updateTime: string;
/** record status */
status: EnableStatus | null;
} & T;
}
@@ -51,16 +49,53 @@ declare namespace Api {
* backend api module: "auth"
*/
namespace Auth {
interface LoginData {
tenantId?: string;
username?: string;
password?: string;
rememberMe?: boolean;
socialCode?: string;
socialState?: string;
source?: string;
code?: string;
uuid?: string;
clientId: string;
grantType: string;
}
type LoginForm = Pick<LoginData, 'tenantId' | 'username' | 'password' | 'rememberMe' | 'code' | 'uuid'>;
interface LoginToken {
token: string;
refreshToken: string;
access_token: string;
client_id: string;
expire_in: number;
openid: string;
refresh_expire_in: number;
refresh_token: string;
scope: string;
}
interface UserInfo {
userId: string;
userName: string;
user?: Api.System.User;
roles: string[];
buttons: string[];
permissions: string[];
}
interface Tenant {
companyName: string;
domain: string;
tenantId: string;
}
interface TenantList {
tenantEnabled: boolean;
voList: Tenant[];
}
interface CaptchaCode {
captchaEnabled: boolean;
uuid?: string;
img?: string;
}
}
+123
View File
@@ -0,0 +1,123 @@
/**
* Namespace Api
*
* All backend api type
*/
declare namespace Api {
/**
* namespace System
*
* backend api module: "system"
*/
namespace System {
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'pageNum' | 'pageSize'>;
/** role */
type Role = Common.CommonRecord<{
roleId: string | number;
roleName: string;
roleKey: string;
roleSort: number;
dataScope: string;
menuCheckStrictly: boolean;
deptCheckStrictly: boolean;
status: string;
delFlag: string;
remark?: any;
flag: boolean;
menuIds?: Array<string | number>;
deptIds?: Array<string | number>;
admin: boolean;
}>;
/** role search params */
type RoleSearchParams = CommonType.RecordNullable<
Pick<Role, 'roleName' | 'roleKey' | 'status'> & CommonSearchParams
>;
/** role list */
type RoleList = Common.PaginatingQueryRecord<Role>;
/** all role */
type AllRole = Pick<Role, 'roleId' | 'roleName' | 'roleKey'>;
/**
* user gender
*
* - "1": "male"
* - "2": "female"
*/
type UserGender = '1' | '2';
/** user */
type User = Common.CommonRecord<{
userId: string | number;
deptId: number;
userName: string;
nickName: string;
userType: string;
email: string;
phonenumber: string;
sex: string;
avatar: string;
status: string;
delFlag: string;
loginIp: string;
loginDate: string;
remark: string;
deptName: string;
roles: Role[];
roleIds: any;
postIds: any;
roleId: any;
admin: boolean;
}>;
/** user search params */
type UserSearchParams = CommonType.RecordNullable<
Pick<User, 'userName' | 'sex' | 'nickName' | 'phonenumber' | 'email' | 'status'> & CommonSearchParams
>;
/** user list */
type UserList = Common.PaginatingQueryRecord<User>;
/**
* menu type
*
* - "M": "目录"
* - "C": "菜单"
* - "F": "按钮"
*/
type MenuType = 'M' | 'C' | 'F';
type Menu = Common.CommonRecord<
{
parentName: string;
parentId: string | number;
children: Menu[];
menuId: string | number;
menuName: string;
orderNum: number;
path: string;
component: string;
queryParam: string;
isFrame: string;
isCache: string;
menuType: MenuType;
visible: string;
status: Common.EnableStatus;
perms: string;
icon: string;
componentInfo: string;
remark: string;
keywords?: string;
} & Pick<import('vue-router').RouteMeta, 'i18nKey'>
>;
/** menu list */
type MenuList = Common.PaginatingQueryRecord<Menu>;
type MenuSearchParams = CommonType.RecordNullable<Pick<Api.System.Menu, 'menuName' | 'status' | 'keywords'>> &
CommonSearchParams;
}
}
+3
View File
@@ -532,6 +532,7 @@ declare namespace App {
baseURL: string;
/** The proxy pattern of the backend service base url */
proxyPattern: string;
ws?: boolean;
}
interface OtherServiceConfigItem extends ServiceConfigItem {
@@ -556,6 +557,8 @@ declare namespace App {
msg: string;
/** The backend service response data */
data: T;
rows?: any[];
total?: number;
};
/** The demo backend service response data */
+1
View File
@@ -76,6 +76,7 @@ declare module 'vue' {
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect']
NSpace: typeof import('naive-ui')['NSpace']
NSpin: typeof import('naive-ui')['NSpin']
NStatistic: typeof import('naive-ui')['NStatistic']
NSwitch: typeof import('naive-ui')['NSwitch']
NTab: typeof import('naive-ui')['NTab']
+5
View File
@@ -103,6 +103,11 @@ declare namespace Env {
readonly VITE_ICONIFY_URL?: string;
/** Used to differentiate storage across different domains */
readonly VITE_STORAGE_PREFIX?: string;
readonly VITE_APP_CLIENT_ID?: string;
readonly VITE_APP_ENCRYPT?: string;
readonly VITE_APP_RSA_PUBLIC_KEY?: string;
readonly VITE_APP_RSA_PRIVATE_KEY?: string;
readonly VITE_APP_WEBSOCKET: string;
}
}
+5
View File
@@ -7,6 +7,11 @@ declare namespace StorageType {
// * the theme settings
// */
// themeSettings: App.Theme.ThemeSetting;
sessionObj: {
url: string;
data: any;
time: number;
};
}
interface Local {
+70
View File
@@ -0,0 +1,70 @@
import CryptoJS from 'crypto-js';
/**
* 随机生成32位的字符串
*
* @returns {string}
*/
const generateRandomString = () => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const charactersLength = characters.length;
// eslint-disable-next-line no-plusplus
for (let i = 0; i < 32; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
};
/**
* 随机生成aes 密钥
*
* @returns {string}
*/
export const generateAesKey = () => {
return CryptoJS.enc.Utf8.parse(generateRandomString());
};
/**
* 加密base64
*
* @returns {string}
*/
export const encryptBase64 = (str: CryptoJS.lib.WordArray) => {
return CryptoJS.enc.Base64.stringify(str);
};
/** 解密base64 */
export const decryptBase64 = (str: string) => {
return CryptoJS.enc.Base64.parse(str);
};
/**
* 使用密钥对数据进行加密
*
* @param message
* @param aesKey
* @returns {string}
*/
export const encryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray) => {
const encrypted = CryptoJS.AES.encrypt(message, aesKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
};
/**
* 使用密钥对数据进行解密
*
* @param message
* @param aesKey
* @returns {string}
*/
export const decryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray) => {
const decrypted = CryptoJS.AES.decrypt(message, aesKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8);
};
+21
View File
@@ -0,0 +1,21 @@
import JSEncrypt from 'jsencrypt';
// 密钥对生成 http://web.chacuo.net/netrsakeypair
const publicKey = import.meta.env.VITE_APP_RSA_PUBLIC_KEY;
// 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大
const privateKey = import.meta.env.VITE_APP_RSA_PRIVATE_KEY;
// 加密
export const encrypt = (txt: string) => {
const encryptor = new JSEncrypt();
encryptor.setPublicKey(publicKey!); // 设置公钥
return encryptor.encrypt(txt); // 对数据进行加密
};
// 解密
export const decrypt = (txt: string) => {
const encryptor = new JSEncrypt();
encryptor.setPrivateKey(privateKey!); // 设置私钥
return encryptor.decrypt(txt); // 对数据进行解密
};
+8 -10
View File
@@ -4,11 +4,11 @@
* @param env The current env
*/
export function createServiceConfig(env: Env.ImportMeta) {
const { VITE_SERVICE_BASE_URL, VITE_OTHER_SERVICE_BASE_URL } = env;
const { VITE_SERVICE_BASE_URL, VITE_OTHER_SERVICE_BASE_URL, VITE_APP_BASE_API, VITE_APP_WEBSOCKET } = env;
let other = {} as Record<App.Service.OtherBaseURLKey, string>;
try {
other = JSON.parse(VITE_OTHER_SERVICE_BASE_URL);
other = JSON.parse(VITE_OTHER_SERVICE_BASE_URL || '{}');
} catch (error) {
// eslint-disable-next-line no-console
console.error('VITE_OTHER_SERVICE_BASE_URL is not a valid JSON string');
@@ -24,6 +24,7 @@ export function createServiceConfig(env: Env.ImportMeta) {
const otherConfig: App.Service.OtherServiceConfigItem[] = otherHttpKeys.map(key => {
return {
key,
ws: false,
baseURL: httpConfig.other[key],
proxyPattern: createProxyPattern(key)
};
@@ -31,7 +32,8 @@ export function createServiceConfig(env: Env.ImportMeta) {
const config: App.Service.ServiceConfig = {
baseURL: httpConfig.baseURL,
proxyPattern: createProxyPattern(),
ws: VITE_APP_WEBSOCKET === 'true',
proxyPattern: VITE_APP_BASE_API,
other: otherConfig
};
@@ -45,7 +47,7 @@ export function createServiceConfig(env: Env.ImportMeta) {
* @param isProxy - if use proxy
*/
export function getServiceBaseURL(env: Env.ImportMeta, isProxy: boolean) {
const { baseURL, other } = createServiceConfig(env);
const { baseURL, other, proxyPattern } = createServiceConfig(env);
const otherBaseURL = {} as Record<App.Service.OtherBaseURLKey, string>;
@@ -54,7 +56,7 @@ export function getServiceBaseURL(env: Env.ImportMeta, isProxy: boolean) {
});
return {
baseURL: isProxy ? createProxyPattern() : baseURL,
baseURL: isProxy ? proxyPattern : baseURL,
otherBaseURL
};
}
@@ -64,10 +66,6 @@ export function getServiceBaseURL(env: Env.ImportMeta, isProxy: boolean) {
*
* @param key If not set, will use the default key
*/
function createProxyPattern(key?: App.Service.OtherBaseURLKey) {
if (!key) {
return '/proxy-default';
}
function createProxyPattern(key: App.Service.OtherBaseURLKey) {
return `/proxy-${key}`;
}
+139
View File
@@ -0,0 +1,139 @@
/**
* socket 通信
*
* @module initWebSocket 初始化
* @module websocketonopen 连接成功
* @module websocketonerror 连接失败
* @module websocketclose 断开连接
* @module resetHeart 重置心跳
* @module sendSocketHeart 心跳发送
* @module reconnect 重连
* @module sendMsg 发送数据
* @module websocketonmessage 接收数据
* @module test 测试收到消息传递
* @param {any} url socket地址
* @param {any} websocket websocket 实例
* @param {any} heartTime 心跳定时器实例
* @param {number} socketHeart 心跳次数
* @param {number} HeartTimeOut 心跳超时时间
* @param {number} socketError 错误次数
*/
import useNoticeStore from '@/store/modules/notice';
import { localStg } from './storage';
let socketUrl: any = ''; // socket地址
let websocket: any = null; // websocket 实例
let heartTime: any = null; // 心跳定时器实例
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let socketHeart = 0; // 心跳次数
const HeartTimeOut = 10000; // 心跳超时时间 10000 = 10s
let socketError = 0; // 错误次数
// 初始化socket
export function initWebSocket(url: any) {
if (import.meta.env.VITE_APP_WEBSOCKET === 'false') {
return null;
}
socketUrl = url;
// 初始化 websocket
const token = localStg.get('token');
websocket = new WebSocket(`${url}?Authorization=Bearer ${token}&clientid=${import.meta.env.VITE_APP_CLIENT_ID}`);
websocketonopen();
websocketonmessage();
websocketonerror();
websocketclose();
sendSocketHeart();
return websocket;
}
// socket 连接成功
export function websocketonopen() {
websocket.onopen = () => {
console.log('连接 websocket 成功');
resetHeart();
};
}
// socket 连接失败
export function websocketonerror() {
websocket.onerror = (e: any) => {
console.log('连接 websocket 失败', e);
};
}
// socket 断开链接
export function websocketclose() {
websocket.onclose = (e: any) => {
console.log('断开连接', e);
};
}
// socket 重置心跳
export function resetHeart() {
socketHeart = 0;
socketError = 0;
clearInterval(heartTime);
sendSocketHeart();
}
// socket心跳发送
export function sendSocketHeart() {
heartTime = setInterval(() => {
// 如果连接正常则发送心跳
if (websocket.readyState === 1) {
// if (socketHeart <= 30) {
websocket.send(
JSON.stringify({
type: 'ping'
})
);
socketHeart += 1;
} else {
// 重连
reconnect();
}
}, HeartTimeOut);
}
// socket重连
export function reconnect() {
if (socketError <= 2) {
clearInterval(heartTime);
initWebSocket(socketUrl);
socketError += 1;
console.log('socket重连', socketError);
} else {
console.log('重试次数已用完');
clearInterval(heartTime);
}
}
// socket 发送数据
export function sendMsg(data: any) {
websocket.send(data);
}
// socket 接收数据
export function websocketonmessage() {
websocket.onmessage = (e: any) => {
if (e.data.indexOf('heartbeat') > 0) {
resetHeart();
}
if (e.data.indexOf('ping') > 0) {
return null;
}
useNoticeStore().addNotice({
message: e.data,
read: false,
time: new Date().toLocaleString()
});
window.$notification?.create({
title: '消息',
content: e.data,
type: 'success',
duration: 3000
});
return e.data;
};
}
+1 -1
View File
@@ -56,7 +56,7 @@ const bgColor = computed(() => {
<NCard :bordered="false" class="relative z-4 w-auto rd-12px">
<div class="w-400px lt-sm:w-300px">
<header class="flex-y-center justify-between">
<SystemLogo class="text-64px text-primary lt-sm:text-48px" />
<SystemLogo class="w-64px text-primary lt-sm:text-48px" />
<h3 class="text-28px text-primary font-500 lt-sm:text-22px">{{ $t('system.title') }}</h3>
<div class="i-flex-col">
<ThemeSchemaSwitch
+83 -52
View File
@@ -1,10 +1,13 @@
<script setup lang="ts">
import { computed, reactive } from 'vue';
import { computed, reactive, ref } from 'vue';
import type { SelectOption } from 'naive-ui';
import { useLoading } from '@sa/hooks';
import { $t } from '@/locales';
import { loginModuleRecord } from '@/constants/app';
import { useRouterPush } from '@/hooks/common/router';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { useAuthStore } from '@/store/modules/auth';
import { fetchCaptchaCode, fetchTenantList } from '@/service/api';
defineOptions({
name: 'PwdLogin'
@@ -13,71 +16,83 @@ defineOptions({
const authStore = useAuthStore();
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useNaiveForm();
const { loading: codeLoading, startLoading: startCodeLoading, endLoading: endCodeLoading } = useLoading();
interface FormModel {
userName: string;
password: string;
}
const codeUrl = ref<string>();
const captchaEnabled = ref<boolean>(false);
const tenantEnabled = ref<boolean>(false);
const tenantOption = ref<SelectOption[]>([]);
const model: FormModel = reactive({
userName: 'Soybean',
password: '123456'
const model: Api.Auth.LoginForm = reactive({
tenantId: '000000',
username: 'admin',
password: 'admin123',
remberMe: false
});
const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
const rules = computed<Record<keyof Api.Auth.LoginForm, App.Global.FormRule[]>>(() => {
// inside computed to make locale reactive, if not apply i18n, you can define it without computed
const { formRules } = useFormRules();
const { formRules, createRequiredRule } = useFormRules();
return {
userName: formRules.userName,
password: formRules.pwd
const loginRules: Record<keyof Api.Auth.LoginForm, App.Global.FormRule[]> = {
username: formRules.userName,
password: formRules.pwd,
code: captchaEnabled.value ? [createRequiredRule($t('form.code.required'))] : [],
tenantId: tenantEnabled.value ? formRules.tenantId : [],
rememberMe: [],
uuid: []
};
return loginRules;
});
async function handleSubmit() {
await validate();
await authStore.login(model.userName, model.password);
try {
await authStore.login(model);
} catch (error) {
handleFetchCaptchaCode();
}
}
type AccountKey = 'super' | 'admin' | 'user';
interface Account {
key: AccountKey;
label: string;
userName: string;
password: string;
async function handleFetchTenantList() {
const { data, error } = await fetchTenantList();
if (!error) {
tenantEnabled.value = data.tenantEnabled;
tenantOption.value = data.voList.map(tenant => {
return {
label: tenant.companyName,
value: tenant.tenantId
};
});
}
}
const accounts = computed<Account[]>(() => [
{
key: 'super',
label: $t('page.login.pwdLogin.superAdmin'),
userName: 'Super',
password: '123456'
},
{
key: 'admin',
label: $t('page.login.pwdLogin.admin'),
userName: 'Admin',
password: '123456'
},
{
key: 'user',
label: $t('page.login.pwdLogin.user'),
userName: 'User',
password: '123456'
}
]);
handleFetchTenantList();
async function handleAccountLogin(account: Account) {
await authStore.login(account.userName, account.password);
async function handleFetchCaptchaCode() {
startCodeLoading();
const { data, error } = await fetchCaptchaCode();
if (!error) {
captchaEnabled.value = data.captchaEnabled;
if (data.captchaEnabled) {
model.uuid = data.uuid;
codeUrl.value = `data:image/gif;base64,${data.img}`;
}
}
endCodeLoading();
}
handleFetchCaptchaCode();
</script>
<template>
<NForm ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<NFormItem path="userName">
<NInput v-model:value="model.userName" :placeholder="$t('page.login.common.userNamePlaceholder')" />
<NFormItem v-if="tenantEnabled" path="tenantId">
<NSelect v-model:value="model.tenantId" placeholder="请选择/输入公司名称" :options="tenantOption" />
</NFormItem>
<NFormItem path="username">
<NInput v-model:value="model.username" :placeholder="$t('page.login.common.usernamePlaceholder')" />
</NFormItem>
<NFormItem path="password">
<NInput
@@ -87,6 +102,17 @@ async function handleAccountLogin(account: Account) {
:placeholder="$t('page.login.common.passwordPlaceholder')"
/>
</NFormItem>
<NFormItem v-if="captchaEnabled" path="code">
<div class="w-full flex-y-center gap-16px">
<NInput v-model:value="model.code" :placeholder="$t('page.login.common.codePlaceholder')" />
<NSpin :show="codeLoading" :size="28" class="h-42px">
<NButton :focusable="false" class="login-code h-42px w-116px" @click="handleFetchCaptchaCode">
<img v-if="codeUrl" :src="codeUrl" />
<NEmpty v-else :show-icon="false" description="暂无验证码" />
</NButton>
</NSpin>
</div>
</NFormItem>
<NSpace vertical :size="24">
<div class="flex-y-center justify-between">
<NCheckbox>{{ $t('page.login.pwdLogin.rememberMe') }}</NCheckbox>
@@ -105,14 +131,19 @@ async function handleAccountLogin(account: Account) {
{{ $t(loginModuleRecord.register) }}
</NButton>
</div>
<NDivider class="text-14px text-#666 !m-0">{{ $t('page.login.pwdLogin.otherAccountLogin') }}</NDivider>
<div class="flex-center gap-12px">
<NButton v-for="item in accounts" :key="item.key" type="primary" @click="handleAccountLogin(item)">
{{ item.label }}
</NButton>
</div>
</NSpace>
</NForm>
</template>
<style scoped></style>
<style scoped>
.login-code {
&.n-button {
--n-padding: 0 8px !important;
background-color: #c0c0c0;
}
img {
height: 40px;
}
}
</style>
+1 -1
View File
@@ -48,7 +48,7 @@ const statisticData = computed<StatisticData[]>(() => [
</div>
<div class="pl-12px">
<h3 class="text-18px font-semibold">
{{ $t('page.home.greeting', { userName: authStore.userInfo.userName }) }}
{{ $t('page.home.greeting', { userName: authStore.userInfo.user?.userName }) }}
</h3>
<p class="text-#999 leading-30px">{{ $t('page.home.weatherDesc') }}</p>
</div>