mirror of
				https://github.com/soybeanjs/soybean-admin.git
				synced 2025-10-31 22:03:41 +08:00 
			
		
		
		
	Compare commits
	
		
			26 Commits
		
	
	
		
			v2.0-route
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 6010f51801 | ||
|  | 9755c31361 | ||
|  | 0b9982bdc6 | ||
|  | 1d14269557 | ||
|  | 5cfb606042 | ||
|  | e471e9140e | ||
|  | b2c919b673 | ||
|  | 8dc17e62f1 | ||
|  | 60115572a8 | ||
|  | 56ad0df974 | ||
|  | 6cbf5705bb | ||
|  | 87a66a4236 | ||
|  | 3549c4dbd5 | ||
|  | 24c6df528b | ||
|  | dac5075be9 | ||
|  | a8d1e5d266 | ||
|  | 345aa2932f | ||
|  | 017440c1e6 | ||
|  | e18d39724a | ||
|  | fd087f59fa | ||
|  | 9fa951aa06 | ||
|  | 12b25e0d58 | ||
|  | e33f944a74 | ||
|  | 8d7f91dccf | ||
|  | 33ade53904 | ||
|  | 923eb98a5c | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -33,5 +33,3 @@ package-lock.json | |||||||
| yarn.lock | yarn.lock | ||||||
|  |  | ||||||
| .VSCodeCounter | .VSCodeCounter | ||||||
|  |  | ||||||
| .temp |  | ||||||
							
								
								
									
										146
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,6 +1,152 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## [v2.0.0-beta.2](https://github.com/soybeanjs/soybean-admin/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2025-10-27) | ||||||
|  |  | ||||||
|  | ###    🚀 Features | ||||||
|  |  | ||||||
|  | - **global-tab**: add support for switching tabs with right mouse button click  -  by @soybeanjs [<samp>(b2c91)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b2c919b6) | ||||||
|  |  | ||||||
|  | ###    🐞 Bug Fixes | ||||||
|  |  | ||||||
|  | - **layout**: fix getSiderWidth  -  by @soybeanjs [<samp>(e471e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e471e914) | ||||||
|  | - **packages**: axios: fix json response. fixed #815  -  by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/815 [<samp>(fd087)</samp>](https://github.com/soybeanjs/soybean-admin/commit/fd087f59) | ||||||
|  | - **readme**: update GitHub stars and forks links for gitee  -  by @soybeanjs [<samp>(923eb)</samp>](https://github.com/soybeanjs/soybean-admin/commit/923eb98a) | ||||||
|  | - **scripts**: update command to use 'npm-check-updates' instead of 'ncu'  -  by @soybeanjs [<samp>(8dc17)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8dc17e62) | ||||||
|  | - **types**: fix proxy types  -  by @soybeanjs [<samp>(12b25)</samp>](https://github.com/soybeanjs/soybean-admin/commit/12b25e0d) | ||||||
|  |  | ||||||
|  | ###    📖 Documentation | ||||||
|  |  | ||||||
|  | - **projects**: | ||||||
|  |   - add github trendshift info.  -  by **恕瑞玛的皇帝** [<samp>(e18d3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e18d3972) | ||||||
|  |   - add contribution leaderboard  -  by @wenyuanw [<samp>(01744)</samp>](https://github.com/soybeanjs/soybean-admin/commit/017440c1) | ||||||
|  |  | ||||||
|  | ###    🏡 Chore | ||||||
|  |  | ||||||
|  | - **deps**: | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(e33f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e33f944a) | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(9fa95)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9fa951aa) | ||||||
|  | - **other**: | ||||||
|  |   - update the ESLint validation configuration to support more file types.  -  by **Azir-11** [<samp>(8d7f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8d7f91dc) | ||||||
|  | - **readme**: | ||||||
|  |   - remove DartNode sponsorship badge from README files  -  by @soybeanjs [<samp>(33ade)</samp>](https://github.com/soybeanjs/soybean-admin/commit/33ade539) | ||||||
|  |  | ||||||
|  | ###    ❤️ Contributors | ||||||
|  |  | ||||||
|  | [](https://github.com/soybeanjs)  [](https://github.com/wenyuanw)   | ||||||
|  | [恕瑞玛的皇帝](mailto:2075125282@qq.com) | ||||||
|  |  | ||||||
|  | ## [v2.0.0-beta.1](https://github.com/soybeanjs/soybean-admin/compare/v1.3.15...v2.0.0-beta.1) (2025-10-25) | ||||||
|  |  | ||||||
|  | ###    🚨 Breaking Changes | ||||||
|  |  | ||||||
|  | - **hooks**: refactor useTable and enhance type definitions  -  by @soybeanjs [<samp>(8cc51)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8cc5177) | ||||||
|  | - **projects**: optimize layout mode, split horizontal mix component into two layouts, and rename the component.  -  by **Azir** [<samp>(b6ac3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b6ac310) | ||||||
|  | - **request**: remove cancelRequest method and related logic from request instances  -  by @soybeanjs [<samp>(b4e12)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e1253) | ||||||
|  |  | ||||||
|  | ###    🚀 Features | ||||||
|  |  | ||||||
|  | - **components**: | ||||||
|  |   - add the IconTooltip component.  -  by **Azir-11** [<samp>(a55b4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a55b4dc) | ||||||
|  |   - replace NTooltip with IconTooltip and optimize the layout of related components.  -  by **Azir-11** [<samp>(40057)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4005763) | ||||||
|  | - **hooks**: | ||||||
|  |   - add scrollX computation for total table width in useNaiveTable  -  by @Lruihao [<samp>(358e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/358e129) | ||||||
|  | - **packages**: | ||||||
|  |   - materials support slider-tab. closed #823  -  by @CyberShen in https://github.com/soybeanjs/soybean-admin/issues/823 [<samp>(61fa4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/61fa4b7) | ||||||
|  | - **projects**: | ||||||
|  |   - refactor theme drawer with tabbed layout for better UX.  -  by **Azir** [<samp>(8ba71)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8ba71a0) | ||||||
|  |   - Add current time display option for watermark  -  by @wenyuanw in https://github.com/soybeanjs/soybean-admin/issues/772 [<samp>(f238f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f238fcb) | ||||||
|  |   - add 'vertical-hybrid-header-first' layout mode  -  by @wenyuanw [<samp>(b4e5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e5c6d) | ||||||
|  |   - add prompt information for scrolling mode and tab bar caching.  -  by **Azir-11** [<samp>(29a2a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/29a2a5c) | ||||||
|  |   - support theme preset function.  -  by **Azir-11** [<samp>(257f1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/257f118) | ||||||
|  |   - modify the default value of the reset cache policy to 'refresh'.  -  by **Azir-11** [<samp>(3c0a5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3c0a528) | ||||||
|  |   - optimize tabs cache cleaning strategy. close #820.  -  by **Azir-11** in https://github.com/soybeanjs/soybean-admin/issues/820 [<samp>(ef7ac)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ef7acc6) | ||||||
|  |   - support closing tabs with middle mouse button click  -  by @wenyuanw [<samp>(a8d1e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a8d1e5d) | ||||||
|  |   - support set global redius  -  by **CyberShen123** [<samp>(24c6d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/24c6df5) | ||||||
|  |   - support set global redius  -  by **CyberShen123** [<samp>(3549c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3549c4d) | ||||||
|  | - **styles**: | ||||||
|  |   - add text-autospace property to improve text layout  -  by @wenyuanw [<samp>(345aa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/345aa29) | ||||||
|  |  | ||||||
|  | ###    🐞 Bug Fixes | ||||||
|  |  | ||||||
|  | - **hooks**: | ||||||
|  |   - correct chart rendering logic in useEcharts  -  by @soybeanjs [<samp>(8a7cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8a7cd59) | ||||||
|  | - **packages**: | ||||||
|  |   - fix the parsing logic for stored data to ensure correct return of boolean values  -  by @Lruihao [<samp>(9ea56)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9ea56c9) | ||||||
|  |   - axios: fix json response. fixed #815  -  by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/815 [<samp>(5be86)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5be864a) | ||||||
|  | - **projects**: | ||||||
|  |   - Fix i18n-ally not working when setting moduleResolution to bundler. fixed #780  -  by @xiaobao0505 in https://github.com/soybeanjs/soybean-admin/issues/780 [<samp>(41191)</samp>](https://github.com/soybeanjs/soybean-admin/commit/41191d5) | ||||||
|  | - **styles**: | ||||||
|  |   - show light color scrollbar while dark mode is on  -  by **whyang** [<samp>(dac50)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dac5075) | ||||||
|  | - **table**: | ||||||
|  |   - add type annotations for records in useTable hook  -  by @soybeanjs [<samp>(32b8f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/32b8f99) | ||||||
|  | - **types**: | ||||||
|  |   - fix proxy types  -  by @soybeanjs [<samp>(3d72f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3d72f95) | ||||||
|  |   - fix ts type error  -  by @soybeanjs [<samp>(d5a3a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d5a3a25) | ||||||
|  |  | ||||||
|  | ###    🛠 Optimizations | ||||||
|  |  | ||||||
|  | - **hooks**: | ||||||
|  |   - optimize useEcharts  -  by @soybeanjs [<samp>(936b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/936b834) | ||||||
|  | - **packages**: | ||||||
|  |   - remove ofetch package  -  by @soybeanjs [<samp>(abaaa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/abaaa4a) | ||||||
|  | - **projects**: | ||||||
|  |   - improve theme drawer responsive width for mobile devices  -  by @wenyuanw [<samp>(8439a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8439a60) | ||||||
|  |   - improve robustness of second-level menu key logic  -  by @wenyuanw [<samp>(8b8a2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8b8a208) | ||||||
|  |   - optimize theme drawer width  -  by @soybeanjs [<samp>(81468)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8146858) | ||||||
|  |   - optimize api type file  -  by @soybeanjs [<samp>(3a343)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3a343ee) | ||||||
|  |   - optimize radius settings  -  by @soybeanjs [<samp>(87a66)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87a66a4) | ||||||
|  | - **request**: | ||||||
|  |   - enhance request options and response handling with generic types  -  by @soybeanjs [<samp>(50a5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/50a5cba) | ||||||
|  |  | ||||||
|  | ###    💅 Refactors | ||||||
|  |  | ||||||
|  | - **hooks**: | ||||||
|  |   - optimize useContext and update useMixMenuContext  -  by @soybeanjs [<samp>(c9651)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c965140) | ||||||
|  |   - streamline column visibility handling in useTable and table components  -  by @soybeanjs [<samp>(ee434)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ee43414) | ||||||
|  |   - remove useSignal hook and update exports  -  by @soybeanjs [<samp>(87adc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87adc35) | ||||||
|  | - **menu**: | ||||||
|  |   - optimize the margin on the menu  -  by **NicholasLD** [<samp>(d7311)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d731111) | ||||||
|  | - **projects**: | ||||||
|  |   - remove unnecessary logic in onRouteSwitchWhenLoggedIn  -  by @wenyuanw [<samp>(d6c81)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d6c8142) | ||||||
|  | - **request**: | ||||||
|  |   - unify response transformation methods and deprecate transformBackendResponse  -  by @soybeanjs [<samp>(f83ee)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f83eefb) | ||||||
|  | - **types**: | ||||||
|  |   - move Auth and Route namespaces to separate files and clean up api.d.ts  -  by **Azir** [<samp>(d37ce)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d37ce04) | ||||||
|  |  | ||||||
|  | ###    📖 Documentation | ||||||
|  |  | ||||||
|  | - **projects**: add github trendshift info.  -  by **恕瑞玛的皇帝** [<samp>(2a0c9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2a0c9f1) | ||||||
|  |  | ||||||
|  | ###    🏡 Chore | ||||||
|  |  | ||||||
|  | - **deps**: | ||||||
|  |   - update NodeJS and pnpm version requirements in package.json and documentation  -  by **Junior25306** [<samp>(a5c4b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a5c4b4e) | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(5cb1c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5cb1ceb) | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(aeb63)</samp>](https://github.com/soybeanjs/soybean-admin/commit/aeb6369) | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(e89b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e89b86c) | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(c962f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c962f7b) | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(12135)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1213531) | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(b041f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b041fdd) | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(d567c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d567c05) | ||||||
|  |   - update deps  -  by @soybeanjs [<samp>(6cbf5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6cbf570) | ||||||
|  | - **other**: | ||||||
|  |   - update the ESLint validation configuration to support more file types.  -  by **Azir-11** [<samp>(be8f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/be8f915) | ||||||
|  | - **packages**: | ||||||
|  |   - update Vite version to 7 in package.json and documentation.  -  by **Azir** [<samp>(03dd6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/03dd64c) | ||||||
|  |   - add picomatch to fix scripts  -  by @soybeanjs [<samp>(805c3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/805c338) | ||||||
|  | - **vscode**: | ||||||
|  |   - remove unused vue.server.hybridMode setting from .vscode/settings.json  -  by @soybeanjs [<samp>(13319)</samp>](https://github.com/soybeanjs/soybean-admin/commit/133196f) | ||||||
|  |  | ||||||
|  | ###    🎨 Styles | ||||||
|  |  | ||||||
|  | - **projects**: format code.  -  by **Azir-11** [<samp>(100e0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/100e0ea) | ||||||
|  |  | ||||||
|  | ###    ❤️ Contributors | ||||||
|  |  | ||||||
|  | [](https://github.com/soybeanjs)  [](https://github.com/wenyuanw)  [](https://github.com/CyberShen)  [](https://github.com/Lruihao)  [](https://github.com/xiaobao0505)   | ||||||
|  | [CyberShen123](mailto:s.lijun@qq.com), [whyang](mailto:whyang9701@gmail.com), [HongxuanG](mailto:1359774872@qq.com), [Azir-11](mailto:2075125282@qq.com), [NicholasLD](mailto:878639947@qq.com), [Junior25306](mailto:dayu429@qq.com) | ||||||
|  |  | ||||||
| ## [v1.3.15](https://github.com/soybeanjs/soybean-admin/compare/v1.3.14...v1.3.15) (2025-06-24) | ## [v1.3.15](https://github.com/soybeanjs/soybean-admin/compare/v1.3.14...v1.3.15) (2025-06-24) | ||||||
|  |  | ||||||
| ###    🚀 Features | ###    🚀 Features | ||||||
|   | |||||||
| @@ -7,11 +7,10 @@ | |||||||
| --- | --- | ||||||
|  |  | ||||||
| [](./LICENSE) | [](./LICENSE) | ||||||
| [](https://github.com/soybeanjs/soybean-admin) | [](https://github.com/soybeanjs/soybean-admin) | ||||||
| [](https://github.com/soybeanjs/soybean-admin) | [](https://github.com/soybeanjs/soybean-admin) | ||||||
| [](https://gitee.com/honghuangdc/soybean-admin) | [](https://gitee.com/honghuangdc/soybean-admin) | ||||||
| [](https://gitcode.com/soybeanjs/soybean-admin) | [](https://gitcode.com/soybeanjs/soybean-admin) | ||||||
| [](https://dartnode.com "Powered by DartNode - Free VPS for Open Source") |  | ||||||
|  |  | ||||||
| <div style="display: flex; gap: 12px; align-items: center;"> | <div style="display: flex; gap: 12px; align-items: center;"> | ||||||
|   <a href="https://trendshift.io/repositories/7963" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7963" alt="soybeanjs%2Fsoybean-admin | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> |   <a href="https://trendshift.io/repositories/7963" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7963" alt="soybeanjs%2Fsoybean-admin | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> | ||||||
| @@ -181,6 +180,14 @@ Thanks the following people for their contributions. If you want to contribute t | |||||||
|   <img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" /> |   <img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" /> | ||||||
| </a> | </a> | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | Here are the most active contributors from the past year. Thank you all for your support, which has enabled the project's continued development. | ||||||
|  |  | ||||||
|  | <a href="https://openomy.com/soybeanjs/soybean-admin" target="_blank" style="display: block; width: 100%;" align="center"> | ||||||
|  |   <img src="https://www.openomy.com/svg?repo=soybeanjs/soybean-admin&chart=list&latestMonth=12" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" /> | ||||||
|  | </a> | ||||||
|  |  | ||||||
| ## Communication | ## Communication | ||||||
|  |  | ||||||
| `SoybeanAdmin` is a completely open source and free project, helping developers to develop medium and large-scale management systems more conveniently. It also provides WeChat and QQ communication groups. If you have any questions, please feel free to ask in the group. | `SoybeanAdmin` is a completely open source and free project, helping developers to develop medium and large-scale management systems more conveniently. It also provides WeChat and QQ communication groups. If you have any questions, please feel free to ask in the group. | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							| @@ -7,11 +7,10 @@ | |||||||
| --- | --- | ||||||
|  |  | ||||||
| [](./LICENSE) | [](./LICENSE) | ||||||
| [](https://github.com/soybeanjs/soybean-admin) | [](https://github.com/soybeanjs/soybean-admin) | ||||||
| [](https://github.com/soybeanjs/soybean-admin) | [](https://github.com/soybeanjs/soybean-admin) | ||||||
| [](https://gitee.com/honghuangdc/soybean-admin) | [](https://gitee.com/honghuangdc/soybean-admin) | ||||||
| [](https://gitcode.com/soybeanjs/soybean-admin) | [](https://gitcode.com/soybeanjs/soybean-admin) | ||||||
| [](https://dartnode.com "Powered by DartNode - Free VPS for Open Source") |  | ||||||
|  |  | ||||||
| <div style="display: flex; gap: 12px; align-items: center;"> | <div style="display: flex; gap: 12px; align-items: center;"> | ||||||
|   <a href="https://trendshift.io/repositories/7963" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7963" alt="soybeanjs%2Fsoybean-admin | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> |   <a href="https://trendshift.io/repositories/7963" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7963" alt="soybeanjs%2Fsoybean-admin | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> | ||||||
| @@ -209,6 +208,14 @@ pnpm build | |||||||
|   <img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" /> |   <img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" /> | ||||||
| </a> | </a> | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | 以下是近一年中活跃度较高的贡献者,感谢各位的支持,让项目得以持续发展。 | ||||||
|  |  | ||||||
|  | <a href="https://openomy.com/soybeanjs/soybean-admin" target="_blank" style="display: block; width: 100%;" align="center"> | ||||||
|  |   <img src="https://www.openomy.com/svg?repo=soybeanjs/soybean-admin&chart=list&latestMonth=12" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" /> | ||||||
|  | </a> | ||||||
|  |  | ||||||
| ## 交流 | ## 交流 | ||||||
|  |  | ||||||
| `SoybeanAdmin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。 | `SoybeanAdmin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。 | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import type { PluginOption } from 'vite'; | |||||||
| import vue from '@vitejs/plugin-vue'; | import vue from '@vitejs/plugin-vue'; | ||||||
| import vueJsx from '@vitejs/plugin-vue-jsx'; | import vueJsx from '@vitejs/plugin-vue-jsx'; | ||||||
| import progress from 'vite-plugin-progress'; | import progress from 'vite-plugin-progress'; | ||||||
| import elegantRouter from 'elegant-router/vite'; | import { setupElegantRouter } from './router'; | ||||||
| import { setupUnocss } from './unocss'; | import { setupUnocss } from './unocss'; | ||||||
| import { setupUnplugin } from './unplugin'; | import { setupUnplugin } from './unplugin'; | ||||||
| import { setupHtmlPlugin } from './html'; | import { setupHtmlPlugin } from './html'; | ||||||
| @@ -13,7 +13,7 @@ export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) { | |||||||
|     vue(), |     vue(), | ||||||
|     vueJsx(), |     vueJsx(), | ||||||
|     setupDevtoolsPlugin(viteEnv), |     setupDevtoolsPlugin(viteEnv), | ||||||
|     elegantRouter(), |     setupElegantRouter(), | ||||||
|     setupUnocss(viteEnv), |     setupUnocss(viteEnv), | ||||||
|     ...setupUnplugin(viteEnv), |     ...setupUnplugin(viteEnv), | ||||||
|     progress(), |     progress(), | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								build/plugins/router.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								build/plugins/router.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | import type { RouteMeta } from 'vue-router'; | ||||||
|  | import ElegantVueRouter from '@elegant-router/vue/vite'; | ||||||
|  | import type { RouteKey } from '@elegant-router/types'; | ||||||
|  |  | ||||||
|  | export function setupElegantRouter() { | ||||||
|  |   return ElegantVueRouter({ | ||||||
|  |     layouts: { | ||||||
|  |       base: 'src/layouts/base-layout/index.vue', | ||||||
|  |       blank: 'src/layouts/blank-layout/index.vue' | ||||||
|  |     }, | ||||||
|  |     routePathTransformer(routeName, routePath) { | ||||||
|  |       const key = routeName as RouteKey; | ||||||
|  |  | ||||||
|  |       if (key === 'login') { | ||||||
|  |         const modules: UnionKey.LoginModule[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat']; | ||||||
|  |  | ||||||
|  |         const moduleReg = modules.join('|'); | ||||||
|  |  | ||||||
|  |         return `/login/:module(${moduleReg})?`; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return routePath; | ||||||
|  |     }, | ||||||
|  |     onRouteMetaGen(routeName) { | ||||||
|  |       const key = routeName as RouteKey; | ||||||
|  |  | ||||||
|  |       const constantRoutes: RouteKey[] = ['login', '403', '404', '500']; | ||||||
|  |  | ||||||
|  |       const meta: Partial<RouteMeta> = { | ||||||
|  |         title: key, | ||||||
|  |         i18nKey: `route.${key}` as App.I18n.I18nKey | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |       if (constantRoutes.includes(key)) { | ||||||
|  |         meta.constant = true; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return meta; | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								er.config.ts
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								er.config.ts
									
									
									
									
									
								
							| @@ -1,37 +0,0 @@ | |||||||
| import type { RouteMeta } from 'vue-router'; |  | ||||||
| import { defineConfig } from 'elegant-router'; |  | ||||||
| import type { RouteKey } from '@elegant-router/types'; |  | ||||||
|  |  | ||||||
| export default defineConfig({ |  | ||||||
|   pageDir: ['src/views'], |  | ||||||
|   layouts: { |  | ||||||
|     base: 'src/layouts/base-layout/index.vue', |  | ||||||
|     blank: 'src/layouts/blank-layout/index.vue' |  | ||||||
|   }, |  | ||||||
|   getRoutePath: node => { |  | ||||||
|     if (node.name === 'Login') { |  | ||||||
|       const modules: UnionKey.LoginModule[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat']; |  | ||||||
|  |  | ||||||
|       const moduleReg = modules.join('|'); |  | ||||||
|  |  | ||||||
|       return `/login/:module(${moduleReg})?`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return node.path; |  | ||||||
|   }, |  | ||||||
|   getRouteMeta: node => { |  | ||||||
|     const constantRoutes: RouteKey[] = ['Login', '403', '404', '500']; |  | ||||||
|  |  | ||||||
|     const name = node.name as RouteKey; |  | ||||||
|  |  | ||||||
|     const meta: Partial<RouteMeta> = { |  | ||||||
|       title: name |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if (constantRoutes.includes(name)) { |  | ||||||
|       meta.constant = true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return meta; |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
							
								
								
									
										37
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "name": "soybean-admin", |   "name": "soybean-admin", | ||||||
|   "type": "module", |   "type": "module", | ||||||
|   "version": "1.3.15", |   "version": "2.0.0-beta.2", | ||||||
|   "description": "A fresh and elegant admin template, based on Vue3、Vite7、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite7、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。", |   "description": "A fresh and elegant admin template, based on Vue3、Vite7、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite7、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。", | ||||||
|   "author": { |   "author": { | ||||||
|     "name": "Soybean", |     "name": "Soybean", | ||||||
| @@ -54,7 +54,7 @@ | |||||||
|     "@sa/hooks": "workspace:*", |     "@sa/hooks": "workspace:*", | ||||||
|     "@sa/materials": "workspace:*", |     "@sa/materials": "workspace:*", | ||||||
|     "@sa/utils": "workspace:*", |     "@sa/utils": "workspace:*", | ||||||
|     "@vueuse/core": "13.9.0", |     "@vueuse/core": "14.0.0", | ||||||
|     "clipboard": "2.0.11", |     "clipboard": "2.0.11", | ||||||
|     "dayjs": "1.11.18", |     "dayjs": "1.11.18", | ||||||
|     "defu": "6.1.4", |     "defu": "6.1.4", | ||||||
| @@ -67,41 +67,40 @@ | |||||||
|     "vue": "3.5.22", |     "vue": "3.5.22", | ||||||
|     "vue-draggable-plus": "0.6.0", |     "vue-draggable-plus": "0.6.0", | ||||||
|     "vue-i18n": "11.1.12", |     "vue-i18n": "11.1.12", | ||||||
|     "vue-router": "4.5.1" |     "vue-router": "4.6.3" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@elegant-router/vue": "0.3.8", |     "@elegant-router/vue": "0.3.8", | ||||||
|     "@iconify/json": "2.2.395", |     "@iconify/json": "2.2.401", | ||||||
|     "@sa/scripts": "workspace:*", |     "@sa/scripts": "workspace:*", | ||||||
|     "@sa/uno-preset": "workspace:*", |     "@sa/uno-preset": "workspace:*", | ||||||
|     "@soybeanjs/eslint-config": "1.7.1", |     "@soybeanjs/eslint-config": "1.7.1", | ||||||
|     "@types/node": "24.7.2", |     "@types/node": "24.9.2", | ||||||
|     "@types/nprogress": "0.2.3", |     "@types/nprogress": "0.2.3", | ||||||
|     "@unocss/eslint-config": "66.5.3", |     "@unocss/eslint-config": "66.5.4", | ||||||
|     "@unocss/preset-icons": "66.5.3", |     "@unocss/preset-icons": "66.5.4", | ||||||
|     "@unocss/preset-uno": "66.5.3", |     "@unocss/preset-uno": "66.5.4", | ||||||
|     "@unocss/transformer-directives": "66.5.3", |     "@unocss/transformer-directives": "66.5.4", | ||||||
|     "@unocss/transformer-variant-group": "66.5.3", |     "@unocss/transformer-variant-group": "66.5.4", | ||||||
|     "@unocss/vite": "66.5.3", |     "@unocss/vite": "66.5.4", | ||||||
|     "@vitejs/plugin-vue": "6.0.1", |     "@vitejs/plugin-vue": "6.0.1", | ||||||
|     "@vitejs/plugin-vue-jsx": "5.1.1", |     "@vitejs/plugin-vue-jsx": "5.1.1", | ||||||
|     "consola": "3.4.2", |     "consola": "3.4.2", | ||||||
|     "elegant-router": "1.0.4", |     "eslint": "9.38.0", | ||||||
|     "eslint": "9.37.0", |     "eslint-plugin-vue": "10.5.1", | ||||||
|     "eslint-plugin-vue": "10.5.0", |  | ||||||
|     "kolorist": "1.8.0", |     "kolorist": "1.8.0", | ||||||
|     "sass": "1.93.2", |     "sass": "1.93.2", | ||||||
|     "simple-git-hooks": "2.13.1", |     "simple-git-hooks": "2.13.1", | ||||||
|     "tsx": "4.20.6", |     "tsx": "4.20.6", | ||||||
|     "typescript": "5.9.3", |     "typescript": "5.9.3", | ||||||
|     "unplugin-icons": "22.4.2", |     "unplugin-icons": "22.5.0", | ||||||
|     "unplugin-vue-components": "29.1.0", |     "unplugin-vue-components": "30.0.0", | ||||||
|     "vite": "7.1.9", |     "vite": "7.1.12", | ||||||
|     "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": "8.0.2", |     "vite-plugin-vue-devtools": "8.0.3", | ||||||
|     "vue-eslint-parser": "10.2.0", |     "vue-eslint-parser": "10.2.0", | ||||||
|     "vue-tsc": "3.1.1" |     "vue-tsc": "3.1.2" | ||||||
|   }, |   }, | ||||||
|   "simple-git-hooks": { |   "simple-git-hooks": { | ||||||
|     "commit-msg": "pnpm sa git-commit-verify", |     "commit-msg": "pnpm sa git-commit-verify", | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@sa/alova", |   "name": "@sa/alova", | ||||||
|   "version": "1.3.15", |   "version": "2.0.0-beta.2", | ||||||
|   "exports": { |   "exports": { | ||||||
|     ".": "./src/index.ts", |     ".": "./src/index.ts", | ||||||
|     "./fetch": "./src/fetch.ts", |     "./fetch": "./src/fetch.ts", | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@sa/axios", |   "name": "@sa/axios", | ||||||
|   "version": "1.3.15", |   "version": "2.0.0-beta.2", | ||||||
|   "exports": { |   "exports": { | ||||||
|     ".": "./src/index.ts" |     ".": "./src/index.ts" | ||||||
|   }, |   }, | ||||||
| @@ -11,7 +11,7 @@ | |||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@sa/utils": "workspace:*", |     "@sa/utils": "workspace:*", | ||||||
|     "axios": "1.12.2", |     "axios": "1.13.1", | ||||||
|     "axios-retry": "4.5.0", |     "axios-retry": "4.5.0", | ||||||
|     "qs": "6.14.0" |     "qs": "6.14.0" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@sa/color", |   "name": "@sa/color", | ||||||
|   "version": "1.3.15", |   "version": "2.0.0-beta.2", | ||||||
|   "exports": { |   "exports": { | ||||||
|     ".": "./src/index.ts" |     ".": "./src/index.ts" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@sa/hooks", |   "name": "@sa/hooks", | ||||||
|   "version": "1.3.15", |   "version": "2.0.0-beta.2", | ||||||
|   "exports": { |   "exports": { | ||||||
|     ".": "./src/index.ts" |     ".": "./src/index.ts" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@sa/materials", |   "name": "@sa/materials", | ||||||
|   "version": "1.3.15", |   "version": "2.0.0-beta.2", | ||||||
|   "exports": { |   "exports": { | ||||||
|     ".": "./src/index.ts" |     ".": "./src/index.ts" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@sa/scripts", |   "name": "@sa/scripts", | ||||||
|   "version": "1.3.15", |   "version": "2.0.0-beta.2", | ||||||
|   "bin": { |   "bin": { | ||||||
|     "sa": "./bin.ts" |     "sa": "./bin.ts" | ||||||
|   }, |   }, | ||||||
| @@ -15,13 +15,13 @@ | |||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@soybeanjs/changelog": "0.3.25", |     "@soybeanjs/changelog": "0.3.25", | ||||||
|     "bumpp": "10.3.1", |     "bumpp": "10.3.1", | ||||||
|     "c12": "3.3.0", |     "c12": "3.3.1", | ||||||
|     "cac": "6.7.14", |     "cac": "6.7.14", | ||||||
|     "consola": "3.4.2", |     "consola": "3.4.2", | ||||||
|     "enquirer": "2.4.1", |     "enquirer": "2.4.1", | ||||||
|     "execa": "9.6.0", |     "execa": "9.6.0", | ||||||
|     "kolorist": "1.8.0", |     "kolorist": "1.8.0", | ||||||
|     "npm-check-updates": "19.0.0", |     "npm-check-updates": "19.1.2", | ||||||
|     "picomatch": "4.0.3", |     "picomatch": "4.0.3", | ||||||
|     "rimraf": "6.0.1" |     "rimraf": "6.0.1" | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { execCommand } from '../shared'; | import { execCommand } from '../shared'; | ||||||
|  |  | ||||||
| export async function updatePkg(args: string[] = ['--deep', '-u']) { | export async function updatePkg(args: string[] = ['--deep', '-u']) { | ||||||
|   execCommand('npx', ['ncu', ...args], { stdio: 'inherit' }); |   execCommand('npx', ['npm-check-updates', ...args], { stdio: 'inherit' }); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@sa/uno-preset", |   "name": "@sa/uno-preset", | ||||||
|   "version": "1.3.15", |   "version": "2.0.0-beta.2", | ||||||
|   "exports": { |   "exports": { | ||||||
|     ".": "./src/index.ts" |     ".": "./src/index.ts" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@sa/utils", |   "name": "@sa/utils", | ||||||
|   "version": "1.3.15", |   "version": "2.0.0-beta.2", | ||||||
|   "exports": { |   "exports": { | ||||||
|     ".": "./src/index.ts" |     ".": "./src/index.ts" | ||||||
|   }, |   }, | ||||||
|   | |||||||
							
								
								
									
										1574
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1574
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -18,7 +18,7 @@ defineOptions({ | |||||||
|  |  | ||||||
| const appStore = useAppStore(); | const appStore = useAppStore(); | ||||||
| const themeStore = useThemeStore(); | const themeStore = useThemeStore(); | ||||||
| const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext(); | const { secondLevelMenus, childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext(); | ||||||
|  |  | ||||||
| const GlobalMenu = defineAsyncComponent(() => import('../modules/global-menu/index.vue')); | const GlobalMenu = defineAsyncComponent(() => import('../modules/global-menu/index.vue')); | ||||||
|  |  | ||||||
| @@ -77,9 +77,9 @@ const isTopHybridSidebarFirst = computed(() => themeStore.layout.mode === 'top-h | |||||||
|  |  | ||||||
| const isTopHybridHeaderFirst = computed(() => themeStore.layout.mode === 'top-hybrid-header-first'); | const isTopHybridHeaderFirst = computed(() => themeStore.layout.mode === 'top-hybrid-header-first'); | ||||||
|  |  | ||||||
| const siderWidth = computed(() => getSiderWidth()); | const siderWidth = computed(() => getSiderAndCollapsedWidth(false)); | ||||||
|  |  | ||||||
| const siderCollapsedWidth = computed(() => getSiderCollapsedWidth()); | const siderCollapsedWidth = computed(() => getSiderAndCollapsedWidth(true)); | ||||||
|  |  | ||||||
| function getSiderAndCollapsedWidth(isCollapsed: boolean) { | function getSiderAndCollapsedWidth(isCollapsed: boolean) { | ||||||
|   const { |   const { | ||||||
| @@ -104,7 +104,7 @@ function getSiderAndCollapsedWidth(isCollapsed: boolean) { | |||||||
|   const isMixMode = isVerticalMix.value || isTopHybridSidebarFirst.value || isVerticalHybridHeaderFirst.value; |   const isMixMode = isVerticalMix.value || isTopHybridSidebarFirst.value || isVerticalHybridHeaderFirst.value; | ||||||
|   let finalWidth = isMixMode ? mixWidth : width; |   let finalWidth = isMixMode ? mixWidth : width; | ||||||
|  |  | ||||||
|   if (isVerticalMix.value && appStore.mixSiderFixed && childLevelMenus.value.length) { |   if (isVerticalMix.value && appStore.mixSiderFixed && secondLevelMenus.value.length) { | ||||||
|     finalWidth += mixChildMenuWidth; |     finalWidth += mixChildMenuWidth; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -114,14 +114,6 @@ function getSiderAndCollapsedWidth(isCollapsed: boolean) { | |||||||
|  |  | ||||||
|   return finalWidth; |   return finalWidth; | ||||||
| } | } | ||||||
|  |  | ||||||
| function getSiderWidth() { |  | ||||||
|   return getSiderAndCollapsedWidth(false); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function getSiderCollapsedWidth() { |  | ||||||
|   return getSiderAndCollapsedWidth(true); |  | ||||||
| } |  | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <template> | <template> | ||||||
|   | |||||||
| @@ -26,6 +26,8 @@ const tabRef = ref<HTMLElement>(); | |||||||
| const isPCFlag = isPC(); | const isPCFlag = isPC(); | ||||||
|  |  | ||||||
| const TAB_DATA_ID = 'data-tab-id'; | const TAB_DATA_ID = 'data-tab-id'; | ||||||
|  | const MIDDLE_MOUSE_BUTTON = 1; | ||||||
|  | const RIGHT_MOUSE_BUTTON = 2; | ||||||
|  |  | ||||||
| type TabNamedNodeMap = NamedNodeMap & { | type TabNamedNodeMap = NamedNodeMap & { | ||||||
|   [TAB_DATA_ID]: Attr; |   [TAB_DATA_ID]: Attr; | ||||||
| @@ -84,6 +86,26 @@ function handleCloseTab(tab: App.Global.Tab) { | |||||||
|   tabStore.removeTab(tab.id); |   tabStore.removeTab(tab.id); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function handleMousedown(e: MouseEvent, tab: App.Global.Tab) { | ||||||
|  |   const isMiddleClick = e.button === MIDDLE_MOUSE_BUTTON; | ||||||
|  |   if (!isMiddleClick || !themeStore.tab.closeTabByMiddleClick) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (tabStore.isTabRetain(tab.id)) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   e.preventDefault(); | ||||||
|  |   handleCloseTab(tab); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function switchTab(e: MouseEvent, tab: App.Global.Tab) { | ||||||
|  |   if ([MIDDLE_MOUSE_BUTTON, RIGHT_MOUSE_BUTTON].includes(e.button)) return; | ||||||
|  |  | ||||||
|  |   tabStore.switchRouteByTab(tab); | ||||||
|  | } | ||||||
|  |  | ||||||
| async function refresh() { | async function refresh() { | ||||||
|   appStore.reloadPage(500); |   appStore.reloadPage(500); | ||||||
| } | } | ||||||
| @@ -182,7 +204,8 @@ init(); | |||||||
|             :active="tab.id === tabStore.activeTabId" |             :active="tab.id === tabStore.activeTabId" | ||||||
|             :active-color="themeStore.themeColor" |             :active-color="themeStore.themeColor" | ||||||
|             :closable="!tabStore.isTabRetain(tab.id)" |             :closable="!tabStore.isTabRetain(tab.id)" | ||||||
|             @pointerdown="tabStore.switchRouteByTab(tab)" |             @pointerdown="switchTab($event, tab)" | ||||||
|  |             @mousedown="handleMousedown($event, tab)" | ||||||
|             @close="handleCloseTab(tab)" |             @close="handleCloseTab(tab)" | ||||||
|             @contextmenu="handleContextMenu($event, tab.id)" |             @contextmenu="handleContextMenu($event, tab.id)" | ||||||
|           > |           > | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import ThemeSchema from './modules/theme-schema.vue'; | import ThemeSchema from './modules/theme-schema.vue'; | ||||||
| import ThemeColor from './modules/theme-color.vue'; | import ThemeColor from './modules/theme-color.vue'; | ||||||
|  | import ThemeRadius from './modules/theme-radius.vue'; | ||||||
|  |  | ||||||
| defineOptions({ | defineOptions({ | ||||||
|   name: 'AppearanceSettings' |   name: 'AppearanceSettings' | ||||||
| @@ -11,6 +12,7 @@ defineOptions({ | |||||||
|   <div class="flex-col-stretch gap-16px"> |   <div class="flex-col-stretch gap-16px"> | ||||||
|     <ThemeSchema /> |     <ThemeSchema /> | ||||||
|     <ThemeColor /> |     <ThemeColor /> | ||||||
|  |     <ThemeRadius /> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import { useThemeStore } from '@/store/modules/theme'; | ||||||
|  | import { $t } from '@/locales'; | ||||||
|  | import SettingItem from '../../../components/setting-item.vue'; | ||||||
|  |  | ||||||
|  | defineOptions({ | ||||||
|  |   name: 'ThemeRadius' | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | const themeStore = useThemeStore(); | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <template> | ||||||
|  |   <NDivider>{{ $t('theme.appearance.themeRadius.title') }}</NDivider> | ||||||
|  |   <TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px"> | ||||||
|  |     <SettingItem key="1" :label="$t('theme.appearance.themeRadius.title')"> | ||||||
|  |       <NInputNumber v-model:value="themeStore.themeRadius" size="small" :step="1" :min="0" :max="16" class="w-120px" /> | ||||||
|  |     </SettingItem> | ||||||
|  |   </TransitionGroup> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <style scoped></style> | ||||||
| @@ -35,6 +35,12 @@ const themeStore = useThemeStore(); | |||||||
|         class="w-120px" |         class="w-120px" | ||||||
|       /> |       /> | ||||||
|     </SettingItem> |     </SettingItem> | ||||||
|  |     <SettingItem v-if="themeStore.tab.visible" key="5" :label="$t('theme.layout.tab.closeByMiddleClick')"> | ||||||
|  |       <template #suffix> | ||||||
|  |         <IconTooltip :desc="$t('theme.layout.tab.closeByMiddleClickTip')" /> | ||||||
|  |       </template> | ||||||
|  |       <NSwitch v-model:value="themeStore.tab.closeTabByMiddleClick" /> | ||||||
|  |     </SettingItem> | ||||||
|   </TransitionGroup> |   </TransitionGroup> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ type ThemePreset = Pick< | |||||||
|   | 'colourWeakness' |   | 'colourWeakness' | ||||||
|   | 'recommendColor' |   | 'recommendColor' | ||||||
|   | 'themeColor' |   | 'themeColor' | ||||||
|  |   | 'themeRadius' | ||||||
|   | 'otherColor' |   | 'otherColor' | ||||||
|   | 'isInfoFollowPrimary' |   | 'isInfoFollowPrimary' | ||||||
|   | 'layout' |   | 'layout' | ||||||
|   | |||||||
| @@ -83,6 +83,9 @@ const local: App.I18n.Schema = { | |||||||
|         error: 'Error', |         error: 'Error', | ||||||
|         followPrimary: 'Follow Primary' |         followPrimary: 'Follow Primary' | ||||||
|       }, |       }, | ||||||
|  |       themeRadius: { | ||||||
|  |         title: 'Theme Radius' | ||||||
|  |       }, | ||||||
|       recommendColor: 'Apply Recommended Color Algorithm', |       recommendColor: 'Apply Recommended Color Algorithm', | ||||||
|       recommendColorDesc: 'The recommended color algorithm refers to', |       recommendColorDesc: 'The recommended color algorithm refers to', | ||||||
|       preset: { |       preset: { | ||||||
| @@ -138,7 +141,9 @@ const local: App.I18n.Schema = { | |||||||
|           slider: 'Slider', |           slider: 'Slider', | ||||||
|           chrome: 'Chrome', |           chrome: 'Chrome', | ||||||
|           button: 'Button' |           button: 'Button' | ||||||
|         } |         }, | ||||||
|  |         closeByMiddleClick: 'Close Tab by Middle Click', | ||||||
|  |         closeByMiddleClickTip: 'Enable closing tabs by clicking with the middle mouse button' | ||||||
|       }, |       }, | ||||||
|       header: { |       header: { | ||||||
|         title: 'Header Settings', |         title: 'Header Settings', | ||||||
|   | |||||||
| @@ -83,6 +83,9 @@ const local: App.I18n.Schema = { | |||||||
|         error: '错误色', |         error: '错误色', | ||||||
|         followPrimary: '跟随主色' |         followPrimary: '跟随主色' | ||||||
|       }, |       }, | ||||||
|  |       themeRadius: { | ||||||
|  |         title: '主题圆角' | ||||||
|  |       }, | ||||||
|       recommendColor: '应用推荐算法的颜色', |       recommendColor: '应用推荐算法的颜色', | ||||||
|       recommendColorDesc: '推荐颜色的算法参照', |       recommendColorDesc: '推荐颜色的算法参照', | ||||||
|       preset: { |       preset: { | ||||||
| @@ -135,7 +138,9 @@ const local: App.I18n.Schema = { | |||||||
|           slider: '滑块风格', |           slider: '滑块风格', | ||||||
|           chrome: '谷歌风格', |           chrome: '谷歌风格', | ||||||
|           button: '按钮风格' |           button: '按钮风格' | ||||||
|         } |         }, | ||||||
|  |         closeByMiddleClick: '鼠标中键关闭标签页', | ||||||
|  |         closeByMiddleClickTip: '启用后可以使用鼠标中键点击标签页进行关闭' | ||||||
|       }, |       }, | ||||||
|       header: { |       header: { | ||||||
|         title: '头部设置', |         title: '头部设置', | ||||||
|   | |||||||
| @@ -1,27 +0,0 @@ | |||||||
| /* eslint-disable */ |  | ||||||
| /* prettier-ignore */ |  | ||||||
| /* oxlint-disable */ |  | ||||||
| // biome-ignore lint: disable |  | ||||||
| // Generated by elegant-router |  | ||||||
| // Read more: https://github.com/soybeanjs/elegant-router |  | ||||||
|  |  | ||||||
| import type { RouteFileKey, RouteLayoutKey, RawRouteComponent } from "@elegant-router/types"; |  | ||||||
|  |  | ||||||
| export const layouts: Record<RouteLayoutKey, RawRouteComponent> = { |  | ||||||
|   base: () => import("@/layouts/base-layout/index.vue"), |  | ||||||
|   blank: () => import("@/layouts/blank-layout/index.vue"), |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export const views: Record<RouteFileKey, RawRouteComponent> = { |  | ||||||
|   403: () => import("@/views/(builtin)/403/index.vue"), |  | ||||||
|   404: () => import("@/views/(builtin)/404/index.vue"), |  | ||||||
|   500: () => import("@/views/(builtin)/500/index.vue"), |  | ||||||
|   Home: () => import("@/views/home/index.vue"), |  | ||||||
|   IframeUrl: () => import("@/views/(builtin)/iframe/[url].vue"), |  | ||||||
|   Login: () => import("@/views/(builtin)/login/index.vue"), |  | ||||||
|   ManageMenu: () => import("@/views/manage/menu/index.vue"), |  | ||||||
|   ManageRole: () => import("@/views/manage/role/index.vue"), |  | ||||||
|   ManageRoute: () => import("@/views/manage/route/index.vue"), |  | ||||||
|   ManageUser: () => import("@/views/manage/user/index.vue"), |  | ||||||
|   Wip: () => import("@/views/(builtin)/wip/index.vue"), |  | ||||||
| }; |  | ||||||
| @@ -1,130 +0,0 @@ | |||||||
| /* eslint-disable */ |  | ||||||
| /* prettier-ignore */ |  | ||||||
| // biome-ignore lint: disable |  | ||||||
| // Generated by elegant-router |  | ||||||
| // Read more: https://github.com/soybeanjs/elegant-router |  | ||||||
|  |  | ||||||
| import type { AutoRouterRoute } from '@elegant-router/types'; |  | ||||||
|  |  | ||||||
| export const routes: AutoRouterRoute[] = [ |  | ||||||
|   { |  | ||||||
|     name: 'Root', |  | ||||||
|     path: '/', |  | ||||||
|     redirect: '/home', |  | ||||||
|     meta: { |  | ||||||
|       title: "Root" |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: 'NotFound', |  | ||||||
|     path: '/:pathMatch(.*)*', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: '404', |  | ||||||
|     meta: { |  | ||||||
|       title: "NotFound" |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: '403', |  | ||||||
|     path: '/403', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: '403', |  | ||||||
|     meta: { |  | ||||||
|       title: "403", |  | ||||||
|       constant: true |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: '404', |  | ||||||
|     path: '/404', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: '404', |  | ||||||
|     meta: { |  | ||||||
|       title: "404", |  | ||||||
|       constant: true |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: '500', |  | ||||||
|     path: '/500', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: '500', |  | ||||||
|     meta: { |  | ||||||
|       title: "500", |  | ||||||
|       constant: true |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: 'Home', |  | ||||||
|     path: '/home', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: 'Home', |  | ||||||
|     meta: { |  | ||||||
|       title: "Home" |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: 'IframeUrl', |  | ||||||
|     path: '/iframe/:url', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: 'IframeUrl', |  | ||||||
|     meta: { |  | ||||||
|       title: "IframeUrl" |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: 'Login', |  | ||||||
|     path: '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: 'Login', |  | ||||||
|     meta: { |  | ||||||
|       title: "Login", |  | ||||||
|       constant: true |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: 'ManageMenu', |  | ||||||
|     path: '/manage/menu', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: 'ManageMenu', |  | ||||||
|     meta: { |  | ||||||
|       title: "ManageMenu" |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: 'ManageRole', |  | ||||||
|     path: '/manage/role', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: 'ManageRole', |  | ||||||
|     meta: { |  | ||||||
|       title: "ManageRole" |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: 'ManageRoute', |  | ||||||
|     path: '/manage/route', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: 'ManageRoute', |  | ||||||
|     meta: { |  | ||||||
|       title: "ManageRoute" |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: 'ManageUser', |  | ||||||
|     path: '/manage/user', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: 'ManageUser', |  | ||||||
|     meta: { |  | ||||||
|       title: "ManageUser" |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     name: 'Wip', |  | ||||||
|     path: '/wip', |  | ||||||
|     layout: 'base', |  | ||||||
|     component: 'Wip', |  | ||||||
|     meta: { |  | ||||||
|       title: "Wip" |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| ]; |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| /* eslint-disable */ |  | ||||||
| /* prettier-ignore */ |  | ||||||
| /* oxlint-disable */ |  | ||||||
| // biome-ignore lint: disable |  | ||||||
| // Generated by elegant-router |  | ||||||
| // Read more: https://github.com/soybeanjs/elegant-router |  | ||||||
|  |  | ||||||
| import type { RouteKey, RoutePathMap } from '@elegant-router/types'; |  | ||||||
|  |  | ||||||
| const routePathMap: RoutePathMap = { |  | ||||||
|   "Root": "/", |  | ||||||
|   "NotFound": "/:pathMatch(.*)*", |  | ||||||
|   "403": "/403", |  | ||||||
|   "404": "/404", |  | ||||||
|   "500": "/500", |  | ||||||
|   "Home": "/home", |  | ||||||
|   "IframeUrl": "/iframe/:url", |  | ||||||
|   "Login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?", |  | ||||||
|   "ManageMenu": "/manage/menu", |  | ||||||
|   "ManageRole": "/manage/role", |  | ||||||
|   "ManageRoute": "/manage/route", |  | ||||||
|   "ManageUser": "/manage/user", |  | ||||||
|   "Wip": "/wip", |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export function getRoutePath(key: RouteKey) { |  | ||||||
|   return routePathMap[key]; |  | ||||||
| } |  | ||||||
| @@ -1,70 +0,0 @@ | |||||||
| /* eslint-disable */ |  | ||||||
| /* prettier-ignore */ |  | ||||||
| /* oxlint-disable */ |  | ||||||
| // biome-ignore lint: disable |  | ||||||
| // Generated by elegant-router |  | ||||||
| // Read more: https://github.com/soybeanjs/elegant-router |  | ||||||
|  |  | ||||||
| import type { RouteRecordRaw } from 'vue-router'; |  | ||||||
| import type { |  | ||||||
|   AutoRouterRedirect, |  | ||||||
|   AutoRouterRoute, |  | ||||||
|   AutoRouterSingleView, |  | ||||||
|   RawRouteComponent, |  | ||||||
|   RouteFileKey, |  | ||||||
|   RouteLayoutKey |  | ||||||
| } from '@elegant-router/types'; |  | ||||||
|  |  | ||||||
| export function transformToVueRoutes( |  | ||||||
|   routes: AutoRouterRoute[], |  | ||||||
|   layouts: Record<RouteLayoutKey, RawRouteComponent>, |  | ||||||
|   views: Record<RouteFileKey, RawRouteComponent> |  | ||||||
| ) { |  | ||||||
|   const { redirects, groupedRoutes } = getFormattedRoutes(routes); |  | ||||||
|  |  | ||||||
|   const vueRoutes: RouteRecordRaw[] = [...redirects]; |  | ||||||
|  |  | ||||||
|   groupedRoutes.forEach((items, layout) => { |  | ||||||
|     const layoutRoute: RouteRecordRaw = { |  | ||||||
|       path: `/${layout}-layout`, |  | ||||||
|       component: layouts[layout], |  | ||||||
|       children: items.map(item => { |  | ||||||
|         const { layout: _, component, ...rest } = item; |  | ||||||
|  |  | ||||||
|         return { |  | ||||||
|           component: views[component], |  | ||||||
|           ...rest |  | ||||||
|         }; |  | ||||||
|       }) |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     vueRoutes.push(layoutRoute); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   return vueRoutes; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function getFormattedRoutes(routes: AutoRouterRoute[]) { |  | ||||||
|   const groupedRoutes = new Map<RouteLayoutKey, AutoRouterSingleView[]>(); |  | ||||||
|   const redirects: AutoRouterRedirect[] = []; |  | ||||||
|  |  | ||||||
|   routes.forEach(route => { |  | ||||||
|     if (isAutoRouterRedirect(route)) { |  | ||||||
|       redirects.push(route); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const items = groupedRoutes.get(route.layout) || []; |  | ||||||
|     items.push(route); |  | ||||||
|     groupedRoutes.set(route.layout, items); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   return { |  | ||||||
|     redirects, |  | ||||||
|     groupedRoutes |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function isAutoRouterRedirect(route: AutoRouterRoute): route is AutoRouterRedirect { |  | ||||||
|   return 'redirect' in route; |  | ||||||
| } |  | ||||||
							
								
								
									
										24
									
								
								src/router/elegant/imports.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/router/elegant/imports.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | /* eslint-disable */ | ||||||
|  | /* prettier-ignore */ | ||||||
|  | // Generated by elegant-router | ||||||
|  | // Read more: https://github.com/soybeanjs/elegant-router | ||||||
|  |  | ||||||
|  | import type { RouteComponent } from "vue-router"; | ||||||
|  | import type { LastLevelRouteKey, RouteLayout } from "@elegant-router/types"; | ||||||
|  |  | ||||||
|  | import BaseLayout from "@/layouts/base-layout/index.vue"; | ||||||
|  | import BlankLayout from "@/layouts/blank-layout/index.vue"; | ||||||
|  |  | ||||||
|  | export const layouts: Record<RouteLayout, RouteComponent | (() => Promise<RouteComponent>)> = { | ||||||
|  |   base: BaseLayout, | ||||||
|  |   blank: BlankLayout, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<RouteComponent>)> = { | ||||||
|  |   403: () => import("@/views/_builtin/403/index.vue"), | ||||||
|  |   404: () => import("@/views/_builtin/404/index.vue"), | ||||||
|  |   500: () => import("@/views/_builtin/500/index.vue"), | ||||||
|  |   "iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"), | ||||||
|  |   login: () => import("@/views/_builtin/login/index.vue"), | ||||||
|  |   home: () => import("@/views/home/index.vue"), | ||||||
|  | }; | ||||||
							
								
								
									
										78
									
								
								src/router/elegant/routes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/router/elegant/routes.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | /* eslint-disable */ | ||||||
|  | /* prettier-ignore */ | ||||||
|  | // Generated by elegant-router | ||||||
|  | // Read more: https://github.com/soybeanjs/elegant-router | ||||||
|  |  | ||||||
|  | import type { GeneratedRoute } from '@elegant-router/types'; | ||||||
|  |  | ||||||
|  | export const generatedRoutes: GeneratedRoute[] = [ | ||||||
|  |   { | ||||||
|  |     name: '403', | ||||||
|  |     path: '/403', | ||||||
|  |     component: 'layout.blank$view.403', | ||||||
|  |     meta: { | ||||||
|  |       title: '403', | ||||||
|  |       i18nKey: 'route.403', | ||||||
|  |       constant: true, | ||||||
|  |       hideInMenu: true | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: '404', | ||||||
|  |     path: '/404', | ||||||
|  |     component: 'layout.blank$view.404', | ||||||
|  |     meta: { | ||||||
|  |       title: '404', | ||||||
|  |       i18nKey: 'route.404', | ||||||
|  |       constant: true, | ||||||
|  |       hideInMenu: true | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: '500', | ||||||
|  |     path: '/500', | ||||||
|  |     component: 'layout.blank$view.500', | ||||||
|  |     meta: { | ||||||
|  |       title: '500', | ||||||
|  |       i18nKey: 'route.500', | ||||||
|  |       constant: true, | ||||||
|  |       hideInMenu: true | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: 'home', | ||||||
|  |     path: '/home', | ||||||
|  |     component: 'layout.base$view.home', | ||||||
|  |     meta: { | ||||||
|  |       title: 'home', | ||||||
|  |       i18nKey: 'route.home', | ||||||
|  |       icon: 'mdi:monitor-dashboard', | ||||||
|  |       order: 1 | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: 'iframe-page', | ||||||
|  |     path: '/iframe-page/:url', | ||||||
|  |     component: 'layout.base$view.iframe-page', | ||||||
|  |     props: true, | ||||||
|  |     meta: { | ||||||
|  |       title: 'iframe-page', | ||||||
|  |       i18nKey: 'route.iframe-page', | ||||||
|  |       constant: true, | ||||||
|  |       hideInMenu: true, | ||||||
|  |       keepAlive: true | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: 'login', | ||||||
|  |     path: '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?', | ||||||
|  |     component: 'layout.blank$view.login', | ||||||
|  |     props: true, | ||||||
|  |     meta: { | ||||||
|  |       title: 'login', | ||||||
|  |       i18nKey: 'route.login', | ||||||
|  |       constant: true, | ||||||
|  |       hideInMenu: true | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | ]; | ||||||
							
								
								
									
										192
									
								
								src/router/elegant/transform.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								src/router/elegant/transform.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | |||||||
|  | /* eslint-disable */ | ||||||
|  | /* prettier-ignore */ | ||||||
|  | // Generated by elegant-router | ||||||
|  | // Read more: https://github.com/soybeanjs/elegant-router | ||||||
|  |  | ||||||
|  | import type { RouteRecordRaw, RouteComponent } from 'vue-router'; | ||||||
|  | import type { ElegantConstRoute } from '@elegant-router/vue'; | ||||||
|  | import type { RouteMap, RouteKey, RoutePath } from '@elegant-router/types'; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * transform elegant const routes to vue routes | ||||||
|  |  * @param routes elegant const routes | ||||||
|  |  * @param layouts layout components | ||||||
|  |  * @param views view components | ||||||
|  |  */ | ||||||
|  | export function transformElegantRoutesToVueRoutes( | ||||||
|  |   routes: ElegantConstRoute[], | ||||||
|  |   layouts: Record<string, RouteComponent | (() => Promise<RouteComponent>)>, | ||||||
|  |   views: Record<string, RouteComponent | (() => Promise<RouteComponent>)> | ||||||
|  | ) { | ||||||
|  |   return routes.flatMap(route => transformElegantRouteToVueRoute(route, layouts, views)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * transform elegant route to vue route | ||||||
|  |  * @param route elegant const route | ||||||
|  |  * @param layouts layout components | ||||||
|  |  * @param views view components | ||||||
|  |  */ | ||||||
|  | function transformElegantRouteToVueRoute( | ||||||
|  |   route: ElegantConstRoute, | ||||||
|  |   layouts: Record<string, RouteComponent | (() => Promise<RouteComponent>)>, | ||||||
|  |   views: Record<string, RouteComponent | (() => Promise<RouteComponent>)> | ||||||
|  | ) { | ||||||
|  |   const LAYOUT_PREFIX = 'layout.'; | ||||||
|  |   const VIEW_PREFIX = 'view.'; | ||||||
|  |   const ROUTE_DEGREE_SPLITTER = '_'; | ||||||
|  |   const FIRST_LEVEL_ROUTE_COMPONENT_SPLIT = '$'; | ||||||
|  |  | ||||||
|  |   function isLayout(component: string) { | ||||||
|  |     return component.startsWith(LAYOUT_PREFIX); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function getLayoutName(component: string) { | ||||||
|  |     const layout = component.replace(LAYOUT_PREFIX, ''); | ||||||
|  |  | ||||||
|  |     if(!layouts[layout]) { | ||||||
|  |       throw new Error(`Layout component "${layout}" not found`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return layout; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function isView(component: string) { | ||||||
|  |     return component.startsWith(VIEW_PREFIX); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function getViewName(component: string) { | ||||||
|  |     const view = component.replace(VIEW_PREFIX, ''); | ||||||
|  |  | ||||||
|  |     if(!views[view]) { | ||||||
|  |       throw new Error(`View component "${view}" not found`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return view; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function isFirstLevelRoute(item: ElegantConstRoute) { | ||||||
|  |     return !item.name.includes(ROUTE_DEGREE_SPLITTER); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function isSingleLevelRoute(item: ElegantConstRoute) { | ||||||
|  |     return isFirstLevelRoute(item) && !item.children?.length; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function getSingleLevelRouteComponent(component: string) { | ||||||
|  |     const [layout, view] = component.split(FIRST_LEVEL_ROUTE_COMPONENT_SPLIT); | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |       layout: getLayoutName(layout), | ||||||
|  |       view: getViewName(view) | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const vueRoutes: RouteRecordRaw[] = []; | ||||||
|  |  | ||||||
|  |   // add props: true to route | ||||||
|  |   if (route.path.includes(':') && !route.props) { | ||||||
|  |     route.props = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const { name, path, component, children, ...rest } = route; | ||||||
|  |  | ||||||
|  |   const vueRoute = { name, path, ...rest } as RouteRecordRaw; | ||||||
|  |  | ||||||
|  |   try { | ||||||
|  |     if (component) { | ||||||
|  |       if (isSingleLevelRoute(route)) { | ||||||
|  |         const { layout, view } = getSingleLevelRouteComponent(component); | ||||||
|  |  | ||||||
|  |         const singleLevelRoute: RouteRecordRaw = { | ||||||
|  |           path, | ||||||
|  |           component: layouts[layout], | ||||||
|  |           meta: { | ||||||
|  |             title: route.meta?.title || '' | ||||||
|  |           }, | ||||||
|  |           children: [ | ||||||
|  |             { | ||||||
|  |               name, | ||||||
|  |               path: '', | ||||||
|  |               component: views[view], | ||||||
|  |               ...rest | ||||||
|  |             } as RouteRecordRaw | ||||||
|  |           ] | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         return [singleLevelRoute]; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (isLayout(component)) { | ||||||
|  |         const layoutName = getLayoutName(component); | ||||||
|  |  | ||||||
|  |         vueRoute.component = layouts[layoutName]; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (isView(component)) { | ||||||
|  |         const viewName = getViewName(component); | ||||||
|  |  | ||||||
|  |         vueRoute.component = views[viewName]; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |   } catch (error: any) { | ||||||
|  |     console.error(`Error transforming route "${route.name}": ${error.toString()}`); | ||||||
|  |     return []; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // add redirect to child | ||||||
|  |   if (children?.length && !vueRoute.redirect) { | ||||||
|  |     vueRoute.redirect = { | ||||||
|  |       name: children[0].name | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (children?.length) { | ||||||
|  |     const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views)); | ||||||
|  |  | ||||||
|  |     if(isFirstLevelRoute(route)) { | ||||||
|  |       vueRoute.children = childRoutes; | ||||||
|  |     } else { | ||||||
|  |       vueRoutes.push(...childRoutes); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   vueRoutes.unshift(vueRoute); | ||||||
|  |  | ||||||
|  |   return vueRoutes; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * map of route name and route path | ||||||
|  |  */ | ||||||
|  | const routeMap: RouteMap = { | ||||||
|  |   "root": "/", | ||||||
|  |   "not-found": "/:pathMatch(.*)*", | ||||||
|  |   "403": "/403", | ||||||
|  |   "404": "/404", | ||||||
|  |   "500": "/500", | ||||||
|  |   "home": "/home", | ||||||
|  |   "iframe-page": "/iframe-page/:url", | ||||||
|  |   "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?" | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * get route path by route name | ||||||
|  |  * @param name route name | ||||||
|  |  */ | ||||||
|  | export function getRoutePath<T extends RouteKey>(name: T) { | ||||||
|  |   return routeMap[name]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * get route name by route path | ||||||
|  |  * @param path route path | ||||||
|  |  */ | ||||||
|  | export function getRouteName(path: RoutePath) { | ||||||
|  |   const routeEntries = Object.entries(routeMap) as [RouteKey, RoutePath][]; | ||||||
|  |  | ||||||
|  |   const routeName: RouteKey | null = routeEntries.find(([, routePath]) => routePath === path)?.[0] || null; | ||||||
|  |  | ||||||
|  |   return routeName; | ||||||
|  | } | ||||||
| @@ -1,6 +1,12 @@ | |||||||
| import type { App } from 'vue'; | import type { App } from 'vue'; | ||||||
| import { createMemoryHistory, createRouter, createWebHashHistory, createWebHistory } from 'vue-router'; | import { | ||||||
| import type { RouterHistory } from 'vue-router'; |   type RouterHistory, | ||||||
|  |   createMemoryHistory, | ||||||
|  |   createRouter, | ||||||
|  |   createWebHashHistory, | ||||||
|  |   createWebHistory | ||||||
|  | } from 'vue-router'; | ||||||
|  | import { createBuiltinVueRoutes } from './routes/builtin'; | ||||||
| import { createRouterGuard } from './guard'; | import { createRouterGuard } from './guard'; | ||||||
|  |  | ||||||
| const { VITE_ROUTER_HISTORY_MODE = 'history', VITE_BASE_URL } = import.meta.env; | const { VITE_ROUTER_HISTORY_MODE = 'history', VITE_BASE_URL } = import.meta.env; | ||||||
| @@ -13,7 +19,7 @@ const historyCreatorMap: Record<Env.RouterHistoryMode, (base?: string) => Router | |||||||
|  |  | ||||||
| export const router = createRouter({ | export const router = createRouter({ | ||||||
|   history: historyCreatorMap[VITE_ROUTER_HISTORY_MODE](VITE_BASE_URL), |   history: historyCreatorMap[VITE_ROUTER_HISTORY_MODE](VITE_BASE_URL), | ||||||
|   routes: [] |   routes: createBuiltinVueRoutes() | ||||||
| }); | }); | ||||||
|  |  | ||||||
| /** Setup Vue Router */ | /** Setup Vue Router */ | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								src/router/routes/builtin.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/router/routes/builtin.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | import type { CustomRoute } from '@elegant-router/types'; | ||||||
|  | import { layouts, views } from '../elegant/imports'; | ||||||
|  | import { getRoutePath, transformElegantRoutesToVueRoutes } from '../elegant/transform'; | ||||||
|  |  | ||||||
|  | export const ROOT_ROUTE: CustomRoute = { | ||||||
|  |   name: 'root', | ||||||
|  |   path: '/', | ||||||
|  |   redirect: getRoutePath(import.meta.env.VITE_ROUTE_HOME) || '/home', | ||||||
|  |   meta: { | ||||||
|  |     title: 'root', | ||||||
|  |     constant: true | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const NOT_FOUND_ROUTE: CustomRoute = { | ||||||
|  |   name: 'not-found', | ||||||
|  |   path: '/:pathMatch(.*)*', | ||||||
|  |   component: 'layout.blank$view.404', | ||||||
|  |   meta: { | ||||||
|  |     title: 'not-found', | ||||||
|  |     constant: true | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** builtin routes, it must be constant and setup in vue-router */ | ||||||
|  | const builtinRoutes: CustomRoute[] = [ROOT_ROUTE, NOT_FOUND_ROUTE]; | ||||||
|  |  | ||||||
|  | /** create builtin vue routes */ | ||||||
|  | export function createBuiltinVueRoutes() { | ||||||
|  |   return transformElegantRoutesToVueRoutes(builtinRoutes, layouts, views); | ||||||
|  | } | ||||||
| @@ -1,39 +1,40 @@ | |||||||
| import type { AutoRouterRedirect, AutoRouterRoute } from '@elegant-router/types'; | import type { CustomRoute, ElegantConstRoute, ElegantRoute } from '@elegant-router/types'; | ||||||
| import { routes } from '../_generated/routes'; | import { generatedRoutes } from '../elegant/routes'; | ||||||
| import { layouts, views } from '../_generated/imports'; | import { layouts, views } from '../elegant/imports'; | ||||||
| import { transformToVueRoutes } from '../_generated/transformer'; | import { transformElegantRoutesToVueRoutes } from '../elegant/transform'; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * custom routes | ||||||
|  |  * | ||||||
|  |  * @link https://github.com/soybeanjs/elegant-router?tab=readme-ov-file#custom-route | ||||||
|  |  */ | ||||||
|  | const customRoutes: CustomRoute[] = []; | ||||||
|  |  | ||||||
| /** create routes when the auth route mode is static */ | /** create routes when the auth route mode is static */ | ||||||
| export function createStaticRoutes() { | export function createStaticRoutes() { | ||||||
|   const constantRoutes: AutoRouterRoute[] = []; |   const constantRoutes: ElegantRoute[] = []; | ||||||
|   const authRoutes: AutoRouterRoute[] = []; |  | ||||||
|  |  | ||||||
|   let rootRoute: AutoRouterRedirect | undefined; |   const authRoutes: ElegantRoute[] = []; | ||||||
|  |  | ||||||
|   routes.forEach(item => { |   [...customRoutes, ...generatedRoutes].forEach(item => { | ||||||
|     if (item.meta?.constant) { |     if (item.meta?.constant) { | ||||||
|       constantRoutes.push(item); |       constantRoutes.push(item); | ||||||
|     } else { |     } else { | ||||||
|       authRoutes.push(item); |       authRoutes.push(item); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (item.name === 'Root') { |  | ||||||
|       rootRoute = item as AutoRouterRedirect; |  | ||||||
|     } |  | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   return { |   return { | ||||||
|     constantRoutes, |     constantRoutes, | ||||||
|     authRoutes, |     authRoutes | ||||||
|     rootRoute: rootRoute as AutoRouterRedirect |  | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Get auth vue routes |  * Get auth vue routes | ||||||
|  * |  * | ||||||
|  * @param authRoutes Elegant routes |  * @param routes Elegant routes | ||||||
|  */ |  */ | ||||||
| export function getAuthVueRoutes(authRoutes: AutoRouterRoute[]) { | export function getAuthVueRoutes(routes: ElegantConstRoute[]) { | ||||||
|   return transformToVueRoutes(authRoutes, layouts, views); |   return transformElegantRoutesToVueRoutes(routes, layouts, views); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => { | |||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   /** Naive theme */ |   /** Naive theme */ | ||||||
|   const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value.recommendColor)); |   const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value)); | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Settings json |    * Settings json | ||||||
|   | |||||||
| @@ -236,22 +236,23 @@ function getNaiveThemeColors(colors: App.Theme.ThemeColor, recommended = false) | |||||||
| /** | /** | ||||||
|  * Get naive theme |  * Get naive theme | ||||||
|  * |  * | ||||||
|  * @param colors Theme colors |  * @param settings Theme settings object. | ||||||
|  * @param [recommended=false] Use recommended color. Default is `false` |  * @param settings.recommendColor Whether to use recommended color palette. | ||||||
|  |  * @param settings.themeRadius Border radius to use in the theme (in px). | ||||||
|  */ |  */ | ||||||
| export function getNaiveTheme(colors: App.Theme.ThemeColor, recommended = false) { | export function getNaiveTheme(colors: App.Theme.ThemeColor, settings: App.Theme.ThemeSetting) { | ||||||
|   const { primary: colorLoading } = colors; |   const { primary: colorLoading } = colors; | ||||||
|  |  | ||||||
|   const theme: GlobalThemeOverrides = { |   const theme: GlobalThemeOverrides = { | ||||||
|     common: { |     common: { | ||||||
|       ...getNaiveThemeColors(colors, recommended), |       ...getNaiveThemeColors(colors, settings.recommendColor), | ||||||
|       borderRadius: '6px' |       borderRadius: `${settings.themeRadius}px` | ||||||
|     }, |     }, | ||||||
|     LoadingBar: { |     LoadingBar: { | ||||||
|       colorLoading |       colorLoading | ||||||
|     }, |     }, | ||||||
|     Tag: { |     Tag: { | ||||||
|       borderRadius: '6px' |       borderRadius: `${settings.themeRadius}px` | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ html, | |||||||
| body, | body, | ||||||
| #app { | #app { | ||||||
|   height: 100%; |   height: 100%; | ||||||
|  |   text-autospace: normal; | ||||||
| } | } | ||||||
|  |  | ||||||
| html { | html { | ||||||
|   | |||||||
| @@ -1,7 +1,14 @@ | |||||||
| @mixin scrollbar($size: 7px, $color: rgba(0, 0, 0, 0.5)) { | @mixin scrollbar($size: 7px, $color: rgba(0, 0, 0, 0.5), $dark-color: rgba(255, 255, 255, 0.5)) { | ||||||
|   scrollbar-width: thin; |   scrollbar-width: thin; | ||||||
|   scrollbar-color: $color transparent; |   scrollbar-color: $color transparent; | ||||||
|  |  | ||||||
|  |   // Dark theme override | ||||||
|  |   .dark & { | ||||||
|  |     // guide safari use light color scrollbar | ||||||
|  |     color-scheme: dark; | ||||||
|  |     scrollbar-color: $dark-color transparent; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   &::-webkit-scrollbar-thumb { |   &::-webkit-scrollbar-thumb { | ||||||
|     background-color: $color; |     background-color: $color; | ||||||
|     border-radius: $size; |     border-radius: $size; | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ | |||||||
|     "warning": "#d4bb9d", |     "warning": "#d4bb9d", | ||||||
|     "error": "#c49a9a" |     "error": "#c49a9a" | ||||||
|   }, |   }, | ||||||
|  |   "themeRadius": 6, | ||||||
|   "isInfoFollowPrimary": true, |   "isInfoFollowPrimary": true, | ||||||
|   "layout": { |   "layout": { | ||||||
|     "mode": "vertical-mix", |     "mode": "vertical-mix", | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ | |||||||
|     "warning": "#faad14", |     "warning": "#faad14", | ||||||
|     "error": "#f5222d" |     "error": "#f5222d" | ||||||
|   }, |   }, | ||||||
|  |   "themeRadius": 6, | ||||||
|   "isInfoFollowPrimary": true, |   "isInfoFollowPrimary": true, | ||||||
|   "layout": { |   "layout": { | ||||||
|     "mode": "vertical", |     "mode": "vertical", | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ | |||||||
|     "warning": "#faad14", |     "warning": "#faad14", | ||||||
|     "error": "#f5222d" |     "error": "#f5222d" | ||||||
|   }, |   }, | ||||||
|  |   "themeRadius": 6, | ||||||
|   "isInfoFollowPrimary": true, |   "isInfoFollowPrimary": true, | ||||||
|   "layout": { |   "layout": { | ||||||
|     "mode": "vertical", |     "mode": "vertical", | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ | |||||||
|     "warning": "#faad14", |     "warning": "#faad14", | ||||||
|     "error": "#f5222d" |     "error": "#f5222d" | ||||||
|   }, |   }, | ||||||
|  |   "themeRadius": 6, | ||||||
|   "isInfoFollowPrimary": true, |   "isInfoFollowPrimary": true, | ||||||
|   "layout": { |   "layout": { | ||||||
|     "mode": "vertical", |     "mode": "vertical", | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ export const themeSettings: App.Theme.ThemeSetting = { | |||||||
|   colourWeakness: false, |   colourWeakness: false, | ||||||
|   recommendColor: false, |   recommendColor: false, | ||||||
|   themeColor: '#646cff', |   themeColor: '#646cff', | ||||||
|  |   themeRadius: 6, | ||||||
|   otherColor: { |   otherColor: { | ||||||
|     info: '#2080f0', |     info: '#2080f0', | ||||||
|     success: '#52c41a', |     success: '#52c41a', | ||||||
| @@ -37,7 +38,8 @@ export const themeSettings: App.Theme.ThemeSetting = { | |||||||
|     visible: true, |     visible: true, | ||||||
|     cache: true, |     cache: true, | ||||||
|     height: 44, |     height: 44, | ||||||
|     mode: 'chrome' |     mode: 'chrome', | ||||||
|  |     closeTabByMiddleClick: false | ||||||
|   }, |   }, | ||||||
|   fixedHeaderAndTab: true, |   fixedHeaderAndTab: true, | ||||||
|   sider: { |   sider: { | ||||||
|   | |||||||
							
								
								
									
										70
									
								
								src/typings/api/common.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								src/typings/api/common.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -5,38 +5,46 @@ | |||||||
|  */ |  */ | ||||||
| declare namespace Api { | declare namespace Api { | ||||||
|   namespace Common { |   namespace Common { | ||||||
|     interface CreatedUpdatedDTO { |     /** common params of paginating */ | ||||||
|       /** 创建人 */ |     interface PaginatingCommonParams { | ||||||
|       createdBy: string; |       /** current page number */ | ||||||
|       /** 创建时间 */ |       current: number; | ||||||
|       createdTime: string; |       /** page size */ | ||||||
|       /** 更新人 */ |       size: number; | ||||||
|       updatedBy: string; |       /** total count */ | ||||||
|       /** 更新时间 */ |  | ||||||
|       updatedTime: string; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface CommonRecordDTO extends CreatedUpdatedDTO { |  | ||||||
|       /** 主键 */ |  | ||||||
|       id: number; |  | ||||||
|       /** |  | ||||||
|        * 启用状态 Enable status |  | ||||||
|        * |  | ||||||
|        * - "1": 启用 |  | ||||||
|        * - "2": 禁用 |  | ||||||
|        */ |  | ||||||
|       status: Union.EnableStatus | null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface PaginationQueryDTO { |  | ||||||
|       page: number; |  | ||||||
|       pageSize: number; |  | ||||||
|       sort: string; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface PaginationDTO extends Pick<PaginationQueryDTO, 'page' | 'pageSize'> { |  | ||||||
|       /** 总条数 */ |  | ||||||
|       total: number; |       total: number; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** common params of paginating query list data */ | ||||||
|  |     interface PaginatingQueryRecord<T = any> extends PaginatingCommonParams { | ||||||
|  |       records: T[]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** common search params of table */ | ||||||
|  |     type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'current' | 'size'>; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * enable status | ||||||
|  |      * | ||||||
|  |      * - "1": enabled | ||||||
|  |      * - "2": disabled | ||||||
|  |      */ | ||||||
|  |     type EnableStatus = '1' | '2'; | ||||||
|  |  | ||||||
|  |     /** common record */ | ||||||
|  |     type CommonRecord<T = any> = { | ||||||
|  |       /** record id */ | ||||||
|  |       id: number; | ||||||
|  |       /** record creator */ | ||||||
|  |       createBy: string; | ||||||
|  |       /** record create time */ | ||||||
|  |       createTime: string; | ||||||
|  |       /** record updater */ | ||||||
|  |       updateBy: string; | ||||||
|  |       /** record update time */ | ||||||
|  |       updateTime: string; | ||||||
|  |       /** record status */ | ||||||
|  |       status: EnableStatus | null; | ||||||
|  |     } & T; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								src/typings/api/menu.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								src/typings/api/menu.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,42 +0,0 @@ | |||||||
| declare namespace Api { |  | ||||||
|   namespace Menu { |  | ||||||
|     type RouteKey = import('@elegant-router/types').RouteKey; |  | ||||||
|  |  | ||||||
|     interface Menu extends Common.CommonRecordDTO { |  | ||||||
|       /** 菜单名称 */ |  | ||||||
|       name: string; |  | ||||||
|       /** 菜单类型 */ |  | ||||||
|       menuType: Union.MenuType; |  | ||||||
|       /** 菜单描述 */ |  | ||||||
|       description?: string | null; |  | ||||||
|       /** 菜单图标 */ |  | ||||||
|       icon?: string | null; |  | ||||||
|       /** 菜单图标类型 */ |  | ||||||
|       iconType?: string | null; |  | ||||||
|       /** 父级菜单ID */ |  | ||||||
|       parentId?: number | null; |  | ||||||
|       /** 菜单排序 */ |  | ||||||
|       order?: number | null; |  | ||||||
|       /** 是否隐藏 */ |  | ||||||
|       isHidden?: boolean | null; |  | ||||||
|       /** iframe 页面URL */ |  | ||||||
|       iframeUrl?: string | null; |  | ||||||
|       /** 链接 */ |  | ||||||
|       href?: string | null; |  | ||||||
|       /** 路由名称 */ |  | ||||||
|       routeName?: RouteKey | null; |  | ||||||
|       /** 路由查询参数 */ |  | ||||||
|       routeQueries?: Record<string, string | number | boolean> | null; |  | ||||||
|       /** 路由路径参数 */ |  | ||||||
|       routeParams?: Record<string, string | number | boolean> | null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface MenuListDTO extends Common.PaginationDTO { |  | ||||||
|       list: Menu[]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface MenuTreeDTO extends Menu { |  | ||||||
|       children?: MenuTreeDTO[]; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										39
									
								
								src/typings/api/route.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								src/typings/api/route.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,34 +1,19 @@ | |||||||
| declare namespace Api { | declare namespace Api { | ||||||
|  |   /** | ||||||
|  |    * namespace Route | ||||||
|  |    * | ||||||
|  |    * backend api module: "route" | ||||||
|  |    */ | ||||||
|   namespace Route { |   namespace Route { | ||||||
|     interface RouteDTO extends Common.CommonRecordDTO { |     type ElegantConstRoute = import('@elegant-router/types').ElegantConstRoute; | ||||||
|       /** 路由路径 */ |  | ||||||
|       path: string; |     interface MenuRoute extends ElegantConstRoute { | ||||||
|       /** 路由名称 */ |       id: string; | ||||||
|       name: string; |  | ||||||
|       /** 路由布局 */ |  | ||||||
|       layout: string; |  | ||||||
|       /** 路由组件 */ |  | ||||||
|       component: string; |  | ||||||
|       /** 重定向路径 */ |  | ||||||
|       redirect?: string | null; |  | ||||||
|       /** 路由图标 */ |  | ||||||
|       icon?: string | null; |  | ||||||
|       /** 路由图标类型 */ |  | ||||||
|       iconType?: string | null; |  | ||||||
|       /** 是否缓存 */ |  | ||||||
|       isCache?: boolean | null; |  | ||||||
|       /** 是否支持多开 */ |  | ||||||
|       isMultiple?: boolean | null; |  | ||||||
|       /** 固定索引 */ |  | ||||||
|       fixedIndex?: number | null; |  | ||||||
|       /** 是否需要权限 */ |  | ||||||
|       requireAuth?: boolean | null; |  | ||||||
|       /** 路由属性 */ |  | ||||||
|       props?: Record<string, string | number | boolean> | null; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     interface RouteListDTO extends Common.PaginationDTO { |     interface UserRoute { | ||||||
|       list: RouteDTO[]; |       routes: MenuRoute[]; | ||||||
|  |       home: import('@elegant-router/types').LastLevelRouteKey; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								src/typings/api/union.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								src/typings/api/union.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,31 +0,0 @@ | |||||||
| declare namespace Api { |  | ||||||
|   namespace Union { |  | ||||||
|     /** |  | ||||||
|      * 是否 Yes or No |  | ||||||
|      * |  | ||||||
|      * - "Y": yes |  | ||||||
|      * - "N": no |  | ||||||
|      */ |  | ||||||
|     type YesOrNo = 'Y' | 'N'; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 启用状态 Enable status |  | ||||||
|      * |  | ||||||
|      * - "1": 启用 |  | ||||||
|      * - "2": 禁用 |  | ||||||
|      */ |  | ||||||
|     type EnableStatus = '1' | '2'; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 菜单类型 |  | ||||||
|      * |  | ||||||
|      * - "directory": 目录 |  | ||||||
|      * - "menu": 菜单 |  | ||||||
|      * - "iframe": iframe 页面(iframe 嵌入页面) |  | ||||||
|      * - "link": 链接(跳转外部链接) |  | ||||||
|      * - "page": 页面(不展示在菜单中,可用于作为菜单类型的子菜单展示在面包屑中) |  | ||||||
|      * - "other": 其他(作用自行定义) |  | ||||||
|      */ |  | ||||||
|     type MenuType = 'page' | 'directory' | 'menu' | 'iframe' | 'link' | 'other'; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										101
									
								
								src/typings/app.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										101
									
								
								src/typings/app.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -16,6 +16,8 @@ declare namespace App { | |||||||
|       recommendColor: boolean; |       recommendColor: boolean; | ||||||
|       /** Theme color */ |       /** Theme color */ | ||||||
|       themeColor: string; |       themeColor: string; | ||||||
|  |       /** Theme radius */ | ||||||
|  |       themeRadius: number; | ||||||
|       /** Other color */ |       /** Other color */ | ||||||
|       otherColor: OtherColor; |       otherColor: OtherColor; | ||||||
|       /** Whether info color is followed by the primary color */ |       /** Whether info color is followed by the primary color */ | ||||||
| @@ -69,6 +71,8 @@ declare namespace App { | |||||||
|         height: number; |         height: number; | ||||||
|         /** Tab mode */ |         /** Tab mode */ | ||||||
|         mode: UnionKey.ThemeTabMode; |         mode: UnionKey.ThemeTabMode; | ||||||
|  |         /** Whether to close tab by middle click */ | ||||||
|  |         closeTabByMiddleClick: boolean; | ||||||
|       }; |       }; | ||||||
|       /** Fixed header and tab */ |       /** Fixed header and tab */ | ||||||
|       fixedHeaderAndTab: boolean; |       fixedHeaderAndTab: boolean; | ||||||
| @@ -179,9 +183,9 @@ declare namespace App { | |||||||
|     type VNode = import('vue').VNode; |     type VNode = import('vue').VNode; | ||||||
|     type RouteLocationNormalizedLoaded = import('vue-router').RouteLocationNormalizedLoaded; |     type RouteLocationNormalizedLoaded = import('vue-router').RouteLocationNormalizedLoaded; | ||||||
|     type RouteKey = import('@elegant-router/types').RouteKey; |     type RouteKey = import('@elegant-router/types').RouteKey; | ||||||
|     type RoutePathMap = import('@elegant-router/types').RoutePathMap; |     type RouteMap = import('@elegant-router/types').RouteMap; | ||||||
|     type RoutePath = import('@elegant-router/types').RoutePath; |     type RoutePath = import('@elegant-router/types').RoutePath; | ||||||
|     type RouteFileKey = import('@elegant-router/types').RouteFileKey; |     type LastLevelRouteKey = import('@elegant-router/types').LastLevelRouteKey; | ||||||
|  |  | ||||||
|     /** The router push options */ |     /** The router push options */ | ||||||
|     type RouterPushOptions = { |     type RouterPushOptions = { | ||||||
| @@ -199,25 +203,77 @@ declare namespace App { | |||||||
|       showMenu?: boolean; |       showMenu?: boolean; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     type MenuType = 'page' | 'directory' | 'menu' | 'iframe' | 'link' | 'button' | 'other'; |  | ||||||
|  |  | ||||||
|     /** The global menu */ |     /** The global menu */ | ||||||
|     interface Menu { |     type Menu = { | ||||||
|       id: number; |       /** | ||||||
|       name: string; |        * The menu key | ||||||
|       menuType: MenuType; |        * | ||||||
|       description?: string | null; |        * Equal to the route key | ||||||
|       icon?: string | null; |        */ | ||||||
|       iconType?: string | null; |       key: string; | ||||||
|       parentId?: number | null; |       /** The menu label */ | ||||||
|       order?: number | null; |       label: string; | ||||||
|       isHidden?: boolean | null; |       /** The menu i18n key */ | ||||||
|       iframeUrl?: string | null; |       i18nKey?: I18n.I18nKey | null; | ||||||
|       href?: string | null; |       /** The route key */ | ||||||
|       routeName?: RouteKey | null; |       routeKey: RouteKey; | ||||||
|       routeQueries?: Record<string, string | number | boolean> | null; |       /** The route path */ | ||||||
|       routeParams?: Record<string, string | number | boolean> | null; |       routePath: RoutePath; | ||||||
|     } |       /** The menu icon */ | ||||||
|  |       icon?: () => VNode; | ||||||
|  |       /** The menu children */ | ||||||
|  |       children?: Menu[]; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     type Breadcrumb = Omit<Menu, 'children'> & { | ||||||
|  |       options?: Breadcrumb[]; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** Tab route */ | ||||||
|  |     type TabRoute = Pick<RouteLocationNormalizedLoaded, 'name' | 'path' | 'meta'> & | ||||||
|  |       Partial<Pick<RouteLocationNormalizedLoaded, 'fullPath' | 'query' | 'matched'>>; | ||||||
|  |  | ||||||
|  |     /** The global tab */ | ||||||
|  |     type Tab = { | ||||||
|  |       /** The tab id */ | ||||||
|  |       id: string; | ||||||
|  |       /** The tab label */ | ||||||
|  |       label: string; | ||||||
|  |       /** | ||||||
|  |        * The new tab label | ||||||
|  |        * | ||||||
|  |        * If set, the tab label will be replaced by this value | ||||||
|  |        */ | ||||||
|  |       newLabel?: string; | ||||||
|  |       /** | ||||||
|  |        * The old tab label | ||||||
|  |        * | ||||||
|  |        * when reset the tab label, the tab label will be replaced by this value | ||||||
|  |        */ | ||||||
|  |       oldLabel?: string; | ||||||
|  |       /** The tab route key */ | ||||||
|  |       routeKey: LastLevelRouteKey; | ||||||
|  |       /** The tab route path */ | ||||||
|  |       routePath: RouteMap[LastLevelRouteKey]; | ||||||
|  |       /** The tab route full path */ | ||||||
|  |       fullPath: string; | ||||||
|  |       /** The tab fixed index */ | ||||||
|  |       fixedIndex?: number | null; | ||||||
|  |       /** | ||||||
|  |        * Tab icon | ||||||
|  |        * | ||||||
|  |        * Iconify icon | ||||||
|  |        */ | ||||||
|  |       icon?: string; | ||||||
|  |       /** | ||||||
|  |        * Tab local icon | ||||||
|  |        * | ||||||
|  |        * Local icon | ||||||
|  |        */ | ||||||
|  |       localIcon?: string; | ||||||
|  |       /** I18n key */ | ||||||
|  |       i18nKey?: I18n.I18nKey | null; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     /** Form rule */ |     /** Form rule */ | ||||||
|     type FormRule = import('naive-ui').FormItemRule; |     type FormRule = import('naive-ui').FormItemRule; | ||||||
| @@ -325,6 +381,9 @@ declare namespace App { | |||||||
|           } & Record<Theme.ThemeColorKey, string>; |           } & Record<Theme.ThemeColorKey, string>; | ||||||
|           recommendColor: string; |           recommendColor: string; | ||||||
|           recommendColorDesc: string; |           recommendColorDesc: string; | ||||||
|  |           themeRadius: { | ||||||
|  |             title: string; | ||||||
|  |           }; | ||||||
|           preset: { |           preset: { | ||||||
|             title: string; |             title: string; | ||||||
|             apply: string; |             apply: string; | ||||||
| @@ -348,6 +407,8 @@ declare namespace App { | |||||||
|             cacheTip: string; |             cacheTip: string; | ||||||
|             height: string; |             height: string; | ||||||
|             mode: { title: string } & Record<UnionKey.ThemeTabMode, string>; |             mode: { title: string } & Record<UnionKey.ThemeTabMode, string>; | ||||||
|  |             closeByMiddleClick: string; | ||||||
|  |             closeByMiddleClickTip: string; | ||||||
|           }; |           }; | ||||||
|           header: { |           header: { | ||||||
|             title: string; |             title: string; | ||||||
|   | |||||||
							
								
								
									
										83
									
								
								src/typings/components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										83
									
								
								src/typings/components.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,12 @@ | |||||||
| /* eslint-disable */ | /* eslint-disable */ | ||||||
| // @ts-nocheck | // @ts-nocheck | ||||||
|  | // biome-ignore lint: disable | ||||||
|  | // oxlint-disable | ||||||
|  | // ------ | ||||||
| // Generated by unplugin-vue-components | // Generated by unplugin-vue-components | ||||||
| // Read more: https://github.com/vuejs/core/pull/3399 | // Read more: https://github.com/vuejs/core/pull/3399 | ||||||
| // biome-ignore lint: disable | import { GlobalComponents } from 'vue' | ||||||
|  |  | ||||||
| export {} | export {} | ||||||
|  |  | ||||||
| /* prettier-ignore */ | /* prettier-ignore */ | ||||||
| @@ -66,7 +70,6 @@ declare module 'vue' { | |||||||
|     NSwitch: typeof import('naive-ui')['NSwitch'] |     NSwitch: typeof import('naive-ui')['NSwitch'] | ||||||
|     NTab: typeof import('naive-ui')['NTab'] |     NTab: typeof import('naive-ui')['NTab'] | ||||||
|     NTabs: typeof import('naive-ui')['NTabs'] |     NTabs: typeof import('naive-ui')['NTabs'] | ||||||
|     NTag: typeof import('naive-ui')['NTag'] |  | ||||||
|     NThing: typeof import('naive-ui')['NThing'] |     NThing: typeof import('naive-ui')['NThing'] | ||||||
|     NTooltip: typeof import('naive-ui')['NTooltip'] |     NTooltip: typeof import('naive-ui')['NTooltip'] | ||||||
|     NWatermark: typeof import('naive-ui')['NWatermark'] |     NWatermark: typeof import('naive-ui')['NWatermark'] | ||||||
| @@ -83,3 +86,79 @@ declare module 'vue' { | |||||||
|     WaveBg: typeof import('./../components/custom/wave-bg.vue')['default'] |     WaveBg: typeof import('./../components/custom/wave-bg.vue')['default'] | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // For TSX support | ||||||
|  | declare global { | ||||||
|  |   const AppProvider: typeof import('./../components/common/app-provider.vue')['default'] | ||||||
|  |   const BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default'] | ||||||
|  |   const ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default'] | ||||||
|  |   const CountTo: typeof import('./../components/custom/count-to.vue')['default'] | ||||||
|  |   const DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default'] | ||||||
|  |   const ExceptionBase: typeof import('./../components/common/exception-base.vue')['default'] | ||||||
|  |   const FullScreen: typeof import('./../components/common/full-screen.vue')['default'] | ||||||
|  |   const IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default'] | ||||||
|  |   const IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default'] | ||||||
|  |   const IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default'] | ||||||
|  |   const IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default'] | ||||||
|  |   const IconLocalBanner: typeof import('~icons/local/banner')['default'] | ||||||
|  |   const IconLocalLogo: typeof import('~icons/local/logo')['default'] | ||||||
|  |   const IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default'] | ||||||
|  |   const IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default'] | ||||||
|  |   const IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default'] | ||||||
|  |   const IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default'] | ||||||
|  |   const IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default'] | ||||||
|  |   const IconUilSearch: typeof import('~icons/uil/search')['default'] | ||||||
|  |   const LangSwitch: typeof import('./../components/common/lang-switch.vue')['default'] | ||||||
|  |   const LookForward: typeof import('./../components/custom/look-forward.vue')['default'] | ||||||
|  |   const MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default'] | ||||||
|  |   const NAlert: typeof import('naive-ui')['NAlert'] | ||||||
|  |   const NBadge: typeof import('naive-ui')['NBadge'] | ||||||
|  |   const NBreadcrumb: typeof import('naive-ui')['NBreadcrumb'] | ||||||
|  |   const NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem'] | ||||||
|  |   const NButton: typeof import('naive-ui')['NButton'] | ||||||
|  |   const NCard: typeof import('naive-ui')['NCard'] | ||||||
|  |   const NCheckbox: typeof import('naive-ui')['NCheckbox'] | ||||||
|  |   const NColorPicker: typeof import('naive-ui')['NColorPicker'] | ||||||
|  |   const NDialogProvider: typeof import('naive-ui')['NDialogProvider'] | ||||||
|  |   const NDivider: typeof import('naive-ui')['NDivider'] | ||||||
|  |   const NDrawer: typeof import('naive-ui')['NDrawer'] | ||||||
|  |   const NDrawerContent: typeof import('naive-ui')['NDrawerContent'] | ||||||
|  |   const NDropdown: typeof import('naive-ui')['NDropdown'] | ||||||
|  |   const NEmpty: typeof import('naive-ui')['NEmpty'] | ||||||
|  |   const NForm: typeof import('naive-ui')['NForm'] | ||||||
|  |   const NFormItem: typeof import('naive-ui')['NFormItem'] | ||||||
|  |   const NGi: typeof import('naive-ui')['NGi'] | ||||||
|  |   const NGrid: typeof import('naive-ui')['NGrid'] | ||||||
|  |   const NInput: typeof import('naive-ui')['NInput'] | ||||||
|  |   const NInputGroup: typeof import('naive-ui')['NInputGroup'] | ||||||
|  |   const NInputNumber: typeof import('naive-ui')['NInputNumber'] | ||||||
|  |   const NList: typeof import('naive-ui')['NList'] | ||||||
|  |   const NListItem: typeof import('naive-ui')['NListItem'] | ||||||
|  |   const NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider'] | ||||||
|  |   const NMenu: typeof import('naive-ui')['NMenu'] | ||||||
|  |   const NMessageProvider: typeof import('naive-ui')['NMessageProvider'] | ||||||
|  |   const NModal: typeof import('naive-ui')['NModal'] | ||||||
|  |   const NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] | ||||||
|  |   const NPopover: typeof import('naive-ui')['NPopover'] | ||||||
|  |   const NScrollbar: typeof import('naive-ui')['NScrollbar'] | ||||||
|  |   const NSelect: typeof import('naive-ui')['NSelect'] | ||||||
|  |   const NSpace: typeof import('naive-ui')['NSpace'] | ||||||
|  |   const NStatistic: typeof import('naive-ui')['NStatistic'] | ||||||
|  |   const NSwitch: typeof import('naive-ui')['NSwitch'] | ||||||
|  |   const NTab: typeof import('naive-ui')['NTab'] | ||||||
|  |   const NTabs: typeof import('naive-ui')['NTabs'] | ||||||
|  |   const NThing: typeof import('naive-ui')['NThing'] | ||||||
|  |   const NTooltip: typeof import('naive-ui')['NTooltip'] | ||||||
|  |   const NWatermark: typeof import('naive-ui')['NWatermark'] | ||||||
|  |   const PinToggler: typeof import('./../components/common/pin-toggler.vue')['default'] | ||||||
|  |   const ReloadButton: typeof import('./../components/common/reload-button.vue')['default'] | ||||||
|  |   const RouterLink: typeof import('vue-router')['RouterLink'] | ||||||
|  |   const RouterView: typeof import('vue-router')['RouterView'] | ||||||
|  |   const SoybeanAvatar: typeof import('./../components/custom/soybean-avatar.vue')['default'] | ||||||
|  |   const SvgIcon: typeof import('./../components/custom/svg-icon.vue')['default'] | ||||||
|  |   const SystemLogo: typeof import('./../components/common/system-logo.vue')['default'] | ||||||
|  |   const TableColumnSetting: typeof import('./../components/advanced/table-column-setting.vue')['default'] | ||||||
|  |   const TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default'] | ||||||
|  |   const ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default'] | ||||||
|  |   const WaveBg: typeof import('./../components/custom/wave-bg.vue')['default'] | ||||||
|  | } | ||||||
							
								
								
									
										236
									
								
								src/typings/elegant-router.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										236
									
								
								src/typings/elegant-router.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,100 +1,240 @@ | |||||||
| /* eslint-disable */ | /* eslint-disable */ | ||||||
| /* prettier-ignore */ | /* prettier-ignore */ | ||||||
| /* oxlint-disable */ |  | ||||||
| // biome-ignore lint: disable |  | ||||||
| // Generated by elegant-router | // Generated by elegant-router | ||||||
| // Read more: https://github.com/soybeanjs/elegant-router | // Read more: https://github.com/soybeanjs/elegant-router | ||||||
|  |  | ||||||
| declare module "@elegant-router/types" { | declare module "@elegant-router/types" { | ||||||
|   type RouteRecordSingleView = import("vue-router").RouteRecordSingleView; |   type ElegantConstRoute = import('@elegant-router/vue').ElegantConstRoute; | ||||||
|   type RouteRecordRedirect = import("vue-router").RouteRecordRedirect; |  | ||||||
|   type RouteComponent = import("vue-router").RouteComponent; |  | ||||||
|  |  | ||||||
|   type Lazy<T> = () => Promise<T>; |  | ||||||
|  |  | ||||||
|   export type RawRouteComponent = RouteComponent | Lazy<RouteComponent>; |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * route layout key |    * route layout | ||||||
|    */ |    */ | ||||||
|   export type RouteLayoutKey = "base" | "blank"; |   export type RouteLayout = "base" | "blank"; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * route path map |    * route map | ||||||
|    */ |    */ | ||||||
|   export type RoutePathMap = { |   export type RouteMap = { | ||||||
|     "Root": "/"; |     "root": "/"; | ||||||
|     "NotFound": "/:pathMatch(.*)*"; |     "not-found": "/:pathMatch(.*)*"; | ||||||
|     "403": "/403"; |     "403": "/403"; | ||||||
|     "404": "/404"; |     "404": "/404"; | ||||||
|     "500": "/500"; |     "500": "/500"; | ||||||
|     "Home": "/home"; |     "home": "/home"; | ||||||
|     "IframeUrl": "/iframe/:url"; |     "iframe-page": "/iframe-page/:url"; | ||||||
|     "Login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"; |     "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"; | ||||||
|     "ManageMenu": "/manage/menu"; |  | ||||||
|     "ManageRole": "/manage/role"; |  | ||||||
|     "ManageRoute": "/manage/route"; |  | ||||||
|     "ManageUser": "/manage/user"; |  | ||||||
|     "Wip": "/wip"; |  | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * route key |    * route key | ||||||
|    */ |    */ | ||||||
|   export type RouteKey = keyof RoutePathMap; |   export type RouteKey = keyof RouteMap; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * route path |    * route path | ||||||
|    */ |    */ | ||||||
|   export type RoutePath = RoutePathMap[RouteKey]; |   export type RoutePath = RouteMap[RouteKey]; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * root route key |    * custom route key | ||||||
|    */ |    */ | ||||||
|   export type RootRouteKey = 'Root'; |   export type CustomRouteKey = Extract< | ||||||
|  |     RouteKey, | ||||||
|  |     | "root" | ||||||
|  |     | "not-found" | ||||||
|  |   >; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * not found route key |    * the generated route key | ||||||
|    */ |    */ | ||||||
|   export type NotFoundRouteKey = 'NotFound'; |   export type GeneratedRouteKey = Exclude<RouteKey, CustomRouteKey>; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * builtin route key |    * the first level route key, which contain the layout of the route | ||||||
|    */ |    */ | ||||||
|   export type BuiltinRouteKey = RootRouteKey | NotFoundRouteKey; |   export type FirstLevelRouteKey = Extract< | ||||||
|  |     RouteKey, | ||||||
|  |     | "403" | ||||||
|  |     | "404" | ||||||
|  |     | "500" | ||||||
|  |     | "home" | ||||||
|  |     | "iframe-page" | ||||||
|  |     | "login" | ||||||
|  |   >; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * reuse route key |    * the custom first level route key | ||||||
|    */ |    */ | ||||||
|   export type ReuseRouteKey = never; |   export type CustomFirstLevelRouteKey = Extract< | ||||||
|  |     CustomRouteKey, | ||||||
|  |     | "root" | ||||||
|  |     | "not-found" | ||||||
|  |   >; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * the route file key, which has it's own file |    * the last level route key, which has the page file | ||||||
|    */ |    */ | ||||||
|   export type RouteFileKey = Exclude<RouteKey, BuiltinRouteKey | ReuseRouteKey>; |   export type LastLevelRouteKey = Extract< | ||||||
|  |     RouteKey, | ||||||
|  |     | "403" | ||||||
|  |     | "404" | ||||||
|  |     | "500" | ||||||
|  |     | "iframe-page" | ||||||
|  |     | "login" | ||||||
|  |     | "home" | ||||||
|  |   >; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * mapped name and path |    * the custom last level route key | ||||||
|    */ |    */ | ||||||
|   type MappedNamePath = { |   export type CustomLastLevelRouteKey = Extract< | ||||||
|     [K in RouteKey]: { name: K; path: RoutePathMap[K] }; |     CustomRouteKey, | ||||||
|   }[RouteKey]; |     | "root" | ||||||
|  |     | "not-found" | ||||||
|  |   >; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * auto router single view |    * the single level route key | ||||||
|    */ |    */ | ||||||
|   export type AutoRouterSingleView = Omit<RouteRecordSingleView, 'component' | 'name' | 'path'> & { |   export type SingleLevelRouteKey = FirstLevelRouteKey & LastLevelRouteKey; | ||||||
|     component: RouteFileKey; |  | ||||||
|     layout: RouteLayoutKey; |  | ||||||
|   } & MappedNamePath; |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * auto router redirect |    * the custom single level route key | ||||||
|    */ |    */ | ||||||
|   export type AutoRouterRedirect = Omit<RouteRecordRedirect, 'children' | 'name' | 'path'> & MappedNamePath; |   export type CustomSingleLevelRouteKey = CustomFirstLevelRouteKey & CustomLastLevelRouteKey; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * auto router route |    * the first level route key, but not the single level | ||||||
|  |   */ | ||||||
|  |   export type FirstLevelRouteNotSingleKey = Exclude<FirstLevelRouteKey, SingleLevelRouteKey>; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the custom first level route key, but not the single level | ||||||
|    */ |    */ | ||||||
|   export type AutoRouterRoute = AutoRouterSingleView | AutoRouterRedirect; |   export type CustomFirstLevelRouteNotSingleKey = Exclude<CustomFirstLevelRouteKey, CustomSingleLevelRouteKey>; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the center level route key | ||||||
|  |    */ | ||||||
|  |   export type CenterLevelRouteKey = Exclude<GeneratedRouteKey, FirstLevelRouteKey | LastLevelRouteKey>; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the custom center level route key | ||||||
|  |    */ | ||||||
|  |   export type CustomCenterLevelRouteKey = Exclude<CustomRouteKey, CustomFirstLevelRouteKey | CustomLastLevelRouteKey>; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the center level route key | ||||||
|  |    */ | ||||||
|  |   type GetChildRouteKey<K extends RouteKey, T extends RouteKey = RouteKey> = T extends `${K}_${infer R}` | ||||||
|  |     ? R extends `${string}_${string}` | ||||||
|  |       ? never | ||||||
|  |       : T | ||||||
|  |     : never; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the single level route | ||||||
|  |    */ | ||||||
|  |   type SingleLevelRoute<K extends SingleLevelRouteKey = SingleLevelRouteKey> = K extends string | ||||||
|  |     ? Omit<ElegantConstRoute, 'children'> & { | ||||||
|  |         name: K; | ||||||
|  |         path: RouteMap[K]; | ||||||
|  |         component: `layout.${RouteLayout}$view.${K}`; | ||||||
|  |       } | ||||||
|  |     : never; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the last level route | ||||||
|  |    */ | ||||||
|  |   type LastLevelRoute<K extends GeneratedRouteKey> = K extends LastLevelRouteKey | ||||||
|  |     ? Omit<ElegantConstRoute, 'children'> & { | ||||||
|  |         name: K; | ||||||
|  |         path: RouteMap[K]; | ||||||
|  |         component: `view.${K}`; | ||||||
|  |       } | ||||||
|  |     : never; | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * the center level route | ||||||
|  |    */ | ||||||
|  |   type CenterLevelRoute<K extends GeneratedRouteKey> = K extends CenterLevelRouteKey | ||||||
|  |     ? Omit<ElegantConstRoute, 'component'> & { | ||||||
|  |         name: K; | ||||||
|  |         path: RouteMap[K]; | ||||||
|  |         children: (CenterLevelRoute<GetChildRouteKey<K>> | LastLevelRoute<GetChildRouteKey<K>>)[]; | ||||||
|  |       } | ||||||
|  |     : never; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the multi level route | ||||||
|  |    */ | ||||||
|  |   type MultiLevelRoute<K extends FirstLevelRouteNotSingleKey = FirstLevelRouteNotSingleKey> = K extends string | ||||||
|  |     ? ElegantConstRoute & { | ||||||
|  |         name: K; | ||||||
|  |         path: RouteMap[K]; | ||||||
|  |         component: `layout.${RouteLayout}`; | ||||||
|  |         children: (CenterLevelRoute<GetChildRouteKey<K>> | LastLevelRoute<GetChildRouteKey<K>>)[]; | ||||||
|  |       } | ||||||
|  |     : never; | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * the custom first level route | ||||||
|  |    */ | ||||||
|  |   type CustomSingleLevelRoute<K extends CustomFirstLevelRouteKey = CustomFirstLevelRouteKey> = K extends string | ||||||
|  |     ? Omit<ElegantConstRoute, 'children'> & { | ||||||
|  |         name: K; | ||||||
|  |         path: RouteMap[K]; | ||||||
|  |         component?: `layout.${RouteLayout}$view.${LastLevelRouteKey}`; | ||||||
|  |       } | ||||||
|  |     : never; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the custom last level route | ||||||
|  |    */ | ||||||
|  |   type CustomLastLevelRoute<K extends CustomRouteKey> = K extends CustomLastLevelRouteKey | ||||||
|  |     ? Omit<ElegantConstRoute, 'children'> & { | ||||||
|  |         name: K; | ||||||
|  |         path: RouteMap[K]; | ||||||
|  |         component?: `view.${LastLevelRouteKey}`; | ||||||
|  |       } | ||||||
|  |     : never; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the custom center level route | ||||||
|  |    */ | ||||||
|  |   type CustomCenterLevelRoute<K extends CustomRouteKey> = K extends CustomCenterLevelRouteKey | ||||||
|  |     ? Omit<ElegantConstRoute, 'component'> & { | ||||||
|  |         name: K; | ||||||
|  |         path: RouteMap[K]; | ||||||
|  |         children: (CustomCenterLevelRoute<GetChildRouteKey<K>> | CustomLastLevelRoute<GetChildRouteKey<K>>)[]; | ||||||
|  |       } | ||||||
|  |     : never; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the custom multi level route | ||||||
|  |    */ | ||||||
|  |   type CustomMultiLevelRoute<K extends CustomFirstLevelRouteNotSingleKey = CustomFirstLevelRouteNotSingleKey> = | ||||||
|  |     K extends string | ||||||
|  |       ? ElegantConstRoute & { | ||||||
|  |           name: K; | ||||||
|  |           path: RouteMap[K]; | ||||||
|  |           component: `layout.${RouteLayout}`; | ||||||
|  |           children: (CustomCenterLevelRoute<GetChildRouteKey<K>> | CustomLastLevelRoute<GetChildRouteKey<K>>)[]; | ||||||
|  |         } | ||||||
|  |       : never; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the custom route | ||||||
|  |    */ | ||||||
|  |   type CustomRoute = CustomSingleLevelRoute | CustomMultiLevelRoute; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the generated route | ||||||
|  |    */ | ||||||
|  |   type GeneratedRoute = SingleLevelRoute | MultiLevelRoute; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * the elegant route | ||||||
|  |    */ | ||||||
|  |   type ElegantRoute = GeneratedRoute | CustomRoute; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								src/typings/router.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								src/typings/router.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| export {}; | import 'vue-router'; | ||||||
|  |  | ||||||
| declare module 'vue-router' { | declare module 'vue-router' { | ||||||
|   export interface RouteMeta { |   interface RouteMeta { | ||||||
|     /** |     /** | ||||||
|      * Title of the route |      * Title of the route | ||||||
|      * |      * | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								src/typings/typed-router.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								src/typings/typed-router.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,40 +0,0 @@ | |||||||
| /* eslint-disable */ |  | ||||||
| // @ts-nocheck |  | ||||||
| /* prettier-ignore */ |  | ||||||
| /* oxlint-disable */ |  | ||||||
| // biome-ignore lint: disable |  | ||||||
| // Generated by elegant-router |  | ||||||
| // Read more: https://github.com/soybeanjs/elegant-router |  | ||||||
|  |  | ||||||
| export {} |  | ||||||
|  |  | ||||||
| declare module "vue-router" { |  | ||||||
|   type RouteNamedMap = import("vue-router/auto-routes").RouteNamedMap; |  | ||||||
|  |  | ||||||
|   export interface TypesConfig { |  | ||||||
|     RouteNamedMap: RouteNamedMap; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| declare module "vue-router/auto-routes" { |  | ||||||
|   import type { RouteParamsRawGeneric, RouteParamsGeneric, RouteMeta, RouteRecordInfo, ParamValue, ParamValueZeroOrOne } from "vue-router"; |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * route named map |  | ||||||
|   */ |  | ||||||
|   export interface RouteNamedMap { |  | ||||||
|     "Root": RouteRecordInfo<"Root", "/", Record<never, never>, Record<never, never>>; |  | ||||||
|     "NotFound": RouteRecordInfo<"NotFound", "/:pathMatch(.*)*", Record<never, never>, Record<never, never>>; |  | ||||||
|     "403": RouteRecordInfo<"403", "/403", Record<never, never>, Record<never, never>>; |  | ||||||
|     "404": RouteRecordInfo<"404", "/404", Record<never, never>, Record<never, never>>; |  | ||||||
|     "500": RouteRecordInfo<"500", "/500", Record<never, never>, Record<never, never>>; |  | ||||||
|     "Home": RouteRecordInfo<"Home", "/home", Record<never, never>, Record<never, never>>; |  | ||||||
|     "IframeUrl": RouteRecordInfo<"IframeUrl", "/iframe/:url", { url: ParamValue<true> }, { url: ParamValue<false> }>; |  | ||||||
|     "Login": RouteRecordInfo<"Login", "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?", { module?: ParamValueZeroOrOne<true> }, { module?: ParamValueZeroOrOne<false> }>; |  | ||||||
|     "ManageMenu": RouteRecordInfo<"ManageMenu", "/manage/menu", Record<never, never>, Record<never, never>>; |  | ||||||
|     "ManageRole": RouteRecordInfo<"ManageRole", "/manage/role", Record<never, never>, Record<never, never>>; |  | ||||||
|     "ManageRoute": RouteRecordInfo<"ManageRoute", "/manage/route", Record<never, never>, Record<never, never>>; |  | ||||||
|     "ManageUser": RouteRecordInfo<"ManageUser", "/manage/user", Record<never, never>, Record<never, never>>; |  | ||||||
|     "Wip": RouteRecordInfo<"Wip", "/wip", Record<never, never>, Record<never, never>> |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| <script setup lang="ts"></script> |  | ||||||
|  |  | ||||||
| <template> |  | ||||||
|   <LookForward /> |  | ||||||
| </template> |  | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { computed } from 'vue'; | import { computed } from 'vue'; | ||||||
| import { createReusableTemplate } from '@vueuse/core'; | import { createReusableTemplate } from '@vueuse/core'; | ||||||
|  | import { useThemeStore } from '@/store/modules/theme'; | ||||||
| import { $t } from '@/locales'; | import { $t } from '@/locales'; | ||||||
|  |  | ||||||
| defineOptions({ | defineOptions({ | ||||||
| @@ -72,6 +73,8 @@ interface GradientBgProps { | |||||||
|  |  | ||||||
| const [DefineGradientBg, GradientBg] = createReusableTemplate<GradientBgProps>(); | const [DefineGradientBg, GradientBg] = createReusableTemplate<GradientBgProps>(); | ||||||
|  |  | ||||||
|  | const themeStore = useThemeStore(); | ||||||
|  |  | ||||||
| function getGradientColor(color: CardData['color']) { | function getGradientColor(color: CardData['color']) { | ||||||
|   return `linear-gradient(to bottom right, ${color.start}, ${color.end})`; |   return `linear-gradient(to bottom right, ${color.start}, ${color.end})`; | ||||||
| } | } | ||||||
| @@ -81,7 +84,10 @@ function getGradientColor(color: CardData['color']) { | |||||||
|   <NCard :bordered="false" size="small" class="card-wrapper"> |   <NCard :bordered="false" size="small" class="card-wrapper"> | ||||||
|     <!-- define component start: GradientBg --> |     <!-- define component start: GradientBg --> | ||||||
|     <DefineGradientBg v-slot="{ $slots, gradientColor }"> |     <DefineGradientBg v-slot="{ $slots, gradientColor }"> | ||||||
|       <div class="rd-8px px-16px pb-4px pt-8px text-white" :style="{ backgroundImage: gradientColor }"> |       <div | ||||||
|  |         class="px-16px pb-4px pt-8px text-white" | ||||||
|  |         :style="{ backgroundImage: gradientColor, borderRadius: themeStore.themeRadius + 'px' }" | ||||||
|  |       > | ||||||
|         <component :is="$slots.default" /> |         <component :is="$slots.default" /> | ||||||
|       </div> |       </div> | ||||||
|     </DefineGradientBg> |     </DefineGradientBg> | ||||||
|   | |||||||
| @@ -21,13 +21,14 @@ const { domRef, updateOptions } = useEcharts(() => ({ | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   legend: { |   legend: { | ||||||
|     data: [$t('page.home.downloadCount'), $t('page.home.registerCount')] |     data: [$t('page.home.downloadCount'), $t('page.home.registerCount')], | ||||||
|  |     top: '0' | ||||||
|   }, |   }, | ||||||
|   grid: { |   grid: { | ||||||
|     left: '3%', |     left: '3%', | ||||||
|     right: '4%', |     right: '4%', | ||||||
|     bottom: '3%', |     bottom: '3%', | ||||||
|     containLabel: true |     top: '15%' | ||||||
|   }, |   }, | ||||||
|   xAxis: { |   xAxis: { | ||||||
|     type: 'category', |     type: 'category', | ||||||
|   | |||||||
| @@ -1,5 +0,0 @@ | |||||||
| <script setup lang="ts"></script> |  | ||||||
|  |  | ||||||
| <template> |  | ||||||
|   <div>ManageMenu</div> |  | ||||||
| </template> |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| <script setup lang="ts"></script> |  | ||||||
|  |  | ||||||
| <template> |  | ||||||
|   <div>ManageRole</div> |  | ||||||
| </template> |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| <script setup lang="ts"></script> |  | ||||||
|  |  | ||||||
| <template> |  | ||||||
|   <div>ManageRoute</div> |  | ||||||
| </template> |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| <script setup lang="ts"></script> |  | ||||||
|  |  | ||||||
| <template> |  | ||||||
|   <div>ManageUser</div> |  | ||||||
| </template> |  | ||||||
		Reference in New Issue
	
	Block a user