mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-12-08 01:05:59 +08:00
Compare commits
1 Commits
v2.0.1
...
v2.0-route
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
858d7625a4 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -33,3 +33,5 @@ package-lock.json
|
|||||||
yarn.lock
|
yarn.lock
|
||||||
|
|
||||||
.VSCodeCounter
|
.VSCodeCounter
|
||||||
|
|
||||||
|
.temp
|
||||||
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@@ -5,6 +5,7 @@
|
|||||||
"antfu.unocss",
|
"antfu.unocss",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"editorconfig.editorconfig",
|
"editorconfig.editorconfig",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
"lokalise.i18n-ally",
|
"lokalise.i18n-ally",
|
||||||
"mhutchie.git-graph",
|
"mhutchie.git-graph",
|
||||||
"mikestead.dotenv",
|
"mikestead.dotenv",
|
||||||
|
|||||||
325
CHANGELOG.md
325
CHANGELOG.md
@@ -1,331 +1,6 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
|
||||||
## [v2.0.1](https://github.com/soybeanjs/soybean-admin/compare/v2.0.0...v2.0.1) (2025-12-04)
|
|
||||||
|
|
||||||
### 🚀 Features
|
|
||||||
|
|
||||||
- **docs**:
|
|
||||||
- update QQ group image in README - by @soybeanjs [<samp>(46081)</samp>](https://github.com/soybeanjs/soybean-admin/commit/46081c36)
|
|
||||||
- **projects**:
|
|
||||||
- support theme presets to only set partial content. - by **Azir-11** [<samp>(9da84)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9da847fb)
|
|
||||||
- support theme perset to override component library presets. - by **Azir-11** [<samp>(60517)</samp>](https://github.com/soybeanjs/soybean-admin/commit/605173a1)
|
|
||||||
- support pinning and unpinning of tabs - by **hooke** [<samp>(b8a76)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b8a767d7)
|
|
||||||
- hybrid layout mode auto select first deepest child menu - by @paynezhuang [<samp>(94019)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9401925f)
|
|
||||||
|
|
||||||
### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- **docs**: update project name in ecosystem section of README - by @soybeanjs [<samp>(bb232)</samp>](https://github.com/soybeanjs/soybean-admin/commit/bb232bf8)
|
|
||||||
- **types**: add missing property in theme presets - by **刘璐** [<samp>(4a9cf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4a9cf6c3)
|
|
||||||
|
|
||||||
### 🛠 Optimizations
|
|
||||||
|
|
||||||
- **projects**: simplify some theme preset configurations. - by **Azir-11** [<samp>(c6d97)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c6d97dba)
|
|
||||||
|
|
||||||
### 📖 Documentation
|
|
||||||
|
|
||||||
- **projects**: add link to ecosystem document. - by **Azir-11** [<samp>(c472a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c472a943)
|
|
||||||
|
|
||||||
### 🏡 Chore
|
|
||||||
|
|
||||||
- **deps**:
|
|
||||||
- update deps - by @soybeanjs [<samp>(f8dc6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f8dc639e)
|
|
||||||
- update deps - by @soybeanjs [<samp>(7cf40)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7cf4083b)
|
|
||||||
- **other**:
|
|
||||||
- remove Prettier's recommendation. - by **Azir-11** [<samp>(73e9a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/73e9a0fe)
|
|
||||||
- **styles**:
|
|
||||||
- format code - by @soybeanjs [<samp>(098cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/098cd50e)
|
|
||||||
|
|
||||||
### 🎨 Styles
|
|
||||||
|
|
||||||
- **projects**: modify homepage prompt title to tip. - by **Azir-11** [<samp>(91a26)</samp>](https://github.com/soybeanjs/soybean-admin/commit/91a261c1)
|
|
||||||
|
|
||||||
### ❤️ Contributors
|
|
||||||
|
|
||||||
[](https://github.com/soybeanjs) [](https://github.com/paynezhuang)
|
|
||||||
[hooke](mailto:hellohooke@foxmail.com), [Azir-11](mailto:2075125282@qq.com), [刘璐](mailto:hi.alue@qq.com)
|
|
||||||
|
|
||||||
## [v2.0.0](https://github.com/soybeanjs/soybean-admin/compare/v1.3.15...v2.0.0) (2025-11-02)
|
|
||||||
|
|
||||||
### 🚨 Breaking Changes
|
|
||||||
|
|
||||||
- **hooks**: refactor useTable and enhance type definitions - by @soybeanjs [<samp>(8cc51)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8cc5177c)
|
|
||||||
- **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/b6ac3106)
|
|
||||||
- **request**: remove cancelRequest method and related logic from request instances - by @soybeanjs [<samp>(b4e12)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e12530)
|
|
||||||
|
|
||||||
### 🚀 Features
|
|
||||||
|
|
||||||
- **components**:
|
|
||||||
- add the IconTooltip component. - by **Azir-11** [<samp>(a55b4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a55b4dc0)
|
|
||||||
- replace NTooltip with IconTooltip and optimize the layout of related components. - by **Azir-11** [<samp>(40057)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4005763c)
|
|
||||||
- **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)
|
|
||||||
- **hooks**:
|
|
||||||
- add scrollX computation for total table width in useNaiveTable - by @Lruihao [<samp>(358e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/358e1297)
|
|
||||||
- **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/61fa4b7f)
|
|
||||||
- **projects**:
|
|
||||||
- refactor theme drawer with tabbed layout for better UX. - by **Azir** [<samp>(8ba71)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8ba71a08)
|
|
||||||
- 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/f238fcbd)
|
|
||||||
- add 'vertical-hybrid-header-first' layout mode - by @wenyuanw [<samp>(b4e5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e5c6d9)
|
|
||||||
- add prompt information for scrolling mode and tab bar caching. - by **Azir-11** [<samp>(29a2a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/29a2a5c6)
|
|
||||||
- support theme preset function. - by **Azir-11** [<samp>(257f1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/257f1183)
|
|
||||||
- modify the default value of the reset cache policy to 'refresh'. - by **Azir-11** [<samp>(3c0a5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3c0a5282)
|
|
||||||
- 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/ef7acc62)
|
|
||||||
- support closing tabs with middle mouse button click - by @wenyuanw [<samp>(a8d1e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a8d1e5d2)
|
|
||||||
- support set global redius - by **CyberShen123** [<samp>(24c6d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/24c6df52)
|
|
||||||
- support set global redius - by **CyberShen123** [<samp>(3549c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3549c4db)
|
|
||||||
- compatible with the new Echarts API and optimized styles. - by **Azir-11** [<samp>(9755c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9755c313)
|
|
||||||
- **styles**:
|
|
||||||
- add text-autospace property to improve text layout - by @wenyuanw [<samp>(345aa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/345aa293)
|
|
||||||
|
|
||||||
### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- **hooks**:
|
|
||||||
- correct chart rendering logic in useEcharts - by @soybeanjs [<samp>(8a7cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8a7cd593)
|
|
||||||
- **layout**:
|
|
||||||
- fix getSiderWidth - by @soybeanjs [<samp>(e471e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e471e914)
|
|
||||||
- **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/9ea56c9b)
|
|
||||||
- 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)
|
|
||||||
- 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/5be864a8)
|
|
||||||
- **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/41191d54)
|
|
||||||
- adjust legend position in line chart options. - by **Azir-11** [<samp>(0b998)</samp>](https://github.com/soybeanjs/soybean-admin/commit/0b9982bd)
|
|
||||||
- **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)
|
|
||||||
- **styles**:
|
|
||||||
- show light color scrollbar while dark mode is on - by **whyang** [<samp>(dac50)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dac5075b)
|
|
||||||
- **table**:
|
|
||||||
- add type annotations for records in useTable hook - by @soybeanjs [<samp>(32b8f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/32b8f990)
|
|
||||||
- **types**:
|
|
||||||
- fix proxy types - by @soybeanjs [<samp>(3d72f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3d72f954)
|
|
||||||
- fix proxy types - by @soybeanjs [<samp>(12b25)</samp>](https://github.com/soybeanjs/soybean-admin/commit/12b25e0d)
|
|
||||||
- fix ts type error - by @soybeanjs [<samp>(d5a3a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d5a3a25d)
|
|
||||||
|
|
||||||
### 🛠 Optimizations
|
|
||||||
|
|
||||||
- **hooks**:
|
|
||||||
- optimize useEcharts - by @soybeanjs [<samp>(936b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/936b834e)
|
|
||||||
- **packages**:
|
|
||||||
- remove ofetch package - by @soybeanjs [<samp>(abaaa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/abaaa4a0)
|
|
||||||
- **projects**:
|
|
||||||
- improve theme drawer responsive width for mobile devices - by @wenyuanw [<samp>(8439a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8439a600)
|
|
||||||
- improve robustness of second-level menu key logic - by @wenyuanw [<samp>(8b8a2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8b8a2083)
|
|
||||||
- optimize theme drawer width - by @soybeanjs [<samp>(81468)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8146858b)
|
|
||||||
- optimize api type file - by @soybeanjs [<samp>(3a343)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3a343eea)
|
|
||||||
- optimize radius settings - by @soybeanjs [<samp>(87a66)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87a66a42)
|
|
||||||
- **request**:
|
|
||||||
- enhance request options and response handling with generic types - by @soybeanjs [<samp>(50a5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/50a5cba0)
|
|
||||||
- **typings**:
|
|
||||||
- update component typings - by @soybeanjs [<samp>(1d142)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1d142695)
|
|
||||||
|
|
||||||
### 💅 Refactors
|
|
||||||
|
|
||||||
- **hooks**:
|
|
||||||
- optimize useContext and update useMixMenuContext - by @soybeanjs [<samp>(c9651)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c965140b)
|
|
||||||
- streamline column visibility handling in useTable and table components - by @soybeanjs [<samp>(ee434)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ee434145)
|
|
||||||
- remove useSignal hook and update exports - by @soybeanjs [<samp>(87adc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87adc35f)
|
|
||||||
- **menu**:
|
|
||||||
- optimize the margin on the menu - by **NicholasLD** [<samp>(d7311)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d7311111)
|
|
||||||
- **projects**:
|
|
||||||
- remove unnecessary logic in onRouteSwitchWhenLoggedIn - by @wenyuanw [<samp>(d6c81)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d6c8142b)
|
|
||||||
- **request**:
|
|
||||||
- unify response transformation methods and deprecate transformBackendResponse - by @soybeanjs [<samp>(f83ee)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f83eefbc)
|
|
||||||
- **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/d37ce046)
|
|
||||||
|
|
||||||
### 📖 Documentation
|
|
||||||
|
|
||||||
- **projects**:
|
|
||||||
- add github trendshift info. - by **恕瑞玛的皇帝** [<samp>(e18d3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e18d3972)
|
|
||||||
- add github trendshift info. - by **恕瑞玛的皇帝** [<samp>(2a0c9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2a0c9f1b)
|
|
||||||
- add contribution leaderboard - by @wenyuanw [<samp>(01744)</samp>](https://github.com/soybeanjs/soybean-admin/commit/017440c1)
|
|
||||||
|
|
||||||
### 🏡 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/a5c4b4e3)
|
|
||||||
- update deps - by @soybeanjs [<samp>(5cb1c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5cb1cebd)
|
|
||||||
- update deps - by @soybeanjs [<samp>(aeb63)</samp>](https://github.com/soybeanjs/soybean-admin/commit/aeb63690)
|
|
||||||
- update deps - by @soybeanjs [<samp>(e89b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e89b86ce)
|
|
||||||
- update deps - by @soybeanjs [<samp>(c962f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c962f7b2)
|
|
||||||
- update deps - by @soybeanjs [<samp>(12135)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1213531b)
|
|
||||||
- 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)
|
|
||||||
- update deps - by @soybeanjs [<samp>(b041f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b041fdd8)
|
|
||||||
- update deps - by @soybeanjs [<samp>(d567c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d567c057)
|
|
||||||
- update deps - by @soybeanjs [<samp>(6cbf5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6cbf5705)
|
|
||||||
- update deps - by @soybeanjs [<samp>(6010f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6010f518)
|
|
||||||
- update deps - by @soybeanjs [<samp>(232f5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/232f56fd)
|
|
||||||
- **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)
|
|
||||||
- update the ESLint validation configuration to support more file types. - by **Azir-11** [<samp>(be8f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/be8f915a)
|
|
||||||
- **packages**:
|
|
||||||
- update Vite version to 7 in package.json and documentation. - by **Azir** [<samp>(03dd6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/03dd64c5)
|
|
||||||
- add picomatch to fix scripts - by @soybeanjs [<samp>(805c3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/805c3381)
|
|
||||||
- **readme**:
|
|
||||||
- remove DartNode sponsorship badge from README files - by @soybeanjs [<samp>(33ade)</samp>](https://github.com/soybeanjs/soybean-admin/commit/33ade539)
|
|
||||||
- **vscode**:
|
|
||||||
- remove unused vue.server.hybridMode setting from .vscode/settings.json - by @soybeanjs [<samp>(13319)</samp>](https://github.com/soybeanjs/soybean-admin/commit/133196f3)
|
|
||||||
|
|
||||||
### 🎨 Styles
|
|
||||||
|
|
||||||
- **projects**: format code. - by **Azir-11** [<samp>(100e0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/100e0ea5)
|
|
||||||
|
|
||||||
### ❤️ Contributors
|
|
||||||
|
|
||||||
[](https://github.com/soybeanjs) [](https://github.com/wenyuanw) [](https://github.com/CyberShen) [](https://github.com/Lruihao) [](https://github.com/xiaobao0505)
|
|
||||||
[Azir-11](mailto:2075125282@qq.com), [CyberShen123](mailto:s.lijun@qq.com), [whyang](mailto:whyang9701@gmail.com), [HongxuanG](mailto:1359774872@qq.com), [NicholasLD](mailto:878639947@qq.com), [Junior25306](mailto:dayu429@qq.com)
|
|
||||||
|
|
||||||
## [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,10 +7,11 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
[](./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>
|
||||||
@@ -139,7 +140,7 @@ Refer to the [Code Synchronization](https://docs.soybeanjs.cn/guide/sync) docume
|
|||||||
|
|
||||||
## Ecosystem
|
## Ecosystem
|
||||||
|
|
||||||
- [skyroc-admin](https://github.com/Ohh-889/skyroc-admin): SoybeanAdmin's React version implementation.
|
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): SoybeanAdmin based version of React.
|
||||||
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): A Mock Api management system that helps front-end developers quickly implement interface mocks.
|
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): A Mock Api management system that helps front-end developers quickly implement interface mocks.
|
||||||
- [T-Shell](https://github.com/TheBlindM/T-Shell): A terminal emulator and SSH client with configurable command prompts.
|
- [T-Shell](https://github.com/TheBlindM/T-Shell): A terminal emulator and SSH client with configurable command prompts.
|
||||||
- [pea](https://github.com/haitang1894/pea) : Adopting SpringBoot3.2 + JDK21, MyBatis-Plus, SpringSecurity security framework, etc., suitable for the simple permission system developed by [soybean-admin](https://gitee.com/honghuangdc/soybean-admin).
|
- [pea](https://github.com/haitang1894/pea) : Adopting SpringBoot3.2 + JDK21, MyBatis-Plus, SpringSecurity security framework, etc., suitable for the simple permission system developed by [soybean-admin](https://gitee.com/honghuangdc/soybean-admin).
|
||||||
@@ -151,8 +152,6 @@ Refer to the [Code Synchronization](https://docs.soybeanjs.cn/guide/sync) docume
|
|||||||
- [ba](https://github.com/xiatianYa/Ba-Server): Backend service docking with soybean admin based on goFrame framework, adapted to dynamic routing, and interface authentication permissions.
|
- [ba](https://github.com/xiatianYa/Ba-Server): Backend service docking with soybean admin based on goFrame framework, adapted to dynamic routing, and interface authentication permissions.
|
||||||
- [soybean-admin-go](https://github.com/WgoW/soybean-admin-go):A Go backend service developed based on the Gin and GORM frameworks, integrated with the example branch of Soybean Admin. It supports dynamic routing and API permission authentication.
|
- [soybean-admin-go](https://github.com/WgoW/soybean-admin-go):A Go backend service developed based on the Gin and GORM frameworks, integrated with the example branch of Soybean Admin. It supports dynamic routing and API permission authentication.
|
||||||
|
|
||||||
More ecosystem please refer to [Ecosystem](https://docs.soybeanjs.cn/awesome) document.
|
|
||||||
|
|
||||||
|
|
||||||
## How to Contribute
|
## How to Contribute
|
||||||
|
|
||||||
@@ -182,21 +181,13 @@ 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.
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p>QQ Group</p>
|
<p>QQ Group</p>
|
||||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-5.jpg" style="width:200px" />
|
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-4.jpg" style="width:200px" />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div>
|
<!-- <div>
|
||||||
<p>WeChat Group</p>
|
<p>WeChat Group</p>
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -7,10 +7,11 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
[](./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>
|
||||||
@@ -165,7 +166,7 @@ pnpm build
|
|||||||
|
|
||||||
## 周边生态
|
## 周边生态
|
||||||
|
|
||||||
- [skyroc-admin](https://github.com/Ohh-889/skyroc-admin): SoybeanAdmin的React版本实现.
|
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): 基于SoybeanAdmin的React版本.
|
||||||
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
|
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
|
||||||
- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。
|
- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。
|
||||||
- [pea](https://github.com/haitang1894/pea) : 采用SpringBoot3.2 + JDK21、MyBatis-Plus、SpringSecurity安全框架等,适配 [soybean-admin](https://gitee.com/honghuangdc/soybean-admin) 开发的简单权限系统。
|
- [pea](https://github.com/haitang1894/pea) : 采用SpringBoot3.2 + JDK21、MyBatis-Plus、SpringSecurity安全框架等,适配 [soybean-admin](https://gitee.com/honghuangdc/soybean-admin) 开发的简单权限系统。
|
||||||
@@ -177,8 +178,6 @@ pnpm build
|
|||||||
- [ba](https://github.com/xiatianYa/Ba-Server): 基于goFrame框架开发的后端服务对接soybean-admin,适配动态路由,接口鉴权限。
|
- [ba](https://github.com/xiatianYa/Ba-Server): 基于goFrame框架开发的后端服务对接soybean-admin,适配动态路由,接口鉴权限。
|
||||||
- [soybean-admin-go](https://github.com/WgoW/soybean-admin-go):基于gin+gorm框架开发的go语言后端服务对接soybean-admin的example分支,适配动态路由,接口鉴权限。
|
- [soybean-admin-go](https://github.com/WgoW/soybean-admin-go):基于gin+gorm框架开发的go语言后端服务对接soybean-admin的example分支,适配动态路由,接口鉴权限。
|
||||||
|
|
||||||
更多周边生态请翻阅 [周边生态](https://docs.soybeanjs.cn/zh/awesome) 文档。
|
|
||||||
|
|
||||||
|
|
||||||
## 如何贡献
|
## 如何贡献
|
||||||
|
|
||||||
@@ -210,21 +209,13 @@ 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 交流群,使用问题欢迎在群内提问。
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p>QQ交流群</p>
|
<p>QQ交流群</p>
|
||||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-5.jpg" style="width:200px" />
|
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-4.jpg" style="width:200px" />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div>
|
<!-- <div>
|
||||||
<p>微信群</p>
|
<p>微信群</p>
|
||||||
|
|||||||
@@ -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 { setupElegantRouter } from './router';
|
import elegantRouter from 'elegant-router/vite';
|
||||||
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),
|
||||||
setupElegantRouter(),
|
elegantRouter(),
|
||||||
setupUnocss(viteEnv),
|
setupUnocss(viteEnv),
|
||||||
...setupUnplugin(viteEnv),
|
...setupUnplugin(viteEnv),
|
||||||
progress(),
|
progress(),
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
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
Normal file
37
er.config.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
59
package.json
59
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "soybean-admin",
|
"name": "soybean-admin",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.0.1",
|
"version": "1.3.15",
|
||||||
"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,53 +54,54 @@
|
|||||||
"@sa/hooks": "workspace:*",
|
"@sa/hooks": "workspace:*",
|
||||||
"@sa/materials": "workspace:*",
|
"@sa/materials": "workspace:*",
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"@vueuse/core": "14.1.0",
|
"@vueuse/core": "13.9.0",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
"dayjs": "1.11.19",
|
"dayjs": "1.11.18",
|
||||||
"defu": "6.1.4",
|
"defu": "6.1.4",
|
||||||
"echarts": "6.0.0",
|
"echarts": "6.0.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"naive-ui": "2.43.2",
|
"naive-ui": "2.43.1",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"pinia": "3.0.4",
|
"pinia": "3.0.3",
|
||||||
"tailwind-merge": "3.4.0",
|
"tailwind-merge": "3.3.1",
|
||||||
"vue": "3.5.25",
|
"vue": "3.5.22",
|
||||||
"vue-draggable-plus": "0.6.0",
|
"vue-draggable-plus": "0.6.0",
|
||||||
"vue-i18n": "11.2.2",
|
"vue-i18n": "11.1.12",
|
||||||
"vue-router": "4.6.3"
|
"vue-router": "4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@elegant-router/vue": "0.3.8",
|
"@elegant-router/vue": "0.3.8",
|
||||||
"@iconify/json": "2.2.414",
|
"@iconify/json": "2.2.395",
|
||||||
"@sa/scripts": "workspace:*",
|
"@sa/scripts": "workspace:*",
|
||||||
"@sa/uno-preset": "workspace:*",
|
"@sa/uno-preset": "workspace:*",
|
||||||
"@soybeanjs/eslint-config": "1.7.4",
|
"@soybeanjs/eslint-config": "1.7.1",
|
||||||
"@types/node": "24.10.1",
|
"@types/node": "24.7.2",
|
||||||
"@types/nprogress": "0.2.3",
|
"@types/nprogress": "0.2.3",
|
||||||
"@unocss/eslint-config": "66.5.10",
|
"@unocss/eslint-config": "66.5.3",
|
||||||
"@unocss/preset-icons": "66.5.10",
|
"@unocss/preset-icons": "66.5.3",
|
||||||
"@unocss/preset-uno": "66.5.10",
|
"@unocss/preset-uno": "66.5.3",
|
||||||
"@unocss/transformer-directives": "66.5.10",
|
"@unocss/transformer-directives": "66.5.3",
|
||||||
"@unocss/transformer-variant-group": "66.5.10",
|
"@unocss/transformer-variant-group": "66.5.3",
|
||||||
"@unocss/vite": "66.5.10",
|
"@unocss/vite": "66.5.3",
|
||||||
"@vitejs/plugin-vue": "6.0.2",
|
"@vitejs/plugin-vue": "6.0.1",
|
||||||
"@vitejs/plugin-vue-jsx": "5.1.2",
|
"@vitejs/plugin-vue-jsx": "5.1.1",
|
||||||
"consola": "3.4.2",
|
"consola": "3.4.2",
|
||||||
"eslint": "9.39.1",
|
"elegant-router": "1.0.4",
|
||||||
"eslint-plugin-vue": "10.6.2",
|
"eslint": "9.37.0",
|
||||||
|
"eslint-plugin-vue": "10.5.0",
|
||||||
"kolorist": "1.8.0",
|
"kolorist": "1.8.0",
|
||||||
"sass": "1.94.2",
|
"sass": "1.93.2",
|
||||||
"simple-git-hooks": "2.13.1",
|
"simple-git-hooks": "2.13.1",
|
||||||
"tsx": "4.21.0",
|
"tsx": "4.20.6",
|
||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
"unplugin-icons": "22.5.0",
|
"unplugin-icons": "22.4.2",
|
||||||
"unplugin-vue-components": "30.0.0",
|
"unplugin-vue-components": "29.1.0",
|
||||||
"vite": "7.2.6",
|
"vite": "7.1.9",
|
||||||
"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.5",
|
"vite-plugin-vue-devtools": "8.0.2",
|
||||||
"vue-eslint-parser": "10.2.0",
|
"vue-eslint-parser": "10.2.0",
|
||||||
"vue-tsc": "3.1.5"
|
"vue-tsc": "3.1.1"
|
||||||
},
|
},
|
||||||
"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": "2.0.1",
|
"version": "1.3.15",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
"./fetch": "./src/fetch.ts",
|
"./fetch": "./src/fetch.ts",
|
||||||
@@ -15,6 +15,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alova/mock": "2.0.17",
|
"@alova/mock": "2.0.17",
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"alova": "3.4.0"
|
"alova": "3.3.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/axios",
|
"name": "@sa/axios",
|
||||||
"version": "2.0.1",
|
"version": "1.3.15",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"axios": "1.13.2",
|
"axios": "1.12.2",
|
||||||
"axios-retry": "4.5.0",
|
"axios-retry": "4.5.0",
|
||||||
"qs": "6.14.0"
|
"qs": "6.14.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -119,11 +119,8 @@ export type FlatResponseData<ResponseData, ApiData> =
|
|||||||
| FlatResponseSuccessData<ResponseData, ApiData>
|
| FlatResponseSuccessData<ResponseData, ApiData>
|
||||||
| FlatResponseFailData<ResponseData>;
|
| FlatResponseFailData<ResponseData>;
|
||||||
|
|
||||||
export interface FlatRequestInstance<
|
export interface FlatRequestInstance<ResponseData, ApiData, State extends Record<string, unknown>>
|
||||||
ResponseData,
|
extends RequestInstanceCommon<State> {
|
||||||
ApiData,
|
|
||||||
State extends Record<string, unknown>
|
|
||||||
> extends RequestInstanceCommon<State> {
|
|
||||||
<T extends ApiData = ApiData, R extends ResponseType = 'json'>(
|
<T extends ApiData = ApiData, R extends ResponseType = 'json'>(
|
||||||
config: CustomAxiosRequestConfig<R>
|
config: CustomAxiosRequestConfig<R>
|
||||||
): Promise<FlatResponseData<ResponseData, MappedType<R, T>>>;
|
): Promise<FlatResponseData<ResponseData, MappedType<R, T>>>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/color",
|
"name": "@sa/color",
|
||||||
"version": "2.0.1",
|
"version": "1.3.15",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/hooks",
|
"name": "@sa/hooks",
|
||||||
"version": "2.0.1",
|
"version": "1.3.15",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,11 +26,8 @@ export type HookRequestInstanceResponseData<ResponseData, ApiData> = {
|
|||||||
loading: Ref<boolean>;
|
loading: Ref<boolean>;
|
||||||
} & (HookRequestInstanceResponseSuccessData<ApiData> | HookRequestInstanceResponseFailData<ResponseData>);
|
} & (HookRequestInstanceResponseSuccessData<ApiData> | HookRequestInstanceResponseFailData<ResponseData>);
|
||||||
|
|
||||||
export interface HookRequestInstance<
|
export interface HookRequestInstance<ResponseData, ApiData, State extends Record<string, unknown>>
|
||||||
ResponseData,
|
extends RequestInstanceCommon<State> {
|
||||||
ApiData,
|
|
||||||
State extends Record<string, unknown>
|
|
||||||
> extends RequestInstanceCommon<State> {
|
|
||||||
<T extends ApiData = ApiData, R extends ResponseType = 'json'>(
|
<T extends ApiData = ApiData, R extends ResponseType = 'json'>(
|
||||||
config: CustomAxiosRequestConfig
|
config: CustomAxiosRequestConfig
|
||||||
): HookRequestInstanceResponseData<ResponseData, MappedType<R, T>>;
|
): HookRequestInstanceResponseData<ResponseData, MappedType<R, T>>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/materials",
|
"name": "@sa/materials",
|
||||||
"version": "2.0.1",
|
"version": "1.3.15",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -146,8 +146,7 @@ export type LayoutScrollMode = 'wrapper' | 'content';
|
|||||||
|
|
||||||
/** Admin layout props */
|
/** Admin layout props */
|
||||||
export interface AdminLayoutProps
|
export interface AdminLayoutProps
|
||||||
extends
|
extends AdminLayoutHeaderConfig,
|
||||||
AdminLayoutHeaderConfig,
|
|
||||||
AdminLayoutTabConfig,
|
AdminLayoutTabConfig,
|
||||||
AdminLayoutSiderConfig,
|
AdminLayoutSiderConfig,
|
||||||
AdminLayoutContentConfig,
|
AdminLayoutContentConfig,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/scripts",
|
"name": "@sa/scripts",
|
||||||
"version": "2.0.1",
|
"version": "1.3.15",
|
||||||
"bin": {
|
"bin": {
|
||||||
"sa": "./bin.ts"
|
"sa": "./bin.ts"
|
||||||
},
|
},
|
||||||
@@ -14,15 +14,15 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@soybeanjs/changelog": "0.3.25",
|
"@soybeanjs/changelog": "0.3.25",
|
||||||
"bumpp": "10.3.2",
|
"bumpp": "10.3.1",
|
||||||
"c12": "3.3.2",
|
"c12": "3.3.0",
|
||||||
"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.1",
|
"execa": "9.6.0",
|
||||||
"kolorist": "1.8.0",
|
"kolorist": "1.8.0",
|
||||||
"npm-check-updates": "19.1.2",
|
"npm-check-updates": "19.0.0",
|
||||||
"picomatch": "4.0.3",
|
"picomatch": "4.0.3",
|
||||||
"rimraf": "6.1.2"
|
"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', ['npm-check-updates', ...args], { stdio: 'inherit' });
|
execCommand('npx', ['ncu', ...args], { stdio: 'inherit' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/uno-preset",
|
"name": "@sa/uno-preset",
|
||||||
"version": "2.0.1",
|
"version": "1.3.15",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/utils",
|
"name": "@sa/utils",
|
||||||
"version": "2.0.1",
|
"version": "1.3.15",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
|
|||||||
2853
pnpm-lock.yaml
generated
2853
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 { secondLevelMenus, childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext();
|
const { 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(() => getSiderAndCollapsedWidth(false));
|
const siderWidth = computed(() => getSiderWidth());
|
||||||
|
|
||||||
const siderCollapsedWidth = computed(() => getSiderAndCollapsedWidth(true));
|
const siderCollapsedWidth = computed(() => getSiderCollapsedWidth());
|
||||||
|
|
||||||
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 && secondLevelMenus.value.length) {
|
if (isVerticalMix.value && appStore.mixSiderFixed && childLevelMenus.value.length) {
|
||||||
finalWidth += mixChildMenuWidth;
|
finalWidth += mixChildMenuWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +114,14 @@ function getSiderAndCollapsedWidth(isCollapsed: boolean) {
|
|||||||
|
|
||||||
return finalWidth;
|
return finalWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSiderWidth() {
|
||||||
|
return getSiderAndCollapsedWidth(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSiderCollapsedWidth() {
|
||||||
|
return getSiderAndCollapsedWidth(true);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { useRoute } from 'vue-router';
|
|||||||
import { useContext } from '@sa/hooks';
|
import { useContext } from '@sa/hooks';
|
||||||
import type { RouteKey } from '@elegant-router/types';
|
import type { RouteKey } from '@elegant-router/types';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
|
|
||||||
export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu', useMixMenu);
|
export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu', useMixMenu);
|
||||||
@@ -11,7 +10,6 @@ export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu',
|
|||||||
function useMixMenu() {
|
function useMixMenu() {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const routeStore = useRouteStore();
|
const routeStore = useRouteStore();
|
||||||
const themeStore = useThemeStore();
|
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||||
|
|
||||||
@@ -102,46 +100,10 @@ function useMixMenu() {
|
|||||||
() => secondLevelMenus.value.find(menu => menu.key === activeSecondLevelMenuKey.value)?.children || []
|
() => secondLevelMenus.value.find(menu => menu.key === activeSecondLevelMenuKey.value)?.children || []
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasChildLevelMenus = computed(() => childLevelMenus.value.length > 0);
|
|
||||||
|
|
||||||
function getDeepestLevelMenuKey(): RouteKey | null {
|
|
||||||
if (!secondLevelMenus.value.length || !themeStore.sider.autoSelectFirstMenu) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const secondLevelFirstMenu = secondLevelMenus.value[0];
|
|
||||||
|
|
||||||
if (!secondLevelFirstMenu) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findDeepest(menu: App.Global.Menu): RouteKey {
|
|
||||||
if (!menu.children?.length) {
|
|
||||||
return menu.routeKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
return findDeepest(menu.children[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return findDeepest(secondLevelFirstMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
function activeDeepestLevelMenuKey() {
|
|
||||||
const deepestLevelMenuKey = getDeepestLevelMenuKey();
|
|
||||||
if (!deepestLevelMenuKey) return;
|
|
||||||
|
|
||||||
// select the deepest second level menu
|
|
||||||
handleSelectSecondLevelMenu(deepestLevelMenuKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.name,
|
() => route.name,
|
||||||
() => {
|
() => {
|
||||||
getActiveFirstLevelMenuKey();
|
getActiveFirstLevelMenuKey();
|
||||||
// if there are child level menus, get the active second level menu key
|
|
||||||
if (hasChildLevelMenus.value) {
|
|
||||||
getActiveSecondLevelMenuKey();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
@@ -159,10 +121,7 @@ function useMixMenu() {
|
|||||||
isActiveSecondLevelMenuHasChildren,
|
isActiveSecondLevelMenuHasChildren,
|
||||||
handleSelectSecondLevelMenu,
|
handleSelectSecondLevelMenu,
|
||||||
getActiveSecondLevelMenuKey,
|
getActiveSecondLevelMenuKey,
|
||||||
childLevelMenus,
|
childLevelMenus
|
||||||
hasChildLevelMenus,
|
|
||||||
getDeepestLevelMenuKey,
|
|
||||||
activeDeepestLevelMenuKey
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { SimpleScrollbar } from '@sa/materials';
|
import { SimpleScrollbar } from '@sa/materials';
|
||||||
import type { RouteKey } from '@elegant-router/types';
|
|
||||||
import { GLOBAL_HEADER_MENU_ID, GLOBAL_SIDER_MENU_ID } from '@/constants/app';
|
import { GLOBAL_HEADER_MENU_ID, GLOBAL_SIDER_MENU_ID } from '@/constants/app';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
@@ -19,28 +18,12 @@ const appStore = useAppStore();
|
|||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const routeStore = useRouteStore();
|
const routeStore = useRouteStore();
|
||||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||||
const {
|
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||||
firstLevelMenus,
|
useMixMenuContext('TopHybridHeaderFirst');
|
||||||
secondLevelMenus,
|
|
||||||
activeFirstLevelMenuKey,
|
|
||||||
handleSelectFirstLevelMenu,
|
|
||||||
activeDeepestLevelMenuKey
|
|
||||||
} = useMixMenuContext('TopHybridHeaderFirst');
|
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
|
|
||||||
const expandedKeys = ref<string[]>([]);
|
const expandedKeys = ref<string[]>([]);
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle first level menu select
|
|
||||||
* @param key RouteKey
|
|
||||||
*/
|
|
||||||
function handleSelectMenu(key: RouteKey) {
|
|
||||||
handleSelectFirstLevelMenu(key);
|
|
||||||
|
|
||||||
// if there are second level menus, select the deepest one by default
|
|
||||||
activeDeepestLevelMenuKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateExpandedKeys() {
|
function updateExpandedKeys() {
|
||||||
if (appStore.siderCollapse || !selectedKey.value) {
|
if (appStore.siderCollapse || !selectedKey.value) {
|
||||||
expandedKeys.value = [];
|
expandedKeys.value = [];
|
||||||
@@ -66,7 +49,7 @@ watch(
|
|||||||
:options="firstLevelMenus"
|
:options="firstLevelMenus"
|
||||||
:indent="18"
|
:indent="18"
|
||||||
responsive
|
responsive
|
||||||
@update:value="handleSelectMenu"
|
@update:value="handleSelectFirstLevelMenu"
|
||||||
/>
|
/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<Teleport :to="`#${GLOBAL_SIDER_MENU_ID}`">
|
<Teleport :to="`#${GLOBAL_SIDER_MENU_ID}`">
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { RouteKey } from '@elegant-router/types';
|
|
||||||
import { GLOBAL_HEADER_MENU_ID, GLOBAL_SIDER_MENU_ID } from '@/constants/app';
|
import { GLOBAL_HEADER_MENU_ID, GLOBAL_SIDER_MENU_ID } from '@/constants/app';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
@@ -14,25 +13,9 @@ defineOptions({
|
|||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||||
const {
|
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||||
firstLevelMenus,
|
useMixMenuContext('TopHybridSidebarFirst');
|
||||||
secondLevelMenus,
|
|
||||||
activeFirstLevelMenuKey,
|
|
||||||
handleSelectFirstLevelMenu,
|
|
||||||
activeDeepestLevelMenuKey
|
|
||||||
} = useMixMenuContext('TopHybridSidebarFirst');
|
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle first level menu select
|
|
||||||
* @param key RouteKey
|
|
||||||
*/
|
|
||||||
function handleSelectMenu(key: RouteKey) {
|
|
||||||
handleSelectFirstLevelMenu(key);
|
|
||||||
|
|
||||||
// if there are second level menus, select the deepest one by default
|
|
||||||
activeDeepestLevelMenuKey();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -54,7 +37,7 @@ function handleSelectMenu(key: RouteKey) {
|
|||||||
:sider-collapse="appStore.siderCollapse"
|
:sider-collapse="appStore.siderCollapse"
|
||||||
:dark-mode="themeStore.darkMode"
|
:dark-mode="themeStore.darkMode"
|
||||||
:theme-color="themeStore.themeColor"
|
:theme-color="themeStore.themeColor"
|
||||||
@select="handleSelectMenu"
|
@select="handleSelectFirstLevelMenu"
|
||||||
@toggle-sider-collapse="appStore.toggleSiderCollapse"
|
@toggle-sider-collapse="appStore.toggleSiderCollapse"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,15 +33,15 @@ const {
|
|||||||
isActiveSecondLevelMenuHasChildren,
|
isActiveSecondLevelMenuHasChildren,
|
||||||
handleSelectSecondLevelMenu,
|
handleSelectSecondLevelMenu,
|
||||||
getActiveSecondLevelMenuKey,
|
getActiveSecondLevelMenuKey,
|
||||||
childLevelMenus,
|
childLevelMenus
|
||||||
hasChildLevelMenus,
|
|
||||||
activeDeepestLevelMenuKey
|
|
||||||
} = useMixMenuContext('VerticalHybridHeaderFirst');
|
} = useMixMenuContext('VerticalHybridHeaderFirst');
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
|
|
||||||
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
||||||
|
|
||||||
const showDrawer = computed(() => hasChildLevelMenus.value && (drawerVisible.value || appStore.mixSiderFixed));
|
const hasChildMenus = computed(() => childLevelMenus.value.length > 0);
|
||||||
|
|
||||||
|
const showDrawer = computed(() => hasChildMenus.value && (drawerVisible.value || appStore.mixSiderFixed));
|
||||||
|
|
||||||
function handleSelectMixMenu(key: RouteKey) {
|
function handleSelectMixMenu(key: RouteKey) {
|
||||||
handleSelectSecondLevelMenu(key);
|
handleSelectSecondLevelMenu(key);
|
||||||
@@ -51,33 +51,12 @@ function handleSelectMixMenu(key: RouteKey) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle second level menu selection based on autoSelectFirstMenu setting:
|
|
||||||
* - When disabled: Activate first second-level menu for display only, expand third-level menu if exists
|
|
||||||
* - When enabled: Navigate to the deepest menu automatically
|
|
||||||
*/
|
|
||||||
function handleSelectMenu(key: RouteKey) {
|
function handleSelectMenu(key: RouteKey) {
|
||||||
handleSelectFirstLevelMenu(key);
|
handleSelectFirstLevelMenu(key);
|
||||||
|
|
||||||
if (secondLevelMenus.value.length === 0) return;
|
if (secondLevelMenus.value.length > 0) {
|
||||||
|
handleSelectMixMenu(secondLevelMenus.value[0].routeKey);
|
||||||
const secondFirstMenuKey = secondLevelMenus.value[0].routeKey;
|
|
||||||
|
|
||||||
// Case 1: autoSelectFirstMenu disabled - only activate menu for display
|
|
||||||
if (!themeStore.sider.autoSelectFirstMenu) {
|
|
||||||
// Check if there are third-level menus
|
|
||||||
const hasChildren = secondLevelMenus.value.find(menu => menu.key === secondFirstMenuKey)?.children?.length;
|
|
||||||
|
|
||||||
// If there are third-level menus, expand them
|
|
||||||
if (hasChildren) {
|
|
||||||
handleSelectMixMenu(secondFirstMenuKey);
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case 2: autoSelectFirstMenu enabled - navigate to deepest menu
|
|
||||||
activeDeepestLevelMenuKey();
|
|
||||||
setDrawerVisible(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleResetActiveMenu() {
|
function handleResetActiveMenu() {
|
||||||
@@ -135,9 +114,7 @@ watch(
|
|||||||
</FirstLevelMenu>
|
</FirstLevelMenu>
|
||||||
<div
|
<div
|
||||||
class="relative h-full transition-width-300"
|
class="relative h-full transition-width-300"
|
||||||
:style="{
|
:style="{ width: appStore.mixSiderFixed && hasChildMenus ? themeStore.sider.mixChildMenuWidth + 'px' : '0px' }"
|
||||||
width: appStore.mixSiderFixed && hasChildLevelMenus ? themeStore.sider.mixChildMenuWidth + 'px' : '0px'
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
<DarkModeContainer
|
<DarkModeContainer
|
||||||
class="absolute-lt h-full flex-col-stretch nowrap-hidden shadow-sm transition-all-300"
|
class="absolute-lt h-full flex-col-stretch nowrap-hidden shadow-sm transition-all-300"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
const visible = defineModel<boolean>('visible');
|
const visible = defineModel<boolean>('visible');
|
||||||
|
|
||||||
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs, fixTab, unfixTab, isTabRetain } = useTabStore();
|
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs } = useTabStore();
|
||||||
const { SvgIconVNode } = useSvgIcon();
|
const { SvgIconVNode } = useSvgIcon();
|
||||||
|
|
||||||
type DropdownOption = {
|
type DropdownOption = {
|
||||||
@@ -64,23 +64,6 @@ const options = computed(() => {
|
|||||||
icon: SvgIconVNode({ icon: 'ant-design:line-outlined', fontSize: 18 })
|
icon: SvgIconVNode({ icon: 'ant-design:line-outlined', fontSize: 18 })
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
if (props.tabId !== '/home') {
|
|
||||||
if (isTabRetain(props.tabId)) {
|
|
||||||
opts.push({
|
|
||||||
key: 'unpin',
|
|
||||||
label: $t('dropdown.unpin'),
|
|
||||||
icon: SvgIconVNode({ icon: 'mdi:pin-off-outline', fontSize: 18 })
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
opts.push({
|
|
||||||
key: 'pin',
|
|
||||||
label: $t('dropdown.pin'),
|
|
||||||
icon: SvgIconVNode({ icon: 'mdi:pin-outline', fontSize: 18 })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { excludeKeys, disabledKeys } = props;
|
const { excludeKeys, disabledKeys } = props;
|
||||||
|
|
||||||
const result = opts.filter(opt => !excludeKeys.includes(opt.key));
|
const result = opts.filter(opt => !excludeKeys.includes(opt.key));
|
||||||
@@ -115,12 +98,6 @@ const dropdownAction: Record<App.Global.DropdownKey, () => void> = {
|
|||||||
},
|
},
|
||||||
closeAll() {
|
closeAll() {
|
||||||
clearTabs();
|
clearTabs();
|
||||||
},
|
|
||||||
pin() {
|
|
||||||
fixTab(props.tabId);
|
|
||||||
},
|
|
||||||
unpin() {
|
|
||||||
unfixTab(props.tabId);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ 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;
|
||||||
@@ -86,26 +84,6 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -204,8 +182,7 @@ 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="switchTab($event, tab)"
|
@pointerdown="tabStore.switchRouteByTab(tab)"
|
||||||
@mousedown="handleMousedown($event, tab)"
|
|
||||||
@close="handleCloseTab(tab)"
|
@close="handleCloseTab(tab)"
|
||||||
@contextmenu="handleContextMenu($event, tab.id)"
|
@contextmenu="handleContextMenu($event, tab.id)"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<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'
|
||||||
@@ -12,7 +11,6 @@ defineOptions({
|
|||||||
<div class="flex-col-stretch gap-16px">
|
<div class="flex-col-stretch gap-16px">
|
||||||
<ThemeSchema />
|
<ThemeSchema />
|
||||||
<ThemeColor />
|
<ThemeColor />
|
||||||
<ThemeRadius />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -12,7 +12,6 @@ const themeStore = useThemeStore();
|
|||||||
|
|
||||||
const layoutMode = computed(() => themeStore.layout.mode);
|
const layoutMode = computed(() => themeStore.layout.mode);
|
||||||
const isMixLayoutMode = computed(() => layoutMode.value.includes('mix') || layoutMode.value.includes('hybrid'));
|
const isMixLayoutMode = computed(() => layoutMode.value.includes('mix') || layoutMode.value.includes('hybrid'));
|
||||||
const isHybridLayoutMode = computed(() => layoutMode.value.includes('hybrid'));
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -33,12 +32,6 @@ const isHybridLayoutMode = computed(() => layoutMode.value.includes('hybrid'));
|
|||||||
<SettingItem v-if="layoutMode === 'vertical-mix'" key="5" :label="$t('theme.layout.sider.mixChildMenuWidth')">
|
<SettingItem v-if="layoutMode === 'vertical-mix'" key="5" :label="$t('theme.layout.sider.mixChildMenuWidth')">
|
||||||
<NInputNumber v-model:value="themeStore.sider.mixChildMenuWidth" size="small" :step="1" class="w-120px" />
|
<NInputNumber v-model:value="themeStore.sider.mixChildMenuWidth" size="small" :step="1" class="w-120px" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<SettingItem v-if="isHybridLayoutMode" key="6" :label="$t('theme.layout.sider.autoSelectFirstMenu')">
|
|
||||||
<template #suffix>
|
|
||||||
<IconTooltip :desc="$t('theme.layout.sider.autoSelectFirstMenuTip')" />
|
|
||||||
</template>
|
|
||||||
<NSwitch v-model:value="themeStore.sider.autoSelectFirstMenu" />
|
|
||||||
</SettingItem>
|
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -35,12 +35,6 @@ 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>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { defu } from 'defu';
|
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { themeSettings } from '@/theme/settings';
|
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -16,7 +14,6 @@ type ThemePreset = Pick<
|
|||||||
| 'colourWeakness'
|
| 'colourWeakness'
|
||||||
| 'recommendColor'
|
| 'recommendColor'
|
||||||
| 'themeColor'
|
| 'themeColor'
|
||||||
| 'themeRadius'
|
|
||||||
| 'otherColor'
|
| 'otherColor'
|
||||||
| 'isInfoFollowPrimary'
|
| 'isInfoFollowPrimary'
|
||||||
| 'layout'
|
| 'layout'
|
||||||
@@ -33,8 +30,6 @@ type ThemePreset = Pick<
|
|||||||
desc: string;
|
desc: string;
|
||||||
i18nkey?: string;
|
i18nkey?: string;
|
||||||
version: string;
|
version: string;
|
||||||
/** Optional NaiveUI theme overrides */
|
|
||||||
naiveui?: App.Theme.NaiveUIThemeOverride;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const presetModules = import.meta.glob('@/theme/preset/*.json', { eager: true, import: 'default' });
|
const presetModules = import.meta.glob('@/theme/preset/*.json', { eager: true, import: 'default' });
|
||||||
@@ -80,9 +75,7 @@ const getPresetDesc = (preset: ThemePreset): string => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyPreset = (preset: ThemePreset): void => {
|
const applyPreset = ({ themeScheme, grayscale, colourWeakness, layout, watermark, ...rest }: ThemePreset): void => {
|
||||||
const mergedPreset = defu(preset, themeSettings);
|
|
||||||
const { themeScheme, grayscale, colourWeakness, layout, watermark, naiveui, ...rest } = mergedPreset;
|
|
||||||
themeStore.setThemeScheme(themeScheme);
|
themeStore.setThemeScheme(themeScheme);
|
||||||
themeStore.setGrayscale(grayscale);
|
themeStore.setGrayscale(grayscale);
|
||||||
themeStore.setColourWeakness(colourWeakness);
|
themeStore.setColourWeakness(colourWeakness);
|
||||||
@@ -102,9 +95,6 @@ const applyPreset = (preset: ThemePreset): void => {
|
|||||||
tokens: { ...rest.tokens }
|
tokens: { ...rest.tokens }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply NaiveUI theme overrides if present
|
|
||||||
themeStore.setNaiveThemeOverrides(naiveui);
|
|
||||||
|
|
||||||
window.$message?.success($t('theme.appearance.preset.applySuccess'));
|
window.$message?.success($t('theme.appearance.preset.applySuccess'));
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -83,9 +83,6 @@ 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: {
|
||||||
@@ -141,9 +138,7 @@ 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',
|
||||||
@@ -160,10 +155,7 @@ const local: App.I18n.Schema = {
|
|||||||
collapsedWidth: 'Sider Collapsed Width',
|
collapsedWidth: 'Sider Collapsed Width',
|
||||||
mixWidth: 'Mix Sider Width',
|
mixWidth: 'Mix Sider Width',
|
||||||
mixCollapsedWidth: 'Mix Sider Collapse Width',
|
mixCollapsedWidth: 'Mix Sider Collapse Width',
|
||||||
mixChildMenuWidth: 'Mix Child Menu Width',
|
mixChildMenuWidth: 'Mix Child Menu Width'
|
||||||
autoSelectFirstMenu: 'Auto Select First Submenu',
|
|
||||||
autoSelectFirstMenuTip:
|
|
||||||
'When a first-level menu is clicked, the first submenu is automatically selected and navigated to the deepest level'
|
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
title: 'Footer Settings',
|
title: 'Footer Settings',
|
||||||
@@ -339,9 +331,7 @@ const local: App.I18n.Schema = {
|
|||||||
closeOther: 'Close Other',
|
closeOther: 'Close Other',
|
||||||
closeLeft: 'Close Left',
|
closeLeft: 'Close Left',
|
||||||
closeRight: 'Close Right',
|
closeRight: 'Close Right',
|
||||||
closeAll: 'Close All',
|
closeAll: 'Close All'
|
||||||
pin: 'Pin Tab',
|
|
||||||
unpin: 'Unpin Tab'
|
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
themeConfig: 'Theme Configuration',
|
themeConfig: 'Theme Configuration',
|
||||||
|
|||||||
@@ -83,9 +83,6 @@ const local: App.I18n.Schema = {
|
|||||||
error: '错误色',
|
error: '错误色',
|
||||||
followPrimary: '跟随主色'
|
followPrimary: '跟随主色'
|
||||||
},
|
},
|
||||||
themeRadius: {
|
|
||||||
title: '主题圆角'
|
|
||||||
},
|
|
||||||
recommendColor: '应用推荐算法的颜色',
|
recommendColor: '应用推荐算法的颜色',
|
||||||
recommendColorDesc: '推荐颜色的算法参照',
|
recommendColorDesc: '推荐颜色的算法参照',
|
||||||
preset: {
|
preset: {
|
||||||
@@ -138,9 +135,7 @@ const local: App.I18n.Schema = {
|
|||||||
slider: '滑块风格',
|
slider: '滑块风格',
|
||||||
chrome: '谷歌风格',
|
chrome: '谷歌风格',
|
||||||
button: '按钮风格'
|
button: '按钮风格'
|
||||||
},
|
}
|
||||||
closeByMiddleClick: '鼠标中键关闭标签页',
|
|
||||||
closeByMiddleClickTip: '启用后可以使用鼠标中键点击标签页进行关闭'
|
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
title: '头部设置',
|
title: '头部设置',
|
||||||
@@ -157,9 +152,7 @@ const local: App.I18n.Schema = {
|
|||||||
collapsedWidth: '侧边栏折叠宽度',
|
collapsedWidth: '侧边栏折叠宽度',
|
||||||
mixWidth: '混合布局侧边栏宽度',
|
mixWidth: '混合布局侧边栏宽度',
|
||||||
mixCollapsedWidth: '混合布局侧边栏折叠宽度',
|
mixCollapsedWidth: '混合布局侧边栏折叠宽度',
|
||||||
mixChildMenuWidth: '混合布局子菜单宽度',
|
mixChildMenuWidth: '混合布局子菜单宽度'
|
||||||
autoSelectFirstMenu: '自动选择第一个子菜单',
|
|
||||||
autoSelectFirstMenuTip: '点击一级菜单时,自动选择并导航到第一个子菜单的最深层级'
|
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
title: '底部设置',
|
title: '底部设置',
|
||||||
@@ -335,9 +328,7 @@ const local: App.I18n.Schema = {
|
|||||||
closeOther: '关闭其它',
|
closeOther: '关闭其它',
|
||||||
closeLeft: '关闭左侧',
|
closeLeft: '关闭左侧',
|
||||||
closeRight: '关闭右侧',
|
closeRight: '关闭右侧',
|
||||||
closeAll: '关闭所有',
|
closeAll: '关闭所有'
|
||||||
pin: '固定标签',
|
|
||||||
unpin: '取消固定'
|
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
themeConfig: '主题配置',
|
themeConfig: '主题配置',
|
||||||
|
|||||||
27
src/router/_generated/imports.ts
Normal file
27
src/router/_generated/imports.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/* 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"),
|
||||||
|
};
|
||||||
130
src/router/_generated/routes.ts
Normal file
130
src/router/_generated/routes.ts
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/* 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
28
src/router/_generated/shared.ts
Normal file
28
src/router/_generated/shared.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* 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];
|
||||||
|
}
|
||||||
70
src/router/_generated/transformer.ts
Normal file
70
src/router/_generated/transformer.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/* 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"),
|
|
||||||
};
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/* 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
/* 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,12 +1,6 @@
|
|||||||
import type { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
import {
|
import { createMemoryHistory, createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
|
||||||
type RouterHistory,
|
import type { RouterHistory } from 'vue-router';
|
||||||
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;
|
||||||
@@ -19,7 +13,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: createBuiltinVueRoutes()
|
routes: []
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Setup Vue Router */
|
/** Setup Vue Router */
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
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,40 +1,39 @@
|
|||||||
import type { CustomRoute, ElegantConstRoute, ElegantRoute } from '@elegant-router/types';
|
import type { AutoRouterRedirect, AutoRouterRoute } from '@elegant-router/types';
|
||||||
import { generatedRoutes } from '../elegant/routes';
|
import { routes } from '../_generated/routes';
|
||||||
import { layouts, views } from '../elegant/imports';
|
import { layouts, views } from '../_generated/imports';
|
||||||
import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
|
import { transformToVueRoutes } from '../_generated/transformer';
|
||||||
|
|
||||||
/**
|
|
||||||
* 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: ElegantRoute[] = [];
|
const constantRoutes: AutoRouterRoute[] = [];
|
||||||
|
const authRoutes: AutoRouterRoute[] = [];
|
||||||
|
|
||||||
const authRoutes: ElegantRoute[] = [];
|
let rootRoute: AutoRouterRedirect | undefined;
|
||||||
|
|
||||||
[...customRoutes, ...generatedRoutes].forEach(item => {
|
routes.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 routes Elegant routes
|
* @param authRoutes Elegant routes
|
||||||
*/
|
*/
|
||||||
export function getAuthVueRoutes(routes: ElegantConstRoute[]) {
|
export function getAuthVueRoutes(authRoutes: AutoRouterRoute[]) {
|
||||||
return transformElegantRoutesToVueRoutes(routes, layouts, views);
|
return transformToVueRoutes(authRoutes, layouts, views);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
getTabByRoute,
|
getTabByRoute,
|
||||||
getTabIdByRoute,
|
getTabIdByRoute,
|
||||||
isTabInTabs,
|
isTabInTabs,
|
||||||
reorderFixedTabs,
|
|
||||||
updateTabByI18nKey,
|
updateTabByI18nKey,
|
||||||
updateTabsByI18nKey
|
updateTabsByI18nKey
|
||||||
} from './shared';
|
} from './shared';
|
||||||
@@ -249,48 +248,6 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
|
|||||||
await clearTabs(excludes);
|
await clearTabs(excludes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fix tab
|
|
||||||
*
|
|
||||||
* @param tabId
|
|
||||||
*/
|
|
||||||
function fixTab(tabId: string) {
|
|
||||||
const tabIndex = tabs.value.findIndex(t => t.id === tabId);
|
|
||||||
if (tabIndex === -1) return;
|
|
||||||
|
|
||||||
const tab = tabs.value[tabIndex];
|
|
||||||
const fixedCount = getFixedTabIds(tabs.value).length;
|
|
||||||
tab.fixedIndex = fixedCount;
|
|
||||||
|
|
||||||
if (tabIndex !== fixedCount) {
|
|
||||||
tabs.value.splice(tabIndex, 1);
|
|
||||||
tabs.value.splice(fixedCount, 0, tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
reorderFixedTabs(tabs.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unfix tab
|
|
||||||
*
|
|
||||||
* @param tabId
|
|
||||||
*/
|
|
||||||
function unfixTab(tabId: string) {
|
|
||||||
const tabIndex = tabs.value.findIndex(t => t.id === tabId);
|
|
||||||
if (tabIndex === -1) return;
|
|
||||||
|
|
||||||
const tab = tabs.value[tabIndex];
|
|
||||||
tab.fixedIndex = undefined;
|
|
||||||
|
|
||||||
const fixedCount = getFixedTabIds(tabs.value).length;
|
|
||||||
if (tabIndex !== fixedCount) {
|
|
||||||
tabs.value.splice(tabIndex, 1);
|
|
||||||
tabs.value.splice(fixedCount, 0, tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
reorderFixedTabs(tabs.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set new label of tab
|
* Set new label of tab
|
||||||
*
|
*
|
||||||
@@ -371,8 +328,6 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
|
|||||||
clearTabs,
|
clearTabs,
|
||||||
clearLeftTabs,
|
clearLeftTabs,
|
||||||
clearRightTabs,
|
clearRightTabs,
|
||||||
fixTab,
|
|
||||||
unfixTab,
|
|
||||||
switchRouteByTab,
|
switchRouteByTab,
|
||||||
setTabLabel,
|
setTabLabel,
|
||||||
resetTabLabel,
|
resetTabLabel,
|
||||||
|
|||||||
@@ -198,18 +198,6 @@ export function getFixedTabIds(tabs: App.Global.Tab[]) {
|
|||||||
return fixedTabs.map(tab => tab.id);
|
return fixedTabs.map(tab => tab.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reorder fixed tabs fixedIndex
|
|
||||||
*
|
|
||||||
* @param tabs
|
|
||||||
*/
|
|
||||||
export function reorderFixedTabs(tabs: App.Global.Tab[]) {
|
|
||||||
const fixedTabs = getFixedTabs(tabs);
|
|
||||||
fixedTabs.forEach((t, i) => {
|
|
||||||
t.fixedIndex = i;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update tabs label
|
* Update tabs label
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
/** Theme settings */
|
/** Theme settings */
|
||||||
const settings: Ref<App.Theme.ThemeSetting> = ref(initThemeSettings());
|
const settings: Ref<App.Theme.ThemeSetting> = ref(initThemeSettings());
|
||||||
|
|
||||||
/** Optional NaiveUI theme overrides from preset */
|
|
||||||
const naiveThemeOverrides: Ref<App.Theme.NaiveUIThemeOverride | undefined> = ref(undefined);
|
|
||||||
|
|
||||||
/** Watermark time instance with controls */
|
/** Watermark time instance with controls */
|
||||||
const { now: watermarkTime, pause: pauseWatermarkTime, resume: resumeWatermarkTime } = useNow({ controls: true });
|
const { now: watermarkTime, pause: pauseWatermarkTime, resume: resumeWatermarkTime } = useNow({ controls: true });
|
||||||
|
|
||||||
@@ -56,7 +53,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** Naive theme */
|
/** Naive theme */
|
||||||
const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value, naiveThemeOverrides.value));
|
const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value.recommendColor));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Settings json
|
* Settings json
|
||||||
@@ -201,15 +198,6 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set NaiveUI theme overrides
|
|
||||||
*
|
|
||||||
* @param overrides NaiveUI theme overrides or undefined to clear
|
|
||||||
*/
|
|
||||||
function setNaiveThemeOverrides(overrides?: App.Theme.NaiveUIThemeOverride) {
|
|
||||||
naiveThemeOverrides.value = overrides;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Only run timer when watermark is visible and time display is enabled */
|
/** Only run timer when watermark is visible and time display is enabled */
|
||||||
function updateWatermarkTimer() {
|
function updateWatermarkTimer() {
|
||||||
const { watermark } = settings.value;
|
const { watermark } = settings.value;
|
||||||
@@ -296,7 +284,6 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
updateThemeColors,
|
updateThemeColors,
|
||||||
setThemeLayout,
|
setThemeLayout,
|
||||||
setWatermarkEnableUserName,
|
setWatermarkEnableUserName,
|
||||||
setWatermarkEnableTime,
|
setWatermarkEnableTime
|
||||||
setNaiveThemeOverrides
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -237,30 +237,23 @@ function getNaiveThemeColors(colors: App.Theme.ThemeColor, recommended = false)
|
|||||||
* Get naive theme
|
* Get naive theme
|
||||||
*
|
*
|
||||||
* @param colors Theme colors
|
* @param colors Theme colors
|
||||||
* @param settings Theme settings object
|
* @param [recommended=false] Use recommended color. Default is `false`
|
||||||
* @param overrides Optional manual overrides from preset
|
|
||||||
*/
|
*/
|
||||||
export function getNaiveTheme(
|
export function getNaiveTheme(colors: App.Theme.ThemeColor, recommended = false) {
|
||||||
colors: App.Theme.ThemeColor,
|
|
||||||
settings: App.Theme.ThemeSetting,
|
|
||||||
overrides?: GlobalThemeOverrides
|
|
||||||
) {
|
|
||||||
const { primary: colorLoading } = colors;
|
const { primary: colorLoading } = colors;
|
||||||
|
|
||||||
const theme: GlobalThemeOverrides = {
|
const theme: GlobalThemeOverrides = {
|
||||||
common: {
|
common: {
|
||||||
...getNaiveThemeColors(colors, settings.recommendColor),
|
...getNaiveThemeColors(colors, recommended),
|
||||||
borderRadius: `${settings.themeRadius}px`
|
borderRadius: '6px'
|
||||||
},
|
},
|
||||||
LoadingBar: {
|
LoadingBar: {
|
||||||
colorLoading
|
colorLoading
|
||||||
},
|
},
|
||||||
Tag: {
|
Tag: {
|
||||||
borderRadius: `${settings.themeRadius}px`
|
borderRadius: '6px'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// If there are overrides, merge them with priority
|
return theme;
|
||||||
// overrides has higher priority than auto-generated theme
|
|
||||||
return overrides ? defu(overrides, theme) : theme;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ html,
|
|||||||
body,
|
body,
|
||||||
#app {
|
#app {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
text-autospace: normal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
@mixin scrollbar($size: 7px, $color: rgba(0, 0, 0, 0.5), $dark-color: rgba(255, 255, 255, 0.5)) {
|
@mixin scrollbar($size: 7px, $color: rgba(0, 0, 0, 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;
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
"name": "Azir's Preset",
|
"name": "Azir's Preset",
|
||||||
"desc": "It is a cold and elegant preset that Azir likes",
|
"desc": "It is a cold and elegant preset that Azir likes",
|
||||||
"i18nkey": "theme.appearance.preset.azir",
|
"i18nkey": "theme.appearance.preset.azir",
|
||||||
"version": "1.0.1",
|
"version": "1.0.0",
|
||||||
"themeScheme": "light",
|
"themeScheme": "light",
|
||||||
|
"grayscale": false,
|
||||||
|
"colourWeakness": false,
|
||||||
"recommendColor": true,
|
"recommendColor": true,
|
||||||
"themeColor": "#78a878",
|
"themeColor": "#78a878",
|
||||||
"otherColor": {
|
"otherColor": {
|
||||||
@@ -13,6 +15,55 @@
|
|||||||
"error": "#c49a9a"
|
"error": "#c49a9a"
|
||||||
},
|
},
|
||||||
"isInfoFollowPrimary": true,
|
"isInfoFollowPrimary": true,
|
||||||
|
"layout": {
|
||||||
|
"mode": "vertical-mix",
|
||||||
|
"scrollMode": "wrapper"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"animate": true,
|
||||||
|
"animateMode": "zoom-fade"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"height": 64,
|
||||||
|
"breadcrumb": {
|
||||||
|
"visible": true,
|
||||||
|
"showIcon": true
|
||||||
|
},
|
||||||
|
"multilingual": {
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
"globalSearch": {
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tab": {
|
||||||
|
"visible": true,
|
||||||
|
"cache": true,
|
||||||
|
"height": 48,
|
||||||
|
"mode": "chrome"
|
||||||
|
},
|
||||||
|
"fixedHeaderAndTab": true,
|
||||||
|
"sider": {
|
||||||
|
"inverted": false,
|
||||||
|
"width": 220,
|
||||||
|
"collapsedWidth": 64,
|
||||||
|
"mixWidth": 90,
|
||||||
|
"mixCollapsedWidth": 64,
|
||||||
|
"mixChildMenuWidth": 200
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"visible": true,
|
||||||
|
"fixed": true,
|
||||||
|
"height": 56,
|
||||||
|
"right": true
|
||||||
|
},
|
||||||
|
"watermark": {
|
||||||
|
"visible": false,
|
||||||
|
"text": "SoybeanAdmin",
|
||||||
|
"enableUserName": false,
|
||||||
|
"enableTime": true,
|
||||||
|
"timeFormat": "YYYY-MM-DD HH:mm:ss"
|
||||||
|
},
|
||||||
"tokens": {
|
"tokens": {
|
||||||
"light": {
|
"light": {
|
||||||
"colors": {
|
"colors": {
|
||||||
@@ -34,19 +85,5 @@
|
|||||||
"base-text": "rgb(224, 224, 224)"
|
"base-text": "rgb(224, 224, 224)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"naiveui": {
|
|
||||||
"Alert": {
|
|
||||||
"borderRadiusMedium": "12px",
|
|
||||||
"fontWeightStrong": "600",
|
|
||||||
"paddingMedium": "0 20px"
|
|
||||||
},
|
|
||||||
"Card": {
|
|
||||||
"borderRadius": "16px",
|
|
||||||
"paddingMedium": "24px"
|
|
||||||
},
|
|
||||||
"Input": {
|
|
||||||
"borderRadius": "10px"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,32 @@
|
|||||||
"name": "Compact Preset",
|
"name": "Compact Preset",
|
||||||
"desc": "Compact layout preset for small screens",
|
"desc": "Compact layout preset for small screens",
|
||||||
"i18nkey": "theme.appearance.preset.compact",
|
"i18nkey": "theme.appearance.preset.compact",
|
||||||
"version": "1.0.1",
|
"version": "1.0.0",
|
||||||
"themeRadius": 6,
|
"themeScheme": "light",
|
||||||
|
"grayscale": false,
|
||||||
|
"colourWeakness": false,
|
||||||
|
"recommendColor": false,
|
||||||
|
"themeColor": "#646cff",
|
||||||
|
"otherColor": {
|
||||||
|
"info": "#2080f0",
|
||||||
|
"success": "#52c41a",
|
||||||
|
"warning": "#faad14",
|
||||||
|
"error": "#f5222d"
|
||||||
|
},
|
||||||
|
"isInfoFollowPrimary": true,
|
||||||
|
"layout": {
|
||||||
|
"mode": "vertical",
|
||||||
|
"scrollMode": "content"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"animate": true,
|
||||||
|
"animateMode": "fade-slide"
|
||||||
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"height": 48,
|
"height": 48,
|
||||||
"breadcrumb": {
|
"breadcrumb": {
|
||||||
"visible": false
|
"visible": true,
|
||||||
|
"showIcon": true
|
||||||
},
|
},
|
||||||
"multilingual": {
|
"multilingual": {
|
||||||
"visible": false
|
"visible": false
|
||||||
@@ -20,9 +40,9 @@
|
|||||||
"visible": true,
|
"visible": true,
|
||||||
"cache": true,
|
"cache": true,
|
||||||
"height": 36,
|
"height": 36,
|
||||||
"mode": "button",
|
"mode": "button"
|
||||||
"closeTabByMiddleClick": false
|
|
||||||
},
|
},
|
||||||
|
"fixedHeaderAndTab": true,
|
||||||
"sider": {
|
"sider": {
|
||||||
"inverted": false,
|
"inverted": false,
|
||||||
"width": 180,
|
"width": 180,
|
||||||
@@ -32,6 +52,38 @@
|
|||||||
"mixChildMenuWidth": 180
|
"mixChildMenuWidth": 180
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"visible": false
|
"visible": false,
|
||||||
|
"fixed": false,
|
||||||
|
"height": 40,
|
||||||
|
"right": true
|
||||||
|
},
|
||||||
|
"watermark": {
|
||||||
|
"visible": false,
|
||||||
|
"text": "SoybeanAdmin",
|
||||||
|
"enableUserName": false,
|
||||||
|
"enableTime": false,
|
||||||
|
"timeFormat": "YYYY-MM-DD HH:mm"
|
||||||
|
},
|
||||||
|
"tokens": {
|
||||||
|
"light": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(255, 255, 255)",
|
||||||
|
"layout": "rgb(247, 250, 252)",
|
||||||
|
"inverted": "rgb(0, 20, 40)",
|
||||||
|
"base-text": "rgb(31, 31, 31)"
|
||||||
|
},
|
||||||
|
"boxShadow": {
|
||||||
|
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||||
|
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||||
|
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(28, 28, 28)",
|
||||||
|
"layout": "rgb(18, 18, 18)",
|
||||||
|
"base-text": "rgb(224, 224, 224)"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,18 @@
|
|||||||
"name": "Dark Preset",
|
"name": "Dark Preset",
|
||||||
"desc": "Dark theme preset for night time usage",
|
"desc": "Dark theme preset for night time usage",
|
||||||
"i18nkey": "theme.appearance.preset.dark",
|
"i18nkey": "theme.appearance.preset.dark",
|
||||||
"version": "1.0.1",
|
"version": "1.0.0",
|
||||||
"themeScheme": "dark",
|
"themeScheme": "dark",
|
||||||
"grayscale": false,
|
"grayscale": false,
|
||||||
"colourWeakness": false,
|
"colourWeakness": false,
|
||||||
"recommendColor": false,
|
"recommendColor": false,
|
||||||
"themeColor": "#646cff",
|
"themeColor": "#409eff",
|
||||||
"otherColor": {
|
"otherColor": {
|
||||||
"info": "#2080f0",
|
"info": "#2080f0",
|
||||||
"success": "#52c41a",
|
"success": "#52c41a",
|
||||||
"warning": "#faad14",
|
"warning": "#faad14",
|
||||||
"error": "#f5222d"
|
"error": "#f5222d"
|
||||||
},
|
},
|
||||||
"themeRadius": 6,
|
|
||||||
"isInfoFollowPrimary": true,
|
"isInfoFollowPrimary": true,
|
||||||
"layout": {
|
"layout": {
|
||||||
"mode": "vertical",
|
"mode": "vertical",
|
||||||
@@ -41,12 +40,11 @@
|
|||||||
"visible": true,
|
"visible": true,
|
||||||
"cache": true,
|
"cache": true,
|
||||||
"height": 44,
|
"height": 44,
|
||||||
"mode": "chrome",
|
"mode": "chrome"
|
||||||
"closeTabByMiddleClick": false
|
|
||||||
},
|
},
|
||||||
"fixedHeaderAndTab": true,
|
"fixedHeaderAndTab": true,
|
||||||
"sider": {
|
"sider": {
|
||||||
"inverted": false,
|
"inverted": true,
|
||||||
"width": 220,
|
"width": 220,
|
||||||
"collapsedWidth": 64,
|
"collapsedWidth": 64,
|
||||||
"mixWidth": 90,
|
"mixWidth": 90,
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
"warning": "#faad14",
|
"warning": "#faad14",
|
||||||
"error": "#f5222d"
|
"error": "#f5222d"
|
||||||
},
|
},
|
||||||
"themeRadius": 6,
|
|
||||||
"isInfoFollowPrimary": true,
|
"isInfoFollowPrimary": true,
|
||||||
"layout": {
|
"layout": {
|
||||||
"mode": "vertical",
|
"mode": "vertical",
|
||||||
@@ -41,8 +40,7 @@
|
|||||||
"visible": true,
|
"visible": true,
|
||||||
"cache": true,
|
"cache": true,
|
||||||
"height": 44,
|
"height": 44,
|
||||||
"mode": "chrome",
|
"mode": "chrome"
|
||||||
"closeTabByMiddleClick": false
|
|
||||||
},
|
},
|
||||||
"fixedHeaderAndTab": true,
|
"fixedHeaderAndTab": true,
|
||||||
"sider": {
|
"sider": {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ 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',
|
||||||
@@ -38,8 +37,7 @@ 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: {
|
||||||
@@ -48,8 +46,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
|||||||
collapsedWidth: 64,
|
collapsedWidth: 64,
|
||||||
mixWidth: 90,
|
mixWidth: 90,
|
||||||
mixCollapsedWidth: 64,
|
mixCollapsedWidth: 64,
|
||||||
mixChildMenuWidth: 200,
|
mixChildMenuWidth: 200
|
||||||
autoSelectFirstMenu: false
|
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|||||||
70
src/typings/api/common.d.ts
vendored
70
src/typings/api/common.d.ts
vendored
@@ -5,46 +5,38 @@
|
|||||||
*/
|
*/
|
||||||
declare namespace Api {
|
declare namespace Api {
|
||||||
namespace Common {
|
namespace Common {
|
||||||
/** common params of paginating */
|
interface CreatedUpdatedDTO {
|
||||||
interface PaginatingCommonParams {
|
/** 创建人 */
|
||||||
/** current page number */
|
createdBy: string;
|
||||||
current: number;
|
/** 创建时间 */
|
||||||
/** page size */
|
createdTime: string;
|
||||||
size: number;
|
/** 更新人 */
|
||||||
/** total count */
|
updatedBy: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
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
Normal file
42
src/typings/api/menu.d.ts
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
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,19 +1,34 @@
|
|||||||
declare namespace Api {
|
declare namespace Api {
|
||||||
/**
|
|
||||||
* namespace Route
|
|
||||||
*
|
|
||||||
* backend api module: "route"
|
|
||||||
*/
|
|
||||||
namespace Route {
|
namespace Route {
|
||||||
type ElegantConstRoute = import('@elegant-router/types').ElegantConstRoute;
|
interface RouteDTO extends Common.CommonRecordDTO {
|
||||||
|
/** 路由路径 */
|
||||||
interface MenuRoute extends ElegantConstRoute {
|
path: string;
|
||||||
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 UserRoute {
|
interface RouteListDTO extends Common.PaginationDTO {
|
||||||
routes: MenuRoute[];
|
list: RouteDTO[];
|
||||||
home: import('@elegant-router/types').LastLevelRouteKey;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
src/typings/api/union.d.ts
vendored
Normal file
31
src/typings/api/union.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/typings/app.d.ts
vendored
110
src/typings/app.d.ts
vendored
@@ -4,9 +4,6 @@ declare namespace App {
|
|||||||
namespace Theme {
|
namespace Theme {
|
||||||
type ColorPaletteNumber = import('@sa/color').ColorPaletteNumber;
|
type ColorPaletteNumber = import('@sa/color').ColorPaletteNumber;
|
||||||
|
|
||||||
/** NaiveUI theme overrides that can be specified in preset */
|
|
||||||
type NaiveUIThemeOverride = import('naive-ui').GlobalThemeOverrides;
|
|
||||||
|
|
||||||
/** Theme setting */
|
/** Theme setting */
|
||||||
interface ThemeSetting {
|
interface ThemeSetting {
|
||||||
/** Theme scheme */
|
/** Theme scheme */
|
||||||
@@ -19,8 +16,6 @@ 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 */
|
||||||
@@ -74,8 +69,6 @@ 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;
|
||||||
@@ -96,8 +89,6 @@ declare namespace App {
|
|||||||
mixCollapsedWidth: number;
|
mixCollapsedWidth: number;
|
||||||
/** Child menu width when the layout is 'vertical-mix', 'top-hybrid-sidebar-first', or 'top-hybrid-header-first' */
|
/** Child menu width when the layout is 'vertical-mix', 'top-hybrid-sidebar-first', or 'top-hybrid-header-first' */
|
||||||
mixChildMenuWidth: number;
|
mixChildMenuWidth: number;
|
||||||
/** Whether to auto select the first submenu */
|
|
||||||
autoSelectFirstMenu: boolean;
|
|
||||||
};
|
};
|
||||||
/** Footer */
|
/** Footer */
|
||||||
footer: {
|
footer: {
|
||||||
@@ -188,9 +179,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 RouteMap = import('@elegant-router/types').RouteMap;
|
type RoutePathMap = import('@elegant-router/types').RoutePathMap;
|
||||||
type RoutePath = import('@elegant-router/types').RoutePath;
|
type RoutePath = import('@elegant-router/types').RoutePath;
|
||||||
type LastLevelRouteKey = import('@elegant-router/types').LastLevelRouteKey;
|
type RouteFileKey = import('@elegant-router/types').RouteFileKey;
|
||||||
|
|
||||||
/** The router push options */
|
/** The router push options */
|
||||||
type RouterPushOptions = {
|
type RouterPushOptions = {
|
||||||
@@ -208,83 +199,31 @@ declare namespace App {
|
|||||||
showMenu?: boolean;
|
showMenu?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MenuType = 'page' | 'directory' | 'menu' | 'iframe' | 'link' | 'button' | 'other';
|
||||||
|
|
||||||
/** The global menu */
|
/** The global menu */
|
||||||
type Menu = {
|
interface Menu {
|
||||||
/**
|
id: number;
|
||||||
* The menu key
|
name: string;
|
||||||
*
|
menuType: MenuType;
|
||||||
* Equal to the route key
|
description?: string | null;
|
||||||
*/
|
icon?: string | null;
|
||||||
key: string;
|
iconType?: string | null;
|
||||||
/** The menu label */
|
parentId?: number | null;
|
||||||
label: string;
|
order?: number | null;
|
||||||
/** The menu i18n key */
|
isHidden?: boolean | null;
|
||||||
i18nKey?: I18n.I18nKey | null;
|
iframeUrl?: string | null;
|
||||||
/** The route key */
|
href?: string | null;
|
||||||
routeKey: RouteKey;
|
routeName?: RouteKey | null;
|
||||||
/** The route path */
|
routeQueries?: Record<string, string | number | boolean> | null;
|
||||||
routePath: RoutePath;
|
routeParams?: Record<string, string | number | boolean> | null;
|
||||||
/** 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;
|
||||||
|
|
||||||
/** The global dropdown key */
|
/** The global dropdown key */
|
||||||
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll' | 'pin' | 'unpin';
|
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -386,9 +325,6 @@ 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;
|
||||||
@@ -412,8 +348,6 @@ 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;
|
||||||
@@ -431,8 +365,6 @@ declare namespace App {
|
|||||||
mixWidth: string;
|
mixWidth: string;
|
||||||
mixCollapsedWidth: string;
|
mixCollapsedWidth: string;
|
||||||
mixChildMenuWidth: string;
|
mixChildMenuWidth: string;
|
||||||
autoSelectFirstMenu: string;
|
|
||||||
autoSelectFirstMenuTip: string;
|
|
||||||
};
|
};
|
||||||
footer: {
|
footer: {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
83
src/typings/components.d.ts
vendored
83
src/typings/components.d.ts
vendored
@@ -1,12 +1,8 @@
|
|||||||
/* 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
|
||||||
import { GlobalComponents } from 'vue'
|
// biome-ignore lint: disable
|
||||||
|
|
||||||
export {}
|
export {}
|
||||||
|
|
||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
@@ -70,6 +66,7 @@ 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']
|
||||||
@@ -86,79 +83,3 @@ 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,240 +1,100 @@
|
|||||||
/* 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 ElegantConstRoute = import('@elegant-router/vue').ElegantConstRoute;
|
type RouteRecordSingleView = import("vue-router").RouteRecordSingleView;
|
||||||
|
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
|
* route layout key
|
||||||
*/
|
*/
|
||||||
export type RouteLayout = "base" | "blank";
|
export type RouteLayoutKey = "base" | "blank";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* route map
|
* route path map
|
||||||
*/
|
*/
|
||||||
export type RouteMap = {
|
export type RoutePathMap = {
|
||||||
"root": "/";
|
"Root": "/";
|
||||||
"not-found": "/:pathMatch(.*)*";
|
"NotFound": "/:pathMatch(.*)*";
|
||||||
"403": "/403";
|
"403": "/403";
|
||||||
"404": "/404";
|
"404": "/404";
|
||||||
"500": "/500";
|
"500": "/500";
|
||||||
"home": "/home";
|
"Home": "/home";
|
||||||
"iframe-page": "/iframe-page/:url";
|
"IframeUrl": "/iframe/: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 RouteMap;
|
export type RouteKey = keyof RoutePathMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* route path
|
* route path
|
||||||
*/
|
*/
|
||||||
export type RoutePath = RouteMap[RouteKey];
|
export type RoutePath = RoutePathMap[RouteKey];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* custom route key
|
* root route key
|
||||||
*/
|
*/
|
||||||
export type CustomRouteKey = Extract<
|
export type RootRouteKey = 'Root';
|
||||||
RouteKey,
|
|
||||||
| "root"
|
|
||||||
| "not-found"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the generated route key
|
* not found route key
|
||||||
*/
|
*/
|
||||||
export type GeneratedRouteKey = Exclude<RouteKey, CustomRouteKey>;
|
export type NotFoundRouteKey = 'NotFound';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the first level route key, which contain the layout of the route
|
* builtin route key
|
||||||
*/
|
*/
|
||||||
export type FirstLevelRouteKey = Extract<
|
export type BuiltinRouteKey = RootRouteKey | NotFoundRouteKey;
|
||||||
RouteKey,
|
|
||||||
| "403"
|
|
||||||
| "404"
|
|
||||||
| "500"
|
|
||||||
| "home"
|
|
||||||
| "iframe-page"
|
|
||||||
| "login"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the custom first level route key
|
* reuse route key
|
||||||
*/
|
*/
|
||||||
export type CustomFirstLevelRouteKey = Extract<
|
export type ReuseRouteKey = never;
|
||||||
CustomRouteKey,
|
|
||||||
| "root"
|
|
||||||
| "not-found"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the last level route key, which has the page file
|
* the route file key, which has it's own file
|
||||||
*/
|
*/
|
||||||
export type LastLevelRouteKey = Extract<
|
export type RouteFileKey = Exclude<RouteKey, BuiltinRouteKey | ReuseRouteKey>;
|
||||||
RouteKey,
|
|
||||||
| "403"
|
|
||||||
| "404"
|
|
||||||
| "500"
|
|
||||||
| "iframe-page"
|
|
||||||
| "login"
|
|
||||||
| "home"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the custom last level route key
|
* mapped name and path
|
||||||
*/
|
*/
|
||||||
export type CustomLastLevelRouteKey = Extract<
|
type MappedNamePath = {
|
||||||
CustomRouteKey,
|
[K in RouteKey]: { name: K; path: RoutePathMap[K] };
|
||||||
| "root"
|
}[RouteKey];
|
||||||
| "not-found"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the single level route key
|
* auto router single view
|
||||||
*/
|
*/
|
||||||
export type SingleLevelRouteKey = FirstLevelRouteKey & LastLevelRouteKey;
|
export type AutoRouterSingleView = Omit<RouteRecordSingleView, 'component' | 'name' | 'path'> & {
|
||||||
|
component: RouteFileKey;
|
||||||
|
layout: RouteLayoutKey;
|
||||||
|
} & MappedNamePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the custom single level route key
|
* auto router redirect
|
||||||
*/
|
*/
|
||||||
export type CustomSingleLevelRouteKey = CustomFirstLevelRouteKey & CustomLastLevelRouteKey;
|
export type AutoRouterRedirect = Omit<RouteRecordRedirect, 'children' | 'name' | 'path'> & MappedNamePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the first level route key, but not the single level
|
* auto router route
|
||||||
*/
|
*/
|
||||||
export type FirstLevelRouteNotSingleKey = Exclude<FirstLevelRouteKey, SingleLevelRouteKey>;
|
export type AutoRouterRoute = AutoRouterSingleView | AutoRouterRedirect;
|
||||||
|
|
||||||
/**
|
|
||||||
* the custom first level route key, but not the single level
|
|
||||||
*/
|
|
||||||
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 @@
|
|||||||
import 'vue-router';
|
export {};
|
||||||
|
|
||||||
declare module 'vue-router' {
|
declare module 'vue-router' {
|
||||||
interface RouteMeta {
|
export interface RouteMeta {
|
||||||
/**
|
/**
|
||||||
* Title of the route
|
* Title of the route
|
||||||
*
|
*
|
||||||
|
|||||||
40
src/typings/typed-router.d.ts
vendored
Normal file
40
src/typings/typed-router.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/* 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>>
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/views/(builtin)/wip/index.vue
Normal file
5
src/views/(builtin)/wip/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<LookForward />
|
||||||
|
</template>
|
||||||
@@ -15,7 +15,7 @@ const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NSpace vertical :size="16">
|
<NSpace vertical :size="16">
|
||||||
<NAlert :title="$t('common.tip')" type="warning">
|
<NAlert :title="$t('common.warning')" type="warning">
|
||||||
{{ $t('page.home.branchDesc') }}
|
{{ $t('page.home.branchDesc') }}
|
||||||
</NAlert>
|
</NAlert>
|
||||||
<HeaderBanner />
|
<HeaderBanner />
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<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({
|
||||||
@@ -73,8 +72,6 @@ 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})`;
|
||||||
}
|
}
|
||||||
@@ -84,10 +81,7 @@ 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
|
<div class="rd-8px px-16px pb-4px pt-8px text-white" :style="{ backgroundImage: gradientColor }">
|
||||||
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,14 +21,13 @@ 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%',
|
||||||
top: '15%'
|
containLabel: true
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
|
|||||||
5
src/views/manage/menu/index.vue
Normal file
5
src/views/manage/menu/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>ManageMenu</div>
|
||||||
|
</template>
|
||||||
5
src/views/manage/role/index.vue
Normal file
5
src/views/manage/role/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>ManageRole</div>
|
||||||
|
</template>
|
||||||
5
src/views/manage/route/index.vue
Normal file
5
src/views/manage/route/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>ManageRoute</div>
|
||||||
|
</template>
|
||||||
5
src/views/manage/user/index.vue
Normal file
5
src/views/manage/user/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>ManageUser</div>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user