Merge branch 'main' into example

This commit is contained in:
Soybean 2024-09-07 11:38:35 +08:00
commit 5bfc7613f7
37 changed files with 1530 additions and 1764 deletions

5
.env
View File

@ -36,7 +36,7 @@ VITE_SERVICE_LOGOUT_CODES=8888,8889
VITE_SERVICE_MODAL_LOGOUT_CODES=7777,7778 VITE_SERVICE_MODAL_LOGOUT_CODES=7777,7778
# token expired codes of backend service, when the code is received, it will refresh the token and resend the request # 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 VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998,3333
# when the route mode is static, the defined super role # when the route mode is static, the defined super role
VITE_STATIC_SUPER_ROLE=R_SUPER VITE_STATIC_SUPER_ROLE=R_SUPER
@ -46,3 +46,6 @@ VITE_SOURCE_MAP=N
# Used to differentiate storage across different domains # Used to differentiate storage across different domains
VITE_STORAGE_PREFIX=SOY_ VITE_STORAGE_PREFIX=SOY_
# used to control whether the program automatically detects updates
VITE_AUTOMATICALLY_DETECT_UPDATE=Y

View File

@ -1,6 +1,61 @@
# Changelog # Changelog
## [v1.3.5](https://github.com/soybeanjs/soybean-admin/compare/v1.3.4...v1.3.5) (2024-09-07)
###    🚀 Features
- **packages**:
- @sa/axios: add response to flatRequest when success &nbsp;-&nbsp; by @soybeanjs [<samp>(c4e16)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c4e1610)
- **projects**:
- README.zh_CN.md 添加合作推广 &nbsp;-&nbsp; by @PZ-18664918826 in https://github.com/soybeanjs/soybean-admin/issues/601 [<samp>(2fa40)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2fa400b)
- Add more commit types according to Apache specifications &nbsp;-&nbsp; by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/610 [<samp>(878d9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/878d9c3)
- does the configuration support automatic updates. close#612 &nbsp;-&nbsp; by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/617 and https://github.com/soybeanjs/soybean-admin/issues/612 [<samp>(4c9f4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4c9f4e0)
- add app error handler. close #587 &nbsp;-&nbsp; by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/587 [<samp>(be855)</samp>](https://github.com/soybeanjs/soybean-admin/commit/be8556c)
### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
- **deps**:
- move json5 from devDependencies to dependencies to support production usage &nbsp;-&nbsp; by @mufeng889 in https://github.com/soybeanjs/soybean-admin/issues/618 [<samp>(7cb43)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7cb43fc)
- **projects**:
- avoid retrieving cached HTML &nbsp;-&nbsp; by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/605 [<samp>(ef6cf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ef6cf93)
- fix login redirect &nbsp;-&nbsp; by @soybeanjs [<samp>(3830e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3830ec7)
- fix vertical-mix-menu when sider collapse. fixed #608 &nbsp;-&nbsp; by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/608 [<samp>(c3f1f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c3f1f69)
- fix breadcrumb when activeMenu is parent menu. fixed #589 &nbsp;-&nbsp; by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/589 [<samp>(79b2a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/79b2a28)
- fix refresh token when meet multi requests. fixed #581 &nbsp;-&nbsp; by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/581 [<samp>(27b52)</samp>](https://github.com/soybeanjs/soybean-admin/commit/27b5222)
- **types**:
- fix the type of TableApiFn &nbsp;-&nbsp; by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/599 [<samp>(26c93)</samp>](https://github.com/soybeanjs/soybean-admin/commit/26c93df)
### &nbsp;&nbsp;&nbsp;🛠 Optimizations
- **projects**: optimize menu selectedKey &nbsp;-&nbsp; by @soybeanjs [<samp>(531bf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/531bfaf)
### &nbsp;&nbsp;&nbsp;📖 Documentation
- **projects**:
- update README &nbsp;-&nbsp; by @mufeng889 in https://github.com/soybeanjs/soybean-admin/issues/594 [<samp>(a8f92)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a8f923e)
- update README &nbsp;-&nbsp; by @soybeanjs [<samp>(e9a2e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e9a2ee4)
- update README &nbsp;-&nbsp; by @soybeanjs [<samp>(73e91)</samp>](https://github.com/soybeanjs/soybean-admin/commit/73e917a)
- update the location of important information in the document &nbsp;-&nbsp; by **Azir** [<samp>(9c012)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9c012c7)
### &nbsp;&nbsp;&nbsp;🏡 Chore
- **deps**:
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(a1c14)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a1c14a1)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(7fa55)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7fa5590)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(a44ea)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a44ea62)
- **projects**:
- use json5 resolve env `VITE_OTHER_SERVICE_BASE_URL` & fix proxy enable &nbsp;-&nbsp; by @soybeanjs [<samp>(b16a9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b16a963)
### &nbsp;&nbsp;&nbsp;🎨 Styles
- **projects**: rename script czh to commit:zh &nbsp;-&nbsp; by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/597 [<samp>(5094f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5094f0e)
### &nbsp;&nbsp;&nbsp;❤️ Contributors
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![mufeng889](https://github.com/mufeng889.png?size=48)](https://github.com/mufeng889)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;[![PZ-18664918826](https://github.com/PZ-18664918826.png?size=48)](https://github.com/PZ-18664918826)&nbsp;&nbsp;
[Azir](mailto:2075125282@qq.com),&nbsp;
## [v1.3.4](https://github.com/honghuangdc/soybean-admin/compare/v1.3.3...v1.3.4) (2024-08-01) ## [v1.3.4](https://github.com/honghuangdc/soybean-admin/compare/v1.3.3...v1.3.4) (2024-08-01)
### &nbsp;&nbsp;&nbsp;🚨 Breaking Changes ### &nbsp;&nbsp;&nbsp;🚨 Breaking Changes

View File

@ -111,6 +111,7 @@ pnpm build
## Ecosystem ## Ecosystem
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): SoybeanAdmin based version of React.
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): A Mock Api management system that helps front-end developers quickly implement interface mocks. - [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. - [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). - [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).
@ -155,7 +156,7 @@ Thanks the following people for their contributions. If you want to contribute t
<div> <div>
<p>QQ Group</p> <p>QQ Group</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-2.jpg" style="width:200px" /> <img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-3.jpg" style="width:200px" />
</div> </div>
<!-- <div> <!-- <div>
<p>WeChat Group</p> <p>WeChat Group</p>
@ -165,10 +166,6 @@ Thanks the following people for their contributions. If you want to contribute t
<p>Add the following WeChat to invite to the WeChat group</p> <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" /> <img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybeanjs.jpg" style="width:200px" />
</div> </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 Trend

View File

@ -57,6 +57,36 @@
- [地址](https://docs.soybeanjs.cn) - [地址](https://docs.soybeanjs.cn)
- [旧版文档](https://legacy-docs.soybeanjs.cn) - [旧版文档](https://legacy-docs.soybeanjs.cn)
## 合作事项
我们非常感谢大家对 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 的支持!为了进一步回馈社区,并助力企业和开发者实现个性化需求,我们现提供多种合作服务,期待与您携手共赢。
##### 1、定制化管理后台开发
针对企业和开发者的特定业务需求,我们提供基于 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 的定制化管理后台开发服务。我们的团队具备丰富的行业经验,能够迅速理解并实现您的需求,打造高效、灵活且安全的定制化解决方案。
- **定制开发**我们将根据您的具体需求提供从需求分析、UI设计到功能实现的全方位服务确保项目高效交付。
- **功能扩展**:在 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 基础上,扩展您所需的特定功能模块,提升管理后台的功能和用户体验。
##### 2、企业外包服务
我们承接各类企业级外包项目,特别是在管理后台系统的开发、集成与运维方面。我们以精益求精的态度,确保项目的质量和进度,为您的业务提供强有力的技术支持。
- **项目开发**:无论是全新的项目,还是现有系统的优化与集成,我们都将为您量身打造高效可靠的解决方案。
- **系统集成与维护**:我们也提供基于 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 的系统集成与长期维护服务,确保您的系统稳定、安全地运行。
##### 3、联系方式
如有合作意向或项目咨询,请通过以下方式与我们联系:
- **Email**: [soybeanjs@outlook.com](mailto:soybeanjs@outlook.com)
- **GitHub Issues**: 欢迎通过 [GitHub Issues](https://github.com/soybeanjs/soybean-admin/issues/new) 联系我们,进行初步的合作洽谈。
- **商务合作微信**: honghuangdc
期待与您开展深入合作,共同推动 SoybeanAdmin 项目及其在更多领域的成功应用!
## 示例图片 ## 示例图片
![](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-01.png)
@ -109,6 +139,7 @@ pnpm build
## 周边生态 ## 周边生态
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): 基于SoybeanAdmin的React版本.
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。 - [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。 - [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) 开发的简单权限系统。 - [pea](https://github.com/haitang1894/pea) : 采用SpringBoot3.2 + JDK21、MyBatis-Plus、SpringSecurity安全框架等适配 [soybean-admin](https://gitee.com/honghuangdc/soybean-admin) 开发的简单权限系统。
@ -155,7 +186,7 @@ pnpm build
<div> <div>
<p>QQ交流群</p> <p>QQ交流群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-2.jpg" style="width:200px" /> <img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-3.jpg" style="width:200px" />
</div> </div>
<!-- <div> <!-- <div>
<p>微信群</p> <p>微信群</p>
@ -165,10 +196,6 @@ pnpm build
<p>添加下面微信邀请进微信群</p> <p>添加下面微信邀请进微信群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybeanjs.jpg" style="width:200px" /> <img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybeanjs.jpg" style="width:200px" />
</div> </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 趋势

View File

@ -5,10 +5,10 @@ import { createServiceConfig } from '../../src/utils/service';
* Set http proxy * Set http proxy
* *
* @param env - The current env * @param env - The current env
* @param isDev - Is development environment * @param enable - If enable http proxy
*/ */
export function createViteProxy(env: Env.ImportMeta, isDev: boolean) { export function createViteProxy(env: Env.ImportMeta, enable: boolean) {
const isEnableHttpProxy = isDev && env.VITE_HTTP_PROXY === 'Y'; const isEnableHttpProxy = enable && env.VITE_HTTP_PROXY === 'Y';
if (!isEnableHttpProxy) return undefined; if (!isEnableHttpProxy) return undefined;

View File

@ -1,7 +1,7 @@
{ {
"name": "soybean-admin", "name": "soybean-admin",
"type": "module", "type": "module",
"version": "1.3.4", "version": "1.3.5",
"description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。", "description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
"author": { "author": {
"name": "Soybean", "name": "Soybean",
@ -35,7 +35,7 @@
"build:test": "vite build --mode test", "build:test": "vite build --mode test",
"cleanup": "sa cleanup", "cleanup": "sa cleanup",
"commit": "sa git-commit", "commit": "sa git-commit",
"czh": "sa git-commit -l=zh-cn", "commit:zh": "sa git-commit -l=zh-cn",
"dev": "vite --mode test", "dev": "vite --mode test",
"dev:prod": "vite --mode prod", "dev:prod": "vite --mode prod",
"gen-route": "sa gen-route", "gen-route": "sa gen-route",
@ -48,7 +48,7 @@
}, },
"dependencies": { "dependencies": {
"@antv/data-set": "0.11.8", "@antv/data-set": "0.11.8",
"@antv/g2": "5.2.1", "@antv/g2": "5.2.5",
"@better-scroll/core": "2.5.1", "@better-scroll/core": "2.5.1",
"@iconify/vue": "4.1.2", "@iconify/vue": "4.1.2",
"@sa/axios": "workspace:*", "@sa/axios": "workspace:*",
@ -56,65 +56,66 @@
"@sa/hooks": "workspace:*", "@sa/hooks": "workspace:*",
"@sa/materials": "workspace:*", "@sa/materials": "workspace:*",
"@sa/utils": "workspace:*", "@sa/utils": "workspace:*",
"@vueuse/core": "10.11.0", "@vueuse/core": "11.0.3",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"dayjs": "1.11.12", "dayjs": "1.11.13",
"dhtmlx-gantt": "8.0.9", "dhtmlx-gantt": "8.0.10",
"dompurify": "3.1.6", "dompurify": "3.1.6",
"echarts": "5.5.1", "echarts": "5.5.1",
"jsbarcode": "3.11.6", "jsbarcode": "3.11.6",
"json5": "2.2.3",
"naive-ui": "2.39.0", "naive-ui": "2.39.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"pinia": "2.2.0", "pinia": "2.2.2",
"pinyin-pro": "3.23.1", "pinyin-pro": "3.24.2",
"print-js": "1.6.0", "print-js": "1.6.0",
"swiper": "11.1.5", "swiper": "11.1.12",
"tailwind-merge": "2.4.0", "tailwind-merge": "2.5.2",
"typeit": "8.8.4", "typeit": "8.8.4",
"vditor": "3.10.4", "vditor": "3.10.5",
"vue": "3.4.35", "vue": "3.5.3",
"vue-draggable-plus": "0.5.2", "vue-draggable-plus": "0.5.3",
"vue-i18n": "9.13.1", "vue-i18n": "9.14.0",
"vue-pdf-embed": "2.1.0", "vue-pdf-embed": "2.1.0",
"vue-router": "4.4.1", "vue-router": "4.4.3",
"wangeditor": "4.7.15", "wangeditor": "4.7.15",
"xgplayer": "3.0.19", "xgplayer": "3.0.20",
"xlsx": "0.18.5" "xlsx": "0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@amap/amap-jsapi-types": "0.0.15", "@amap/amap-jsapi-types": "0.0.15",
"@elegant-router/vue": "0.3.8", "@elegant-router/vue": "0.3.8",
"@iconify/json": "2.2.232", "@iconify/json": "2.2.245",
"@sa/scripts": "workspace:*", "@sa/scripts": "workspace:*",
"@sa/uno-preset": "workspace:*", "@sa/uno-preset": "workspace:*",
"@soybeanjs/eslint-config": "1.4.0", "@soybeanjs/eslint-config": "1.4.0",
"@types/bmapgl": "0.0.7", "@types/bmapgl": "0.0.7",
"@types/dompurify": "3.0.5", "@types/dompurify": "3.0.5",
"@types/node": "22.0.1", "@types/node": "22.5.4",
"@types/nprogress": "0.2.3", "@types/nprogress": "0.2.3",
"@unocss/eslint-config": "0.61.9", "@unocss/eslint-config": "0.62.3",
"@unocss/preset-icons": "0.61.9", "@unocss/preset-icons": "0.62.3",
"@unocss/preset-uno": "0.61.9", "@unocss/preset-uno": "0.62.3",
"@unocss/transformer-directives": "0.61.9", "@unocss/transformer-directives": "0.62.3",
"@unocss/transformer-variant-group": "0.61.9", "@unocss/transformer-variant-group": "0.62.3",
"@unocss/vite": "0.61.9", "@unocss/vite": "0.62.3",
"@vitejs/plugin-vue": "5.1.1", "@vitejs/plugin-vue": "5.1.3",
"@vitejs/plugin-vue-jsx": "4.0.0", "@vitejs/plugin-vue-jsx": "4.0.1",
"eslint": "9.8.0", "eslint": "9.10.0",
"eslint-plugin-vue": "9.27.0", "eslint-plugin-vue": "9.28.0",
"lint-staged": "15.2.7", "lint-staged": "15.2.10",
"sass": "1.77.8", "sass": "1.78.0",
"simple-git-hooks": "2.11.1", "simple-git-hooks": "2.11.1",
"tsx": "4.16.3", "tsx": "4.19.0",
"typescript": "5.5.4", "typescript": "5.5.4",
"unplugin-icons": "0.19.1", "unplugin-icons": "0.19.3",
"unplugin-vue-components": "0.27.3", "unplugin-vue-components": "0.27.4",
"vite": "5.3.5", "vite": "5.4.3",
"vite-plugin-progress": "0.0.7", "vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1", "vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "7.3.7", "vite-plugin-vue-devtools": "7.4.4",
"vue-eslint-parser": "9.4.3", "vue-eslint-parser": "9.4.3",
"vue-tsc": "2.0.29" "vue-tsc": "2.1.6"
}, },
"simple-git-hooks": { "simple-git-hooks": {
"commit-msg": "pnpm sa git-commit-verify", "commit-msg": "pnpm sa git-commit-verify",

View File

@ -1,6 +1,6 @@
{ {
"name": "@sa/axios", "name": "@sa/axios",
"version": "1.3.4", "version": "1.3.5",
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts"
}, },
@ -11,9 +11,9 @@
}, },
"dependencies": { "dependencies": {
"@sa/utils": "workspace:*", "@sa/utils": "workspace:*",
"axios": "1.7.2", "axios": "1.7.7",
"axios-retry": "4.4.2", "axios-retry": "4.5.0",
"qs": "6.12.3" "qs": "6.13.0"
}, },
"devDependencies": { "devDependencies": {
"@types/qs": "6.9.15" "@types/qs": "6.9.15"

View File

@ -162,12 +162,12 @@ export function createFlatRequest<ResponseData = any, State = Record<string, unk
if (responseType === 'json') { if (responseType === 'json') {
const data = opts.transformBackendResponse(response); const data = opts.transformBackendResponse(response);
return { data, error: null }; return { data, error: null, response };
} }
return { data: response.data as MappedType<R, T>, error: null }; return { data: response.data as MappedType<R, T>, error: null };
} catch (error) { } catch (error) {
return { data: null, error }; return { data: null, error, response: (error as AxiosError<ResponseData>).response };
} }
} as FlatRequestInstance<State, ResponseData>; } as FlatRequestInstance<State, ResponseData>;

View File

@ -92,18 +92,20 @@ export interface RequestInstance<S = Record<string, unknown>> extends RequestIns
<T = any, R extends ResponseType = 'json'>(config: CustomAxiosRequestConfig<R>): Promise<MappedType<R, T>>; <T = any, R extends ResponseType = 'json'>(config: CustomAxiosRequestConfig<R>): Promise<MappedType<R, T>>;
} }
export type FlatResponseSuccessData<T = any> = { export type FlatResponseSuccessData<T = any, ResponseData = any> = {
data: T; data: T;
error: null; error: null;
response: AxiosResponse<ResponseData>;
}; };
export type FlatResponseFailData<ResponseData = any> = { export type FlatResponseFailData<ResponseData = any> = {
data: null; data: null;
error: AxiosError<ResponseData>; error: AxiosError<ResponseData>;
response: AxiosResponse<ResponseData>;
}; };
export type FlatResponseData<T = any, ResponseData = any> = export type FlatResponseData<T = any, ResponseData = any> =
| FlatResponseSuccessData<T> | FlatResponseSuccessData<T, ResponseData>
| FlatResponseFailData<ResponseData>; | FlatResponseFailData<ResponseData>;
export interface FlatRequestInstance<S = Record<string, unknown>, ResponseData = any> extends RequestInstanceCommon<S> { export interface FlatRequestInstance<S = Record<string, unknown>, ResponseData = any> extends RequestInstanceCommon<S> {

View File

@ -1,6 +1,6 @@
{ {
"name": "@sa/color", "name": "@sa/color",
"version": "1.3.4", "version": "1.3.5",
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts"
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "@sa/hooks", "name": "@sa/hooks",
"version": "1.3.4", "version": "1.3.5",
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts"
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "@sa/materials", "name": "@sa/materials",
"version": "1.3.4", "version": "1.3.5",
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts"
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "@sa/fetch", "name": "@sa/fetch",
"version": "1.3.4", "version": "1.3.5",
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts"
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "@sa/scripts", "name": "@sa/scripts",
"version": "1.3.4", "version": "1.3.5",
"bin": { "bin": {
"sa": "./bin.ts" "sa": "./bin.ts"
}, },
@ -14,14 +14,14 @@
}, },
"devDependencies": { "devDependencies": {
"@soybeanjs/changelog": "0.3.24", "@soybeanjs/changelog": "0.3.24",
"bumpp": "9.4.1", "bumpp": "9.5.2",
"c12": "1.11.1", "c12": "1.11.2",
"cac": "6.7.14", "cac": "6.7.14",
"consola": "3.2.3", "consola": "3.2.3",
"enquirer": "2.4.1", "enquirer": "2.4.1",
"execa": "9.3.0", "execa": "9.3.1",
"kolorist": "1.8.0", "kolorist": "1.8.0",
"npm-check-updates": "17.0.0", "npm-check-updates": "17.1.1",
"rimraf": "6.0.1" "rimraf": "6.0.1"
} }
} }

View File

@ -11,13 +11,15 @@ export const locales = {
}, },
gitCommitTypes: [ gitCommitTypes: [
['feat', '新功能'], ['feat', '新功能'],
['feat-wip', '开发中的功能,比如某功能的部分代码'],
['fix', '修复Bug'], ['fix', '修复Bug'],
['docs', '只更新文档'], ['docs', '只涉及文档更新'],
['typo', '代码或文档勘误,比如错误拼写'],
['style', '修改代码风格,不影响代码含义的变更'], ['style', '修改代码风格,不影响代码含义的变更'],
['refactor', '代码重构,既不修复 bug 也不添加功能的代码变更'], ['refactor', '代码重构,既不修复 bug 也不添加功能的代码变更'],
['perf', '可提高性能的代码更改'], ['perf', '可提高性能的代码更改'],
['optimize', '优化代码质量的代码更改'], ['optimize', '优化代码质量的代码更改'],
['test', '添加缺失的测试或更正现有测'], ['test', '添加缺失的测试或更正现有测'],
['build', '影响构建系统或外部依赖项的更改'], ['build', '影响构建系统或外部依赖项的更改'],
['ci', '对 CI 配置文件和脚本的更改'], ['ci', '对 CI 配置文件和脚本的更改'],
['chore', '没有修改src或测试文件的其他变更'], ['chore', '没有修改src或测试文件的其他变更'],
@ -47,8 +49,10 @@ export const locales = {
}, },
gitCommitTypes: [ gitCommitTypes: [
['feat', 'A new feature'], ['feat', 'A new feature'],
['feat-wip', 'Features in development, such as partial code for a certain feature'],
['fix', 'A bug fix'], ['fix', 'A bug fix'],
['docs', 'Documentation only changes'], ['docs', 'Documentation only changes'],
['typo', 'Code or document corrections, such as spelling errors'],
['style', 'Changes that do not affect the meaning of the code'], ['style', 'Changes that do not affect the meaning of the code'],
['refactor', 'A code change that neither fixes a bug nor adds a feature'], ['refactor', 'A code change that neither fixes a bug nor adds a feature'],
['perf', 'A code change that improves performance'], ['perf', 'A code change that improves performance'],

View File

@ -1,6 +1,6 @@
{ {
"name": "@sa/uno-preset", "name": "@sa/uno-preset",
"version": "1.3.4", "version": "1.3.5",
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts"
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "@sa/utils", "name": "@sa/utils",
"version": "1.3.4", "version": "1.3.5",
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts"
}, },

File diff suppressed because it is too large Load Diff

View File

@ -93,11 +93,15 @@ export function useRouterPush(inSetup = true) {
return routerPushByKey('login', { query, params: { module } }); return routerPushByKey('login', { query, params: { module } });
} }
/** Redirect from login */ /**
async function redirectFromLogin() { * Redirect from login
*
* @param [needRedirect=true] Whether to redirect after login. Default is `true`
*/
async function redirectFromLogin(needRedirect = true) {
const redirect = route.value.query?.redirect as string; const redirect = route.value.query?.redirect as string;
if (redirect) { if (needRedirect && redirect) {
routerPush(redirect); routerPush(redirect);
} else { } else {
toHome(); toHome();

View File

@ -8,6 +8,7 @@ export const { setupStore: setupMixMenuContext, useStore: useMixMenuContext } =
function useMixMenu() { function useMixMenu() {
const route = useRoute(); const route = useRoute();
const routeStore = useRouteStore(); const routeStore = useRouteStore();
const { selectedKey } = useMenu();
const activeFirstLevelMenuKey = ref(''); const activeFirstLevelMenuKey = ref('');
@ -16,12 +17,7 @@ function useMixMenu() {
} }
function getActiveFirstLevelMenuKey() { function getActiveFirstLevelMenuKey() {
const { hideInMenu, activeMenu } = route.meta; const [firstLevelRouteName] = selectedKey.value.split('_');
const name = route.name as string;
const routeName = (hideInMenu ? activeMenu : name) || name;
const [firstLevelRouteName] = routeName.split('_');
setActiveFirstLevelMenuKey(firstLevelRouteName); setActiveFirstLevelMenuKey(firstLevelRouteName);
} }
@ -68,3 +64,20 @@ function useMixMenu() {
getActiveFirstLevelMenuKey getActiveFirstLevelMenuKey
}; };
} }
export function useMenu() {
const route = useRoute();
const selectedKey = computed(() => {
const { hideInMenu, activeMenu } = route.meta;
const name = route.name as string;
const routeName = (hideInMenu ? activeMenu : name) || name;
return routeName;
});
return {
selectedKey
};
}

View File

@ -1,26 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { GLOBAL_HEADER_MENU_ID } from '@/constants/app'; import { GLOBAL_HEADER_MENU_ID } from '@/constants/app';
import { useRouteStore } from '@/store/modules/route'; import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
import { useMenu } from '../../../context';
defineOptions({ defineOptions({
name: 'HorizontalMenu' name: 'HorizontalMenu'
}); });
const route = useRoute();
const routeStore = useRouteStore(); const routeStore = useRouteStore();
const { routerPushByKeyWithMetaQuery } = useRouterPush(); const { routerPushByKeyWithMetaQuery } = useRouterPush();
const { selectedKey } = useMenu();
const selectedKey = computed(() => {
const { hideInMenu, activeMenu } = route.meta;
const name = route.name as string;
const routeName = (hideInMenu ? activeMenu : name) || name;
return routeName;
});
</script> </script>
<template> <template>

View File

@ -1,31 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { GLOBAL_HEADER_MENU_ID, GLOBAL_SIDER_MENU_ID } from '@/constants/app'; import { GLOBAL_HEADER_MENU_ID, GLOBAL_SIDER_MENU_ID } from '@/constants/app';
import { useAppStore } from '@/store/modules/app'; import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
import FirstLevelMenu from '../components/first-level-menu.vue'; import FirstLevelMenu from '../components/first-level-menu.vue';
import { useMixMenuContext } from '../../../context'; import { useMenu, useMixMenuContext } from '../../../context';
defineOptions({ defineOptions({
name: 'HorizontalMixMenu' name: 'HorizontalMixMenu'
}); });
const route = useRoute();
const appStore = useAppStore(); const appStore = useAppStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();
const { allMenus, childLevelMenus, activeFirstLevelMenuKey, setActiveFirstLevelMenuKey } = useMixMenuContext();
const { routerPushByKeyWithMetaQuery } = useRouterPush(); const { routerPushByKeyWithMetaQuery } = useRouterPush();
const { allMenus, childLevelMenus, activeFirstLevelMenuKey, setActiveFirstLevelMenuKey } = useMixMenuContext();
const selectedKey = computed(() => { const { selectedKey } = useMenu();
const { hideInMenu, activeMenu } = route.meta;
const name = route.name as string;
const routeName = (hideInMenu ? activeMenu : name) || name;
return routeName;
});
function handleSelectMixMenu(menu: App.Global.Menu) { function handleSelectMixMenu(menu: App.Global.Menu) {
setActiveFirstLevelMenuKey(menu.key); setActiveFirstLevelMenuKey(menu.key);

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import type { RouteKey } from '@elegant-router/types'; import type { RouteKey } from '@elegant-router/types';
import { SimpleScrollbar } from '@sa/materials'; import { SimpleScrollbar } from '@sa/materials';
@ -8,7 +8,7 @@ import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { useRouteStore } from '@/store/modules/route'; import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
import { useMixMenuContext } from '../../../context'; import { useMenu, useMixMenuContext } from '../../../context';
defineOptions({ defineOptions({
name: 'ReversedHorizontalMixMenu' name: 'ReversedHorizontalMixMenu'
@ -18,6 +18,7 @@ const route = useRoute();
const appStore = useAppStore(); const appStore = useAppStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();
const routeStore = useRouteStore(); const routeStore = useRouteStore();
const { routerPushByKeyWithMetaQuery } = useRouterPush();
const { const {
firstLevelMenus, firstLevelMenus,
childLevelMenus, childLevelMenus,
@ -25,16 +26,7 @@ const {
setActiveFirstLevelMenuKey, setActiveFirstLevelMenuKey,
isActiveFirstLevelMenuHasChildren isActiveFirstLevelMenuHasChildren
} = useMixMenuContext(); } = useMixMenuContext();
const { routerPushByKeyWithMetaQuery } = useRouterPush(); const { selectedKey } = useMenu();
const selectedKey = computed(() => {
const { hideInMenu, activeMenu } = route.meta;
const name = route.name as string;
const routeName = (hideInMenu ? activeMenu : name) || name;
return routeName;
});
function handleSelectMixMenu(key: RouteKey) { function handleSelectMixMenu(key: RouteKey) {
setActiveFirstLevelMenuKey(key); setActiveFirstLevelMenuKey(key);

View File

@ -7,6 +7,7 @@ import { useThemeStore } from '@/store/modules/theme';
import { useRouteStore } from '@/store/modules/route'; import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
import { GLOBAL_SIDER_MENU_ID } from '@/constants/app'; import { GLOBAL_SIDER_MENU_ID } from '@/constants/app';
import { useMenu } from '../../../context';
defineOptions({ defineOptions({
name: 'VerticalMenu' name: 'VerticalMenu'
@ -17,18 +18,10 @@ const appStore = useAppStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();
const routeStore = useRouteStore(); const routeStore = useRouteStore();
const { routerPushByKeyWithMetaQuery } = useRouterPush(); const { routerPushByKeyWithMetaQuery } = useRouterPush();
const { selectedKey } = useMenu();
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted); const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
const selectedKey = computed(() => {
const { hideInMenu, activeMenu } = route.meta;
const name = route.name as string;
const routeName = (hideInMenu ? activeMenu : name) || name;
return routeName;
});
const expandedKeys = ref<string[]>([]); const expandedKeys = ref<string[]>([]);
function updateExpandedKeys() { function updateExpandedKeys() {

View File

@ -9,7 +9,7 @@ import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
import { $t } from '@/locales'; import { $t } from '@/locales';
import { GLOBAL_SIDER_MENU_ID } from '@/constants/app'; import { GLOBAL_SIDER_MENU_ID } from '@/constants/app';
import { useMixMenuContext } from '../../../context'; import { useMenu, useMixMenuContext } from '../../../context';
import FirstLevelMenu from '../components/first-level-menu.vue'; import FirstLevelMenu from '../components/first-level-menu.vue';
import GlobalLogo from '../../global-logo/index.vue'; import GlobalLogo from '../../global-logo/index.vue';
@ -31,6 +31,7 @@ const {
getActiveFirstLevelMenuKey getActiveFirstLevelMenuKey
// //
} = useMixMenuContext(); } = useMixMenuContext();
const { selectedKey } = useMenu();
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted); const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
@ -56,15 +57,6 @@ function handleResetActiveMenu() {
} }
} }
const selectedKey = computed(() => {
const { hideInMenu, activeMenu } = route.meta;
const name = route.name as string;
const routeName = (hideInMenu ? activeMenu : name) || name;
return routeName;
});
const expandedKeys = ref<string[]>([]); const expandedKeys = ref<string[]>([]);
function updateExpandedKeys() { function updateExpandedKeys() {
@ -122,9 +114,6 @@ watch(
mode="vertical" mode="vertical"
:value="selectedKey" :value="selectedKey"
:options="childLevelMenus" :options="childLevelMenus"
:collapsed="appStore.siderCollapse"
:collapsed-width="themeStore.sider.collapsedWidth"
:collapsed-icon-size="22"
:inverted="inverted" :inverted="inverted"
:indent="18" :indent="18"
@update:value="routerPushByKeyWithMetaQuery" @update:value="routerPushByKeyWithMetaQuery"

View File

@ -1,8 +1,20 @@
import { h } from 'vue'; import { h } from 'vue';
import type { App } from 'vue';
import { NButton } from 'naive-ui'; import { NButton } from 'naive-ui';
import { $t } from '../locales'; import { $t } from '@/locales';
export function setupAppErrorHandle(app: App) {
app.config.errorHandler = (err, vm, info) => {
// eslint-disable-next-line no-console
console.error(err, vm, info);
};
}
export function setupAppVersionNotification() { export function setupAppVersionNotification() {
const canAutoUpdateApp = import.meta.env.VITE_AUTOMATICALLY_DETECT_UPDATE === 'Y';
if (!canAutoUpdateApp) return;
let isShow = false; let isShow = false;
document.addEventListener('visibilitychange', async () => { document.addEventListener('visibilitychange', async () => {
@ -52,9 +64,7 @@ export function setupAppVersionNotification() {
} }
async function getHtmlBuildTime() { async function getHtmlBuildTime() {
const baseURL = import.meta.env.VITE_BASE_URL; const res = await fetch(`/index.html?time=${Date.now()}`);
const res = await fetch(`${baseURL}index.html`);
const html = await res.text(); const html = await res.text();

View File

@ -4,7 +4,7 @@ import { useAuthStore } from '@/store/modules/auth';
import { $t } from '@/locales'; import { $t } from '@/locales';
import { localStg } from '@/utils/storage'; import { localStg } from '@/utils/storage';
import { getServiceBaseURL } from '@/utils/service'; import { getServiceBaseURL } from '@/utils/service';
import { handleRefreshToken, showErrorMsg } from './shared'; import { getAuthorization, handleExpiredRequest, showErrorMsg } from './shared';
import type { RequestInstanceState } from './type'; import type { RequestInstanceState } from './type';
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y'; const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
@ -19,12 +19,8 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
}, },
{ {
async onRequest(config) { async onRequest(config) {
const { headers } = config; const Authorization = getAuthorization();
Object.assign(config.headers, { Authorization });
// set token
const token = localStg.get('token');
const Authorization = token ? `Bearer ${token}` : null;
Object.assign(headers, { Authorization });
return config; return config;
}, },
@ -83,15 +79,13 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
// when the backend response code is in `expiredTokenCodes`, it means the token is expired, and refresh token // when the backend response code is in `expiredTokenCodes`, it means the token is expired, and refresh token
// the api `refreshToken` can not return error code in `expiredTokenCodes`, otherwise it will be a dead loop, should return `logoutCodes` or `modalLogoutCodes` // the api `refreshToken` can not return error code in `expiredTokenCodes`, otherwise it will be a dead loop, should return `logoutCodes` or `modalLogoutCodes`
const expiredTokenCodes = import.meta.env.VITE_SERVICE_EXPIRED_TOKEN_CODES?.split(',') || []; const expiredTokenCodes = import.meta.env.VITE_SERVICE_EXPIRED_TOKEN_CODES?.split(',') || [];
if (expiredTokenCodes.includes(responseCode) && !request.state.isRefreshingToken) { if (expiredTokenCodes.includes(responseCode)) {
request.state.isRefreshingToken = true; const success = await handleExpiredRequest(request.state);
if (success) {
const Authorization = getAuthorization();
Object.assign(response.config.headers, { Authorization });
const refreshConfig = await handleRefreshToken(response.config); return instance.request(response.config) as Promise<AxiosResponse>;
request.state.isRefreshingToken = false;
if (refreshConfig) {
return instance.request(refreshConfig) as Promise<AxiosResponse>;
} }
} }

View File

@ -1,34 +1,44 @@
import type { AxiosRequestConfig } from 'axios';
import { useAuthStore } from '@/store/modules/auth'; import { useAuthStore } from '@/store/modules/auth';
import { localStg } from '@/utils/storage'; import { localStg } from '@/utils/storage';
import { fetchRefreshToken } from '../api'; import { fetchRefreshToken } from '../api';
import type { RequestInstanceState } from './type'; import type { RequestInstanceState } from './type';
/** export function getAuthorization() {
* refresh token const token = localStg.get('token');
* const Authorization = token ? `Bearer ${token}` : null;
* @param axiosConfig - request config when the token is expired
*/ return Authorization;
export async function handleRefreshToken(axiosConfig: AxiosRequestConfig) { }
/** refresh token */
async function handleRefreshToken() {
const { resetStore } = useAuthStore(); const { resetStore } = useAuthStore();
const refreshToken = localStg.get('refreshToken') || ''; const rToken = localStg.get('refreshToken') || '';
const { error, data } = await fetchRefreshToken(refreshToken); const { error, data } = await fetchRefreshToken(rToken);
if (!error) { if (!error) {
localStg.set('token', data.token); localStg.set('token', data.token);
localStg.set('refreshToken', data.refreshToken); localStg.set('refreshToken', data.refreshToken);
return true;
const config = { ...axiosConfig };
if (config.headers) {
config.headers.Authorization = data.token;
}
return config;
} }
resetStore(); resetStore();
return null; return false;
}
export async function handleExpiredRequest(state: RequestInstanceState) {
if (!state.refreshTokenFn) {
state.refreshTokenFn = handleRefreshToken();
}
const success = await state.refreshTokenFn;
setTimeout(() => {
state.refreshTokenFn = null;
}, 1000);
return success;
} }
export function showErrorMsg(state: RequestInstanceState, message: string) { export function showErrorMsg(state: RequestInstanceState, message: string) {

View File

@ -1,6 +1,6 @@
export interface RequestInstanceState { export interface RequestInstanceState {
/** whether the request is refreshing token */ /** whether the request is refreshing token */
isRefreshingToken: boolean; refreshTokenFn: Promise<boolean> | null;
/** the request error message stack */ /** the request error message stack */
errMsgStack: string[]; errMsgStack: string[];
} }

View File

@ -71,9 +71,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
if (pass) { if (pass) {
await routeStore.initAuthRoute(); await routeStore.initAuthRoute();
if (redirect) { await redirectFromLogin(redirect);
await redirectFromLogin();
}
if (routeStore.isInitAuthRoute) { if (routeStore.isInitAuthRoute) {
window.$notification?.success({ window.$notification?.success({

View File

@ -281,15 +281,25 @@ export function getBreadcrumbsByRoute(
const key = route.name as string; const key = route.name as string;
const activeKey = route.meta?.activeMenu; const activeKey = route.meta?.activeMenu;
const menuKey = activeKey || key;
for (const menu of menus) { for (const menu of menus) {
if (menu.key === menuKey) { if (menu.key === key) {
const breadcrumbMenu = menuKey !== activeKey ? menu : getGlobalMenuByBaseRoute(route); const breadcrumbMenu = menu;
return [transformMenuToBreadcrumb(breadcrumbMenu)]; return [transformMenuToBreadcrumb(breadcrumbMenu)];
} }
if (menu.key === activeKey) {
const ROUTE_DEGREE_SPLITTER = '_';
const parentKey = key.split(ROUTE_DEGREE_SPLITTER).slice(0, -1).join(ROUTE_DEGREE_SPLITTER);
const breadcrumbMenu = getGlobalMenuByBaseRoute(route);
if (parentKey !== activeKey) {
return [transformMenuToBreadcrumb(breadcrumbMenu)];
}
return [transformMenuToBreadcrumb(menu), transformMenuToBreadcrumb(breadcrumbMenu)];
}
if (menu.children?.length) { if (menu.children?.length) {
const result = getBreadcrumbsByRoute(route, menu.children); const result = getBreadcrumbsByRoute(route, menu.children);
if (result.length > 0) { if (result.length > 0) {

View File

@ -20,6 +20,9 @@ declare namespace Api {
records: T[]; records: T[];
} }
/** common search params of table */
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'current' | 'size'>;
/** /**
* enable status * enable status
* *

View File

@ -103,6 +103,8 @@ declare namespace Env {
readonly VITE_ICONIFY_URL?: string; readonly VITE_ICONIFY_URL?: string;
/** Used to differentiate storage across different domains */ /** Used to differentiate storage across different domains */
readonly VITE_STORAGE_PREFIX?: string; readonly VITE_STORAGE_PREFIX?: string;
/** Whether to automatically detect updates after configuring application packaging */
readonly VITE_AUTOMATICALLY_DETECT_UPDATE?: CommonType.YesOrNo;
} }
} }

View File

@ -26,7 +26,7 @@ declare namespace NaiveUI {
type TableColumn<T> = TableColumnWithKey<T> | DataTableSelectionColumn<T> | DataTableExpandColumn<T>; type TableColumn<T> = TableColumnWithKey<T> | DataTableSelectionColumn<T> | DataTableExpandColumn<T>;
type TableApiFn<T = any, R = Api.SystemManage.CommonSearchParams> = ( type TableApiFn<T = any, R = Api.Common.CommonSearchParams> = (
params: R params: R
) => Promise<FlatResponseData<Api.Common.PaginatingQueryRecord<T>>>; ) => Promise<FlatResponseData<Api.Common.PaginatingQueryRecord<T>>>;

View File

@ -1,3 +1,5 @@
import json5 from 'json5';
/** /**
* Create service config by current env * Create service config by current env
* *
@ -8,10 +10,10 @@ export function createServiceConfig(env: Env.ImportMeta) {
let other = {} as Record<App.Service.OtherBaseURLKey, string>; let other = {} as Record<App.Service.OtherBaseURLKey, string>;
try { try {
other = JSON.parse(VITE_OTHER_SERVICE_BASE_URL); other = json5.parse(VITE_OTHER_SERVICE_BASE_URL);
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error('VITE_OTHER_SERVICE_BASE_URL is not a valid JSON string'); console.error('VITE_OTHER_SERVICE_BASE_URL is not a valid json5 string');
} }
const httpConfig: App.Service.SimpleServiceConfig = { const httpConfig: App.Service.SimpleServiceConfig = {

View File

@ -1,7 +1,7 @@
<script setup lang="tsx"> <script setup lang="tsx">
import { onMounted, shallowRef } from 'vue'; import { onMounted, shallowRef } from 'vue';
import { gantt } from 'dhtmlx-gantt'; import { gantt } from 'dhtmlx-gantt';
import type { GanttConfigOptions, ZoomLevels } from 'dhtmlx-gantt'; import type { GanttConfigOptions, ZoomLevel } from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'; import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
import { ganttTasks } from './data'; import { ganttTasks } from './data';
@ -72,7 +72,7 @@ function initGantt() {
gantt.init(ganttRef.value); gantt.init(ganttRef.value);
gantt.parse({ data: ganttTasks }); gantt.parse({ data: ganttTasks });
const zoomLevels: ZoomLevels[] = [ const zoomLevels: ZoomLevel[] = [
{ {
name: 'day', name: 'day',
scale_height: 60, scale_height: 60,

View File

@ -9,6 +9,8 @@ export default defineConfig(configEnv => {
const buildTime = getBuildTime(); const buildTime = getBuildTime();
const enableProxy = configEnv.command === 'serve' && !configEnv.isPreview;
return { return {
base: viteEnv.VITE_BASE_URL, base: viteEnv.VITE_BASE_URL,
resolve: { resolve: {
@ -32,7 +34,7 @@ export default defineConfig(configEnv => {
host: '0.0.0.0', host: '0.0.0.0',
port: 9527, port: 9527,
open: true, open: true,
proxy: createViteProxy(viteEnv, configEnv.command === 'serve'), proxy: createViteProxy(viteEnv, enableProxy),
fs: { fs: {
cachedChecks: false cachedChecks: false
} }