mirror of
				https://github.com/soybeanjs/soybean-admin.git
				synced 2025-10-22 17:43:42 +08:00 
			
		
		
		
	Compare commits
	
		
			24 Commits
		
	
	
		
			a013ea2c46
			...
			v1.3.15
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 87a675bf62 | ||
|  | 4d42dcbea8 | ||
|  | 276d836c87 | ||
|  | 7d84062e2c | ||
|  | afd604212b | ||
|  | fcb89883fa | ||
|  | dc674ce870 | ||
|  | dbd995c12c | ||
|  | 7b2e510a2f | ||
|  | da149e5bbd | ||
|  | 7c3dac4212 | ||
|  | 39b89a1234 | ||
|  | c57f88aad2 | ||
|  | 3e4e17abd8 | ||
|  | e6044d0fc7 | ||
|  | 2ed0b6484c | ||
|  | 222187d3b0 | ||
|  | 75455b006c | ||
|  | dfb647a82c | ||
|  | 7fb5c72f7e | ||
|  | 41b5f49341 | ||
|  | f35c250a89 | ||
|  | 1ff4d82d19 | ||
|  | c3abc3df09 | 
							
								
								
									
										96
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,6 +1,102 @@ | ||||
| # Changelog | ||||
|  | ||||
|  | ||||
| ## [v1.3.15](https://github.com/soybeanjs/soybean-admin/compare/v1.3.14...v1.3.15) (2025-06-24) | ||||
|  | ||||
| ###    🚀 Features | ||||
|  | ||||
| - **projects**: add configurable user name watermark option  -  by @wenyuanw [<samp>(7c3da)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7c3dac42) | ||||
|  | ||||
| ###    🐞 Bug Fixes | ||||
|  | ||||
| - **app**: replace console.error with window.console.error for consistency  -  by @soybeanjs [<samp>(7d840)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7d84062e) | ||||
| - **projects**: ensure proper text color when themes are inverted  -  by @wenyuanw [<samp>(afd60)</samp>](https://github.com/soybeanjs/soybean-admin/commit/afd60421) | ||||
| - **types**: The environment variable VITE_ICON_LOCAL_PREFIX has the wrong type.  -  by **chenziwen** [<samp>(da149)</samp>](https://github.com/soybeanjs/soybean-admin/commit/da149e5b) | ||||
|  | ||||
| ###    🛠 Optimizations | ||||
|  | ||||
| - **components**: optimize spacing for lang-switch dropdown options  -  by @wenyuanw [<samp>(fcb89)</samp>](https://github.com/soybeanjs/soybean-admin/commit/fcb89883) | ||||
|  | ||||
| ###    💅 Refactors | ||||
|  | ||||
| - **iframe-page**: remove unused lifecycle hooks and clean up script setup  -  by @soybeanjs [<samp>(276d8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/276d836c) | ||||
|  | ||||
| ###    📖 Documentation | ||||
|  | ||||
| - **other**: update docs with video tutorial link.  -  by **Azir** [<samp>(7b2e5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7b2e510a) | ||||
| - **readme**: add warning about upcoming `V2` version and link to plan list  -  by @soybeanjs [<samp>(4d42d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4d42dcbe) | ||||
|  | ||||
| ###    🏡 Chore | ||||
|  | ||||
| - **deps**: update deps  -  by @soybeanjs [<samp>(dc674)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dc674ce8) | ||||
| - **projects**: update deps & fix `moduleResolution`  -  by @soybeanjs [<samp>(dbd99)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dbd995c1) | ||||
|  | ||||
| ###    ❤️ Contributors | ||||
|  | ||||
| [](https://github.com/soybeanjs)  [](https://github.com/wenyuanw)   | ||||
| [Azir](mailto:2075125282@qq.com), [chenziwen](mailto:chenziwen@qesong.com) | ||||
|  | ||||
| ## [v1.3.14](https://github.com/soybeanjs/soybean-admin/compare/v1.3.13...v1.3.14) (2025-06-09) | ||||
|  | ||||
| ###    🚀 Features | ||||
|  | ||||
| - **docs**: | ||||
|   - add GitCode star badge to README files  -  by @soybeanjs [<samp>(05dc1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/05dc11e2) | ||||
|   - add DartNode sponsorship badge to README files  -  by @soybeanjs [<samp>(2ed0b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2ed0b648) | ||||
| - **projects**: | ||||
|   - support vite devtools specify the editor by launchEditor option.  -  by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/730 [<samp>(29698)</samp>](https://github.com/soybeanjs/soybean-admin/commit/29698bef) | ||||
|   - clear tabs cache when switching users.  -  by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/744 [<samp>(1ff4d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1ff4d82d) | ||||
| - **theme**: | ||||
|   - global search button toggle  -  by **t8y2** [<samp>(75455)</samp>](https://github.com/soybeanjs/soybean-admin/commit/75455b00) | ||||
| - **types**: | ||||
|   - enhance Option type to support customizable label types  -  by @WgoW and @testbrate in https://github.com/soybeanjs/soybean-admin/issues/735 [<samp>(123d2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/123d2c90) | ||||
| - **utils**: | ||||
|   - support quick generation of code templates.  -  by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/733 [<samp>(8527a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8527aa80) | ||||
|  | ||||
| ###    🐞 Bug Fixes | ||||
|  | ||||
| - **auth**: | ||||
|   - remove redundant authStore declaration in resetStore function  -  by @soybeanjs [<samp>(c57f8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c57f88aa) | ||||
| - **hooks**: | ||||
|   - fixed the issue where loading was not properly closed in some cases.  -  by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/737 [<samp>(85e40)</samp>](https://github.com/soybeanjs/soybean-admin/commit/85e40b19) | ||||
|   - refactor useCountDown hook for improved countdown logic and clarity.  -  by **Azir** [<samp>(dfb64)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dfb647a8) | ||||
| - **projects**: | ||||
|   - tab closure did not remove cache correctly.  -  by **Azir** [<samp>(7fb5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7fb5c72f) | ||||
|  | ||||
| ###    🛠 Optimizations | ||||
|  | ||||
| - **hooks**: | ||||
|   - remove obsolete disabling cache.  -  by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/729 [<samp>(4e1b6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4e1b65b6) | ||||
|   - update detection function to cover the exceptions that occur when the request fails.  -  by **恕瑞玛的皇帝** [<samp>(22218)</samp>](https://github.com/soybeanjs/soybean-admin/commit/222187d3) | ||||
| - **projects**: | ||||
|   - optimize tab deletion logic. closed #755  -  by @wenyuanw in https://github.com/soybeanjs/soybean-admin/issues/755 [<samp>(e6044)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e6044d0f) | ||||
|  | ||||
| ###    📖 Documentation | ||||
|  | ||||
| - **README**: | ||||
|   - Add supporting ecosystem tools to the open-source repository  -  by @WgoW and @testbrate in https://github.com/soybeanjs/soybean-admin/issues/740 [<samp>(a013e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a013ea2c) | ||||
| - **deps**: | ||||
|   - update the Vite version of the project description.  -  by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/732 [<samp>(80486)</samp>](https://github.com/soybeanjs/soybean-admin/commit/80486099) | ||||
| - **projects**: | ||||
|   - update README  -  by @xiatianYa in https://github.com/soybeanjs/soybean-admin/issues/726 [<samp>(3cbaf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3cbaf4f4) | ||||
|   - add gitcode link  -  by @soybeanjs [<samp>(f35c2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f35c250a) | ||||
|  | ||||
| ###    🏡 Chore | ||||
|  | ||||
| - **deps**: | ||||
|   - add vscode recommend plugin  close #738  -  by @tu6ge in https://github.com/soybeanjs/soybean-admin/issues/739 and https://github.com/soybeanjs/soybean-admin/issues/738 [<samp>(61244)</samp>](https://github.com/soybeanjs/soybean-admin/commit/61244f0f) | ||||
|   - update deps  -  by @soybeanjs [<samp>(41b5f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/41b5f493) | ||||
|   - update deps  -  by @soybeanjs [<samp>(3e4e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3e4e17ab) | ||||
|  | ||||
| ###    🤖 CI | ||||
|  | ||||
| - **hooks**: remove lint-staged in git hook.  close #724  -  by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/743 and https://github.com/soybeanjs/soybean-admin/issues/724 [<samp>(c3abc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c3abc3df) | ||||
|  | ||||
| ###    ❤️ Contributors | ||||
|  | ||||
| [](https://github.com/soybeanjs)  [](https://github.com/wenyuanw)  [](https://github.com/Azir-11)  [](https://github.com/WgoW)  [](https://github.com/testbrate)  [](https://github.com/tu6ge)  [](https://github.com/xiatianYa)   | ||||
| [恕瑞玛的皇帝](mailto:2075125282@qq.com), [t8y2](mailto:1156263951@qq.com),  | ||||
|  | ||||
| ## [v1.3.13](https://github.com/soybeanjs/soybean-admin/compare/v1.3.12...v1.3.13) (2025-03-19) | ||||
|  | ||||
| ###    🐞 Bug Fixes | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
| [](https://github.com/soybeanjs/soybean-admin) | ||||
| [](https://gitee.com/honghuangdc/soybean-admin) | ||||
| [](https://gitcode.com/soybeanjs/soybean-admin) | ||||
| [](https://dartnode.com "Powered by DartNode - Free VPS for Open Source") | ||||
|  | ||||
| <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> | ||||
|  | ||||
| @@ -18,6 +19,12 @@ | ||||
| > [!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! | ||||
|  | ||||
| > [!NOTE] | ||||
| > The `SoybeanAdmin` quick start series videos have been uploaded to [Bilibili](https://www.bilibili.com/video/BV1YKdRYXELC) Go online [click here](https://www.bilibili.com/video/BV1YKdRYXELC) Go check it out | ||||
|  | ||||
| > [!WARNING] | ||||
| > `SoybeanAdmin` is planning to develop a `V2` version, see [plan list](https://github.com/soybeanjs/soybean-admin/issues/767) | ||||
|  | ||||
| ## 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, Vite6, 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. | ||||
| @@ -44,19 +51,25 @@ | ||||
|   - [Preview Link](https://naive.soybeanjs.cn/) | ||||
|   - [Github Repository](https://github.com/soybeanjs/soybean-admin) | ||||
|   - [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin) | ||||
|   - [Gitcode Repository](https://gitcode.com/soybeanjs/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) | ||||
|   - [Gitcode Repository](https://gitcode.com/soybeanjs/soybean-admin-antd) | ||||
|  | ||||
| - **ElementPlus Version:** | ||||
|   - [Preview Link](https://elp.soybeanjs.cn/) | ||||
|   - [Github Repository](https://github.com/soybeanjs/soybean-admin-element-plus) | ||||
|   - [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin-element-plus) | ||||
|   - [Gitcode Repository](https://gitcode.com/soybeanjs/soybean-admin-element-plus) | ||||
|  | ||||
| - **Legacy Version:** | ||||
|   - [Preview Link](https://legacy.soybeanjs.cn/) | ||||
|   - [Github Repository](https://github.com/soybeanjs/soybean-admin/tree/legacy) | ||||
|   - [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin/tree/legacy) | ||||
|   - [Gitcode Repository](https://gitcode.com/soybeanjs/soybean-admin/tree/legacy) | ||||
|  | ||||
|  | ||||
| ## Documentation | ||||
| @@ -92,7 +105,12 @@ Make sure your environment meets the following requirements: | ||||
| **Clone Project** | ||||
|  | ||||
| ```bash | ||||
| # github | ||||
| git clone https://github.com/soybeanjs/soybean-admin.git | ||||
| # gitee | ||||
| git clone https://gitee.com/honghuangdc/soybean-admin.git | ||||
| # gitcode | ||||
| git clone https://gitcode.com/soybeanjs/soybean-admin.git | ||||
| ``` | ||||
|  | ||||
| **Install Dependencies** | ||||
|   | ||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
								
							| @@ -11,12 +11,19 @@ | ||||
| [](https://github.com/soybeanjs/soybean-admin) | ||||
| [](https://gitee.com/honghuangdc/soybean-admin) | ||||
| [](https://gitcode.com/soybeanjs/soybean-admin) | ||||
| [](https://dartnode.com "Powered by DartNode - Free VPS for Open Source") | ||||
|  | ||||
| <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 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持! | ||||
|  | ||||
| > [!NOTE] | ||||
| > `SoybeanAdmin` 快速上手系列视频已在 [Bilibili](https://www.bilibili.com/video/BV1YKdRYXELC) 上线 [点击这里](https://www.bilibili.com/video/BV1YKdRYXELC) 前往查看 | ||||
|  | ||||
| > [!WARNING] | ||||
| > `SoybeanAdmin` 正在计划开发 `V2` 版本,详情见[计划清单](https://github.com/soybeanjs/soybean-admin/issues/767) | ||||
|  | ||||
| ## 简介 | ||||
|  | ||||
| [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite6, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件,代码规范严谨,实现了自动化的文件路由系统。此外,它还采用了基于 ApiFox 的在线Mock数据方案。`SoybeanAdmin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。 | ||||
| @@ -42,16 +49,22 @@ | ||||
|   - [预览地址](https://naive.soybeanjs.cn/) | ||||
|   - [Github 仓库](https://github.com/soybeanjs/soybean-admin) | ||||
|   - [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin) | ||||
|   - [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin) | ||||
| - **AntDesignVue 版本:** | ||||
|   - [预览地址](https://antd.soybeanjs.cn/) | ||||
|   - [Github 仓库](https://github.com/soybeanjs/soybean-admin-antd) | ||||
|   - [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin-antd) | ||||
|   - [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin-antd) | ||||
| - **ElementPlus 版本:** | ||||
|   - [预览地址](https://elp.soybeanjs.cn/) | ||||
|   - [Github 仓库](https://github.com/soybeanjs/soybean-admin-element-plus) | ||||
|   - [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin-element-plus) | ||||
|   - [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin-element-plus) | ||||
| - **旧版:** | ||||
|   - [预览地址](https://legacy.soybeanjs.cn/) | ||||
|   - [Github 仓库](https://github.com/soybeanjs/soybean-admin/tree/legacy) | ||||
|   - [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin/tree/legacy) | ||||
|   - [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin/tree/legacy) | ||||
|  | ||||
|  | ||||
| ## 文档 | ||||
| @@ -117,7 +130,12 @@ | ||||
| **克隆项目** | ||||
|  | ||||
| ```bash | ||||
| # github | ||||
| git clone https://github.com/soybeanjs/soybean-admin.git | ||||
| # gitee | ||||
| git clone https://gitee.com/honghuangdc/soybean-admin.git | ||||
| # gitcode | ||||
| git clone https://gitcode.com/soybeanjs/soybean-admin.git | ||||
| ``` | ||||
|  | ||||
| **安装依赖** | ||||
|   | ||||
							
								
								
									
										68
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "soybean-admin", | ||||
|   "type": "module", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "description": "A fresh and elegant admin template, based on Vue3、Vite6、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite6、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。", | ||||
|   "author": { | ||||
|     "name": "Soybean", | ||||
| @@ -48,67 +48,63 @@ | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@better-scroll/core": "2.5.1", | ||||
|     "@iconify/vue": "4.3.0", | ||||
|     "@iconify/vue": "5.0.0", | ||||
|     "@sa/axios": "workspace:*", | ||||
|     "@sa/color": "workspace:*", | ||||
|     "@sa/hooks": "workspace:*", | ||||
|     "@sa/materials": "workspace:*", | ||||
|     "@sa/utils": "workspace:*", | ||||
|     "@vueuse/core": "13.0.0", | ||||
|     "@vueuse/core": "13.4.0", | ||||
|     "clipboard": "2.0.11", | ||||
|     "dayjs": "1.11.13", | ||||
|     "defu": "6.1.4", | ||||
|     "echarts": "5.6.0", | ||||
|     "json5": "2.2.3", | ||||
|     "naive-ui": "2.41.0", | ||||
|     "naive-ui": "2.42.0", | ||||
|     "nprogress": "0.2.0", | ||||
|     "pinia": "3.0.1", | ||||
|     "tailwind-merge": "3.0.2", | ||||
|     "vue": "3.5.13", | ||||
|     "pinia": "3.0.3", | ||||
|     "tailwind-merge": "3.3.1", | ||||
|     "vue": "3.5.17", | ||||
|     "vue-draggable-plus": "0.6.0", | ||||
|     "vue-i18n": "11.1.2", | ||||
|     "vue-router": "4.5.0" | ||||
|     "vue-i18n": "11.1.7", | ||||
|     "vue-router": "4.5.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@elegant-router/vue": "0.3.8", | ||||
|     "@iconify/json": "2.2.318", | ||||
|     "@iconify/json": "2.2.352", | ||||
|     "@sa/scripts": "workspace:*", | ||||
|     "@sa/uno-preset": "workspace:*", | ||||
|     "@soybeanjs/eslint-config": "1.6.0", | ||||
|     "@types/node": "22.13.10", | ||||
|     "@soybeanjs/eslint-config": "1.6.1", | ||||
|     "@types/node": "24.0.3", | ||||
|     "@types/nprogress": "0.2.3", | ||||
|     "@unocss/eslint-config": "66.0.0", | ||||
|     "@unocss/preset-icons": "66.0.0", | ||||
|     "@unocss/preset-uno": "66.0.0", | ||||
|     "@unocss/transformer-directives": "66.0.0", | ||||
|     "@unocss/transformer-variant-group": "66.0.0", | ||||
|     "@unocss/vite": "66.0.0", | ||||
|     "@vitejs/plugin-vue": "5.2.3", | ||||
|     "@vitejs/plugin-vue-jsx": "4.1.2", | ||||
|     "@unocss/eslint-config": "66.2.3", | ||||
|     "@unocss/preset-icons": "66.2.3", | ||||
|     "@unocss/preset-uno": "66.2.3", | ||||
|     "@unocss/transformer-directives": "66.2.3", | ||||
|     "@unocss/transformer-variant-group": "66.2.3", | ||||
|     "@unocss/vite": "66.2.3", | ||||
|     "@vitejs/plugin-vue": "6.0.0", | ||||
|     "@vitejs/plugin-vue-jsx": "5.0.0", | ||||
|     "consola": "3.4.2", | ||||
|     "eslint": "9.22.0", | ||||
|     "eslint-plugin-vue": "10.0.0", | ||||
|     "eslint": "9.29.0", | ||||
|     "eslint-plugin-vue": "10.2.0", | ||||
|     "kolorist": "1.8.0", | ||||
|     "lint-staged": "15.5.0", | ||||
|     "sass": "1.86.0", | ||||
|     "simple-git-hooks": "2.11.1", | ||||
|     "tsx": "4.19.3", | ||||
|     "typescript": "5.8.2", | ||||
|     "sass": "1.89.2", | ||||
|     "simple-git-hooks": "2.13.0", | ||||
|     "tsx": "4.20.3", | ||||
|     "typescript": "5.8.3", | ||||
|     "unplugin-icons": "22.1.0", | ||||
|     "unplugin-vue-components": "28.4.1", | ||||
|     "vite": "6.2.2", | ||||
|     "unplugin-vue-components": "28.7.0", | ||||
|     "vite": "7.0.0", | ||||
|     "vite-plugin-progress": "0.0.7", | ||||
|     "vite-plugin-svg-icons": "2.0.1", | ||||
|     "vite-plugin-vue-devtools": "7.7.2", | ||||
|     "vue-eslint-parser": "10.1.1", | ||||
|     "vue-tsc": "2.2.8" | ||||
|     "vite-plugin-vue-devtools": "7.7.7", | ||||
|     "vue-eslint-parser": "10.1.4", | ||||
|     "vue-tsc": "2.2.10" | ||||
|   }, | ||||
|   "simple-git-hooks": { | ||||
|     "commit-msg": "pnpm sa git-commit-verify", | ||||
|     "pre-commit": "pnpm typecheck && pnpm lint-staged" | ||||
|   }, | ||||
|   "lint-staged": { | ||||
|     "*": "eslint --fix" | ||||
|     "pre-commit": "pnpm typecheck && pnpm lint && git diff --exit-code" | ||||
|   }, | ||||
|   "website": "https://admin.soybeanjs.cn" | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@sa/alova", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "exports": { | ||||
|     ".": "./src/index.ts", | ||||
|     "./fetch": "./src/fetch.ts", | ||||
| @@ -13,8 +13,8 @@ | ||||
|     } | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@alova/mock": "2.0.12", | ||||
|     "@alova/mock": "2.0.17", | ||||
|     "@sa/utils": "workspace:*", | ||||
|     "alova": "3.2.10" | ||||
|     "alova": "3.3.3" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@sa/axios", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "exports": { | ||||
|     ".": "./src/index.ts" | ||||
|   }, | ||||
| @@ -11,11 +11,11 @@ | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@sa/utils": "workspace:*", | ||||
|     "axios": "1.8.3", | ||||
|     "axios": "1.10.0", | ||||
|     "axios-retry": "4.5.0", | ||||
|     "qs": "6.14.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/qs": "6.9.18" | ||||
|     "@types/qs": "6.14.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@sa/color", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "exports": { | ||||
|     ".": "./src/index.ts" | ||||
|   }, | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@sa/hooks", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "exports": { | ||||
|     ".": "./src/index.ts" | ||||
|   }, | ||||
|   | ||||
| @@ -2,40 +2,59 @@ import { computed, onScopeDispose, ref } from 'vue'; | ||||
| import { useRafFn } from '@vueuse/core'; | ||||
|  | ||||
| /** | ||||
|  * count down | ||||
|  * A hook for implementing a countdown timer. It uses `requestAnimationFrame` for smooth and accurate timing, | ||||
|  * independent of the screen refresh rate. | ||||
|  * | ||||
|  * @param seconds - count down seconds | ||||
|  * @param initialSeconds - The total number of seconds for the countdown. | ||||
|  */ | ||||
| export default function useCountDown(seconds: number) { | ||||
|   const FPS_PER_SECOND = 60; | ||||
| export default function useCountDown(initialSeconds: number) { | ||||
|   const remainingSeconds = ref(0); | ||||
|  | ||||
|   const fps = ref(0); | ||||
|   const count = computed(() => Math.ceil(remainingSeconds.value)); | ||||
|  | ||||
|   const count = computed(() => Math.ceil(fps.value / FPS_PER_SECOND)); | ||||
|  | ||||
|   const isCounting = computed(() => fps.value > 0); | ||||
|   const isCounting = computed(() => remainingSeconds.value > 0); | ||||
|  | ||||
|   const { pause, resume } = useRafFn( | ||||
|     () => { | ||||
|       if (fps.value > 0) { | ||||
|         fps.value -= 1; | ||||
|       } else { | ||||
|     ({ delta }) => { | ||||
|       // delta: milliseconds elapsed since the last frame. | ||||
|  | ||||
|       // If countdown already reached zero or below, ensure it's 0 and stop. | ||||
|       if (remainingSeconds.value <= 0) { | ||||
|         remainingSeconds.value = 0; | ||||
|         pause(); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       // Calculate seconds passed since the last frame. | ||||
|       const secondsPassed = delta / 1000; | ||||
|       remainingSeconds.value -= secondsPassed; | ||||
|  | ||||
|       // If countdown has finished after decrementing. | ||||
|       if (remainingSeconds.value <= 0) { | ||||
|         remainingSeconds.value = 0; | ||||
|         pause(); | ||||
|       } | ||||
|     }, | ||||
|     { immediate: false } | ||||
|     { immediate: false } // The timer does not start automatically. | ||||
|   ); | ||||
|  | ||||
|   function start(updateSeconds: number = seconds) { | ||||
|     fps.value = FPS_PER_SECOND * updateSeconds; | ||||
|   /** | ||||
|    * Starts the countdown. | ||||
|    * | ||||
|    * @param [updatedSeconds=initialSeconds] - Optionally, start with a new duration. Default is `initialSeconds` | ||||
|    */ | ||||
|   function start(updatedSeconds: number = initialSeconds) { | ||||
|     remainingSeconds.value = updatedSeconds; | ||||
|     resume(); | ||||
|   } | ||||
|  | ||||
|   /** Stops the countdown and resets the remaining time to 0. */ | ||||
|   function stop() { | ||||
|     fps.value = 0; | ||||
|     remainingSeconds.value = 0; | ||||
|     pause(); | ||||
|   } | ||||
|  | ||||
|   // Ensure the rAF loop is cleaned up when the component is unmounted. | ||||
|   onScopeDispose(() => { | ||||
|     pause(); | ||||
|   }); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@sa/materials", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "exports": { | ||||
|     ".": "./src/index.ts" | ||||
|   }, | ||||
| @@ -11,7 +11,7 @@ | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@sa/utils": "workspace:*", | ||||
|     "simplebar-vue": "2.4.0" | ||||
|     "simplebar-vue": "2.4.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "typed-css-modules": "0.9.1" | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@sa/fetch", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "exports": { | ||||
|     ".": "./src/index.ts" | ||||
|   }, | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@sa/scripts", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "bin": { | ||||
|     "sa": "./bin.ts" | ||||
|   }, | ||||
| @@ -14,14 +14,14 @@ | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@soybeanjs/changelog": "0.3.24", | ||||
|     "bumpp": "10.1.0", | ||||
|     "c12": "3.0.2", | ||||
|     "bumpp": "10.2.0", | ||||
|     "c12": "3.0.4", | ||||
|     "cac": "6.7.14", | ||||
|     "consola": "3.4.2", | ||||
|     "enquirer": "2.4.1", | ||||
|     "execa": "9.5.2", | ||||
|     "execa": "9.6.0", | ||||
|     "kolorist": "1.8.0", | ||||
|     "npm-check-updates": "17.1.15", | ||||
|     "npm-check-updates": "18.0.1", | ||||
|     "rimraf": "6.0.1" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@sa/uno-preset", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "exports": { | ||||
|     ".": "./src/index.ts" | ||||
|   }, | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@sa/utils", | ||||
|   "version": "1.3.13", | ||||
|   "version": "1.3.15", | ||||
|   "exports": { | ||||
|     ".": "./src/index.ts" | ||||
|   }, | ||||
|   | ||||
							
								
								
									
										3406
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3406
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -4,6 +4,7 @@ import { NConfigProvider, darkTheme } from 'naive-ui'; | ||||
| import type { WatermarkProps } from 'naive-ui'; | ||||
| import { useAppStore } from './store/modules/app'; | ||||
| import { useThemeStore } from './store/modules/theme'; | ||||
| import { useAuthStore } from './store/modules/auth'; | ||||
| import { naiveDateLocales, naiveLocales } from './locales/naive'; | ||||
|  | ||||
| defineOptions({ | ||||
| @@ -12,6 +13,7 @@ defineOptions({ | ||||
|  | ||||
| const appStore = useAppStore(); | ||||
| const themeStore = useThemeStore(); | ||||
| const authStore = useAuthStore(); | ||||
|  | ||||
| const naiveDarkTheme = computed(() => (themeStore.darkMode ? darkTheme : undefined)); | ||||
|  | ||||
| @@ -24,8 +26,13 @@ const naiveDateLocale = computed(() => { | ||||
| }); | ||||
|  | ||||
| const watermarkProps = computed<WatermarkProps>(() => { | ||||
|   const content = | ||||
|     themeStore.watermark.enableUserName && authStore.userInfo.userName | ||||
|       ? authStore.userInfo.userName | ||||
|       : themeStore.watermark.text; | ||||
|  | ||||
|   return { | ||||
|     content: themeStore.watermark.text, | ||||
|     content, | ||||
|     cross: true, | ||||
|     fullscreen: true, | ||||
|     fontSize: 16, | ||||
|   | ||||
| @@ -31,13 +31,25 @@ const tooltipContent = computed(() => { | ||||
|   return $t('icon.lang'); | ||||
| }); | ||||
|  | ||||
| /** Add bottom margin to all options except the last one for proper visual separation */ | ||||
| const dropdownOptions = computed(() => { | ||||
|   const lastIndex = props.langOptions.length - 1; | ||||
|  | ||||
|   return props.langOptions.map((option, index) => ({ | ||||
|     ...option, | ||||
|     props: { | ||||
|       class: index < lastIndex ? 'mb-1' : undefined | ||||
|     } | ||||
|   })); | ||||
| }); | ||||
|  | ||||
| function changeLang(lang: App.I18n.LangType) { | ||||
|   emit('changeLang', lang); | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <NDropdown :value="lang" :options="langOptions" trigger="hover" @select="changeLang"> | ||||
|   <NDropdown :value="lang" :options="dropdownOptions" trigger="hover" @select="changeLang"> | ||||
|     <div> | ||||
|       <ButtonIcon :tooltip-content="tooltipContent" tooltip-placement="left"> | ||||
|         <SvgIcon icon="heroicons:language" /> | ||||
|   | ||||
| @@ -38,7 +38,7 @@ const { isFullscreen, toggle } = useFullscreen(); | ||||
|       <GlobalBreadcrumb v-if="!appStore.isMobile" class="ml-12px" /> | ||||
|     </div> | ||||
|     <div class="h-full flex-y-center justify-end"> | ||||
|       <GlobalSearch /> | ||||
|       <GlobalSearch v-if="themeStore.header.globalSearch.visible" /> | ||||
|       <FullScreen v-if="!appStore.isMobile" :full="isFullscreen" @click="toggle" /> | ||||
|       <LangSwitch | ||||
|         v-if="themeStore.header.multilingual.visible" | ||||
|   | ||||
| @@ -5,7 +5,6 @@ import { useElementBounding } from '@vueuse/core'; | ||||
| import { PageTab } from '@sa/materials'; | ||||
| import { useAppStore } from '@/store/modules/app'; | ||||
| import { useThemeStore } from '@/store/modules/theme'; | ||||
| import { useRouteStore } from '@/store/modules/route'; | ||||
| import { useTabStore } from '@/store/modules/tab'; | ||||
| import { isPC } from '@/utils/agent'; | ||||
| import BetterScroll from '@/components/custom/better-scroll.vue'; | ||||
| @@ -18,7 +17,6 @@ defineOptions({ | ||||
| const route = useRoute(); | ||||
| const appStore = useAppStore(); | ||||
| const themeStore = useThemeStore(); | ||||
| const routeStore = useRouteStore(); | ||||
| const tabStore = useTabStore(); | ||||
|  | ||||
| const bsWrapper = ref<HTMLElement>(); | ||||
| @@ -82,12 +80,8 @@ function getContextMenuDisabledKeys(tabId: string) { | ||||
|   return disabledKeys; | ||||
| } | ||||
|  | ||||
| async function handleCloseTab(tab: App.Global.Tab) { | ||||
|   await tabStore.removeTab(tab.id); | ||||
|  | ||||
|   if (themeStore.resetCacheStrategy === 'close') { | ||||
|     routeStore.resetRouteCache(tab.routeKey); | ||||
|   } | ||||
| function handleCloseTab(tab: App.Global.Tab) { | ||||
|   tabStore.removeTab(tab.id); | ||||
| } | ||||
|  | ||||
| async function refresh() { | ||||
|   | ||||
| @@ -117,7 +117,10 @@ const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wra | ||||
|     <SettingItem key="8" :label="$t('theme.watermark.visible')"> | ||||
|       <NSwitch v-model:value="themeStore.watermark.visible" /> | ||||
|     </SettingItem> | ||||
|     <SettingItem v-if="themeStore.watermark.visible" key="8-1" :label="$t('theme.watermark.text')"> | ||||
|     <SettingItem v-if="themeStore.watermark.visible" key="8-1" :label="$t('theme.watermark.enableUserName')"> | ||||
|       <NSwitch v-model:value="themeStore.watermark.enableUserName" /> | ||||
|     </SettingItem> | ||||
|     <SettingItem v-if="themeStore.watermark.visible" key="8-2" :label="$t('theme.watermark.text')"> | ||||
|       <NInput | ||||
|         v-model:value="themeStore.watermark.text" | ||||
|         autosize | ||||
| @@ -130,6 +133,9 @@ const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wra | ||||
|     <SettingItem key="9" :label="$t('theme.header.multilingual.visible')"> | ||||
|       <NSwitch v-model:value="themeStore.header.multilingual.visible" /> | ||||
|     </SettingItem> | ||||
|     <SettingItem key="10" :label="$t('theme.header.globalSearch.visible')"> | ||||
|       <NSwitch v-model:value="themeStore.header.globalSearch.visible" /> | ||||
|     </SettingItem> | ||||
|   </TransitionGroup> | ||||
| </template> | ||||
|  | ||||
|   | ||||
| @@ -112,6 +112,9 @@ const local: App.I18n.Schema = { | ||||
|       }, | ||||
|       multilingual: { | ||||
|         visible: 'Display multilingual button' | ||||
|       }, | ||||
|       globalSearch: { | ||||
|         visible: 'Display GlobalSearch button' | ||||
|       } | ||||
|     }, | ||||
|     tab: { | ||||
| @@ -140,7 +143,8 @@ const local: App.I18n.Schema = { | ||||
|     }, | ||||
|     watermark: { | ||||
|       visible: 'Watermark Full Screen Visible', | ||||
|       text: 'Watermark Text' | ||||
|       text: 'Watermark Text', | ||||
|       enableUserName: 'Enable User Name Watermark' | ||||
|     }, | ||||
|     themeDrawerTitle: 'Theme Configuration', | ||||
|     pageFunTitle: 'Page Function', | ||||
|   | ||||
| @@ -112,6 +112,9 @@ const local: App.I18n.Schema = { | ||||
|       }, | ||||
|       multilingual: { | ||||
|         visible: '显示多语言按钮' | ||||
|       }, | ||||
|       globalSearch: { | ||||
|         visible: '显示全局搜索按钮' | ||||
|       } | ||||
|     }, | ||||
|     tab: { | ||||
| @@ -140,7 +143,8 @@ const local: App.I18n.Schema = { | ||||
|     }, | ||||
|     watermark: { | ||||
|       visible: '显示全屏水印', | ||||
|       text: '水印文本' | ||||
|       text: '水印文本', | ||||
|       enableUserName: '启用用户名水印' | ||||
|     }, | ||||
|     themeDrawerTitle: '主题配置', | ||||
|     pageFunTitle: '页面功能', | ||||
|   | ||||
| @@ -25,8 +25,8 @@ export function setupAppVersionNotification() { | ||||
|  | ||||
|     const buildTime = await getHtmlBuildTime(); | ||||
|  | ||||
|     // If build time hasn't changed, no update is needed | ||||
|     if (buildTime === BUILD_TIME) { | ||||
|     // If failed to get build time or build time hasn't changed, no update is needed. | ||||
|     if (!buildTime || buildTime === BUILD_TIME) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| @@ -88,16 +88,21 @@ export function setupAppVersionNotification() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function getHtmlBuildTime() { | ||||
| async function getHtmlBuildTime(): Promise<string | null> { | ||||
|   const baseUrl = import.meta.env.VITE_BASE_URL || '/'; | ||||
|  | ||||
|   const res = await fetch(`${baseUrl}index.html?time=${Date.now()}`); | ||||
|   try { | ||||
|     const res = await fetch(`${baseUrl}index.html?time=${Date.now()}`); | ||||
|  | ||||
|   const html = await res.text(); | ||||
|     if (!res.ok) { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|   const match = html.match(/<meta name="buildTime" content="(.*)">/); | ||||
|  | ||||
|   const buildTime = match?.[1] || ''; | ||||
|  | ||||
|   return buildTime; | ||||
|     const html = await res.text(); | ||||
|     const match = html.match(/<meta name="buildTime" content="(.*)">/); | ||||
|     return match?.[1] || null; | ||||
|   } catch (error) { | ||||
|     window.console.error('getHtmlBuildTime error:', error); | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import { clearAuthStorage, getToken } from './shared'; | ||||
|  | ||||
| export const useAuthStore = defineStore(SetupStoreId.Auth, () => { | ||||
|   const route = useRoute(); | ||||
|   const authStore = useAuthStore(); | ||||
|   const routeStore = useRouteStore(); | ||||
|   const tabStore = useTabStore(); | ||||
|   const { toLogin, redirectFromLogin } = useRouterPush(false); | ||||
| @@ -39,7 +40,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => { | ||||
|  | ||||
|   /** Reset auth store */ | ||||
|   async function resetStore() { | ||||
|     const authStore = useAuthStore(); | ||||
|     recordUserId(); | ||||
|  | ||||
|     clearAuthStorage(); | ||||
|  | ||||
| @@ -53,6 +54,41 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => { | ||||
|     routeStore.resetStore(); | ||||
|   } | ||||
|  | ||||
|   /** Record the user ID of the previous login session Used to compare with the current user ID on next login */ | ||||
|   function recordUserId() { | ||||
|     if (!userInfo.userId) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // Store current user ID locally for next login comparison | ||||
|     localStg.set('lastLoginUserId', userInfo.userId); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Check if current login user is different from previous login user If different, clear all tabs | ||||
|    * | ||||
|    * @returns {boolean} Whether to clear all tabs | ||||
|    */ | ||||
|   function checkTabClear(): boolean { | ||||
|     if (!userInfo.userId) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     const lastLoginUserId = localStg.get('lastLoginUserId'); | ||||
|  | ||||
|     // Clear all tabs if current user is different from previous user | ||||
|     if (!lastLoginUserId || lastLoginUserId !== userInfo.userId) { | ||||
|       localStg.remove('globalTabs'); | ||||
|       tabStore.clearTabs(); | ||||
|  | ||||
|       localStg.remove('lastLoginUserId'); | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     localStg.remove('lastLoginUserId'); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Login | ||||
|    * | ||||
| @@ -69,7 +105,15 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => { | ||||
|       const pass = await loginByToken(loginToken); | ||||
|  | ||||
|       if (pass) { | ||||
|         await redirectFromLogin(redirect); | ||||
|         // Check if the tab needs to be cleared | ||||
|         const isClear = checkTabClear(); | ||||
|         let needRedirect = redirect; | ||||
|  | ||||
|         if (isClear) { | ||||
|           // If the tab needs to be cleared,it means we don't need to redirect. | ||||
|           needRedirect = false; | ||||
|         } | ||||
|         await redirectFromLogin(needRedirect); | ||||
|  | ||||
|         window.$notification?.success({ | ||||
|           title: $t('page.login.common.loginSuccess'), | ||||
|   | ||||
| @@ -98,13 +98,24 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => { | ||||
|     const removeTabIndex = tabs.value.findIndex(tab => tab.id === tabId); | ||||
|     if (removeTabIndex === -1) return; | ||||
|  | ||||
|     const removedTabRouteKey = tabs.value[removeTabIndex].routeKey; | ||||
|     const isRemoveActiveTab = activeTabId.value === tabId; | ||||
|     const nextTab = tabs.value[removeTabIndex + 1] || homeTab.value; | ||||
|  | ||||
|     // if remove the last tab, then switch to the second last tab | ||||
|     const nextTab = tabs.value[removeTabIndex + 1] || tabs.value[removeTabIndex - 1] || homeTab.value; | ||||
|  | ||||
|     // remove tab | ||||
|     tabs.value.splice(removeTabIndex, 1); | ||||
|  | ||||
|     // if current tab is removed, then switch to next tab | ||||
|     if (isRemoveActiveTab && nextTab) { | ||||
|       await switchRouteByTab(nextTab); | ||||
|     } | ||||
|  | ||||
|     // reset route cache if cache strategy is close | ||||
|     if (themeStore.resetCacheStrategy === 'close') { | ||||
|       routeStore.resetRouteCache(removedTabRouteKey); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** remove active tab */ | ||||
| @@ -131,9 +142,26 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => { | ||||
|    */ | ||||
|   async function clearTabs(excludes: string[] = []) { | ||||
|     const remainTabIds = [...getFixedTabIds(tabs.value), ...excludes]; | ||||
|     const removedTabsIds = tabs.value.map(tab => tab.id).filter(id => !remainTabIds.includes(id)); | ||||
|  | ||||
|     // Identify tabs to be removed and collect their routeKeys if strategy is 'close' | ||||
|     const tabsToRemove = tabs.value.filter(tab => !remainTabIds.includes(tab.id)); | ||||
|     const routeKeysToReset: RouteKey[] = []; | ||||
|  | ||||
|     if (themeStore.resetCacheStrategy === 'close') { | ||||
|       for (const tab of tabsToRemove) { | ||||
|         routeKeysToReset.push(tab.routeKey); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     const removedTabsIds = tabsToRemove.map(tab => tab.id); | ||||
|  | ||||
|     // If no tabs are actually being removed based on excludes and fixed tabs, exit | ||||
|     if (removedTabsIds.length === 0) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const isRemoveActiveTab = removedTabsIds.includes(activeTabId.value); | ||||
|     // filterTabsByIds returns tabs NOT in removedTabsIds, so these are the tabs that will remain | ||||
|     const updatedTabs = filterTabsByIds(removedTabsIds, tabs.value); | ||||
|  | ||||
|     function update() { | ||||
| @@ -142,13 +170,21 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => { | ||||
|  | ||||
|     if (!isRemoveActiveTab) { | ||||
|       update(); | ||||
|       return; | ||||
|     } else { | ||||
|       const activeTabCandidate = updatedTabs[updatedTabs.length - 1] || homeTab.value; | ||||
|  | ||||
|       if (activeTabCandidate) { | ||||
|         // Ensure there's a tab to switch to | ||||
|         await switchRouteByTab(activeTabCandidate); | ||||
|       } | ||||
|       // Update the tabs array regardless of switch success or if a candidate was found | ||||
|       update(); | ||||
|     } | ||||
|  | ||||
|     const activeTab = updatedTabs[updatedTabs.length - 1] || homeTab.value; | ||||
|  | ||||
|     await switchRouteByTab(activeTab); | ||||
|     update(); | ||||
|     // After tabs are updated and route potentially switched, reset cache for removed tabs | ||||
|     for (const routeKey of routeKeysToReset) { | ||||
|       routeStore.resetRouteCache(routeKey); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   const { routerPushByKey } = useRouterPush(); | ||||
|   | ||||
| @@ -10,4 +10,5 @@ body, | ||||
|  | ||||
| html { | ||||
|   overflow-x: hidden; | ||||
|   color: rgb(var(--base-text-color)); | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,9 @@ export const themeSettings: App.Theme.ThemeSetting = { | ||||
|     }, | ||||
|     multilingual: { | ||||
|       visible: true | ||||
|     }, | ||||
|     globalSearch: { | ||||
|       visible: true | ||||
|     } | ||||
|   }, | ||||
|   tab: { | ||||
| @@ -55,7 +58,8 @@ export const themeSettings: App.Theme.ThemeSetting = { | ||||
|   }, | ||||
|   watermark: { | ||||
|     visible: false, | ||||
|     text: 'SoybeanAdmin' | ||||
|     text: 'SoybeanAdmin', | ||||
|     enableUserName: false | ||||
|   }, | ||||
|   tokens: { | ||||
|     light: { | ||||
|   | ||||
							
								
								
									
										10
									
								
								src/typings/app.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								src/typings/app.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -58,6 +58,10 @@ declare namespace App { | ||||
|           /** Whether to show the multilingual */ | ||||
|           visible: boolean; | ||||
|         }; | ||||
|         globalSearch: { | ||||
|           /** Whether to show the GlobalSearch */ | ||||
|           visible: boolean; | ||||
|         }; | ||||
|       }; | ||||
|       /** Tab */ | ||||
|       tab: { | ||||
| @@ -108,6 +112,8 @@ declare namespace App { | ||||
|         visible: boolean; | ||||
|         /** Watermark text */ | ||||
|         text: string; | ||||
|         /** Whether to use user name as watermark text */ | ||||
|         enableUserName: boolean; | ||||
|       }; | ||||
|       /** define some theme settings tokens, will transform to css variables */ | ||||
|       tokens: { | ||||
| @@ -377,6 +383,9 @@ declare namespace App { | ||||
|           multilingual: { | ||||
|             visible: string; | ||||
|           }; | ||||
|           globalSearch: { | ||||
|             visible: string; | ||||
|           }; | ||||
|         }; | ||||
|         tab: { | ||||
|           visible: string; | ||||
| @@ -401,6 +410,7 @@ declare namespace App { | ||||
|         watermark: { | ||||
|           visible: string; | ||||
|           text: string; | ||||
|           enableUserName: string; | ||||
|         }; | ||||
|         themeDrawerTitle: string; | ||||
|         pageFunTitle: string; | ||||
|   | ||||
							
								
								
									
										2
									
								
								src/typings/storage.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/typings/storage.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -37,5 +37,7 @@ declare namespace StorageType { | ||||
|       layout: UnionKey.ThemeLayoutMode; | ||||
|       siderCollapse: boolean; | ||||
|     }; | ||||
|     /** The last login user id */ | ||||
|     lastLoginUserId: string; | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								src/typings/vite-env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/typings/vite-env.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -25,7 +25,7 @@ declare namespace Env { | ||||
|      * | ||||
|      * This prefix is start with the icon prefix | ||||
|      */ | ||||
|     readonly VITE_ICON_LOCAL_PREFIX: 'local-icon'; | ||||
|     readonly VITE_ICON_LOCAL_PREFIX: 'icon-local'; | ||||
|     /** backend service base url */ | ||||
|     readonly VITE_SERVICE_BASE_URL: string; | ||||
|     /** | ||||
|   | ||||
| @@ -1,19 +1,9 @@ | ||||
| <script setup lang="ts"> | ||||
| import { onActivated, onMounted } from 'vue'; | ||||
|  | ||||
| interface Props { | ||||
|   url: string; | ||||
| } | ||||
|  | ||||
| defineProps<Props>(); | ||||
|  | ||||
| onMounted(() => { | ||||
|   console.log('mounted'); | ||||
| }); | ||||
|  | ||||
| onActivated(() => { | ||||
|   console.log('activated'); | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|     "lib": ["DOM", "ESNext"], | ||||
|     "baseUrl": ".", | ||||
|     "module": "ESNext", | ||||
|     "moduleResolution": "node", | ||||
|     "moduleResolution": "bundler", | ||||
|     "paths": { | ||||
|       "@/*": ["./src/*"], | ||||
|       "~/*": ["./*"] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user