Compare commits

..

2 Commits

68 changed files with 1905 additions and 3487 deletions

View File

@@ -5,6 +5,7 @@
"antfu.unocss",
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"lokalise.i18n-ally",
"mhutchie.git-graph",
"mikestead.dotenv",

13
.vscode/settings.json vendored
View File

@@ -4,18 +4,7 @@
"source.organizeImports": "never"
},
"editor.formatOnSave": false,
"eslint.validate": [
"html",
"css",
"scss",
"json",
"jsonc",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue"
],
"eslint.validate": ["html", "css", "scss", "json", "jsonc"],
"i18n-ally.displayLanguage": "zh-cn",
"i18n-ally.enabledParsers": ["ts"],
"i18n-ally.enabledFrameworks": ["vue"],

View File

@@ -1,331 +1,6 @@
# 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 &nbsp;-&nbsp; by @soybeanjs [<samp>(46081)</samp>](https://github.com/soybeanjs/soybean-admin/commit/46081c36)
- **projects**:
- support theme presets to only set partial content. &nbsp;-&nbsp; by **Azir-11** [<samp>(9da84)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9da847fb)
- support theme perset to override component library presets. &nbsp;-&nbsp; by **Azir-11** [<samp>(60517)</samp>](https://github.com/soybeanjs/soybean-admin/commit/605173a1)
- support pinning and unpinning of tabs &nbsp;-&nbsp; by **hooke** [<samp>(b8a76)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b8a767d7)
- hybrid layout mode auto select first deepest child menu &nbsp;-&nbsp; by @paynezhuang [<samp>(94019)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9401925f)
### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
- **docs**: update project name in ecosystem section of README &nbsp;-&nbsp; by @soybeanjs [<samp>(bb232)</samp>](https://github.com/soybeanjs/soybean-admin/commit/bb232bf8)
- **types**: add missing property in theme presets &nbsp;-&nbsp; by **刘璐** [<samp>(4a9cf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4a9cf6c3)
### &nbsp;&nbsp;&nbsp;🛠 Optimizations
- **projects**: simplify some theme preset configurations. &nbsp;-&nbsp; by **Azir-11** [<samp>(c6d97)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c6d97dba)
### &nbsp;&nbsp;&nbsp;📖 Documentation
- **projects**: add link to ecosystem document. &nbsp;-&nbsp; by **Azir-11** [<samp>(c472a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c472a943)
### &nbsp;&nbsp;&nbsp;🏡 Chore
- **deps**:
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(f8dc6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f8dc639e)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(7cf40)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7cf4083b)
- **other**:
- remove Prettier's recommendation. &nbsp;-&nbsp; by **Azir-11** [<samp>(73e9a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/73e9a0fe)
- **styles**:
- format code &nbsp;-&nbsp; by @soybeanjs [<samp>(098cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/098cd50e)
### &nbsp;&nbsp;&nbsp;🎨 Styles
- **projects**: modify homepage prompt title to tip. &nbsp;-&nbsp; by **Azir-11** [<samp>(91a26)</samp>](https://github.com/soybeanjs/soybean-admin/commit/91a261c1)
### &nbsp;&nbsp;&nbsp;❤️ Contributors
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![paynezhuang](https://github.com/paynezhuang.png?size=48)](https://github.com/paynezhuang)&nbsp;&nbsp;
[hooke](mailto:hellohooke@foxmail.com),&nbsp;[Azir-11](mailto:2075125282@qq.com),&nbsp;[刘璐](mailto:hi.alue@qq.com)
## [v2.0.0](https://github.com/soybeanjs/soybean-admin/compare/v1.3.15...v2.0.0) (2025-11-02)
### &nbsp;&nbsp;&nbsp;🚨 Breaking Changes
- **hooks**: refactor useTable and enhance type definitions &nbsp;-&nbsp; 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. &nbsp;-&nbsp; by **Azir** [<samp>(b6ac3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b6ac3106)
- **request**: remove cancelRequest method and related logic from request instances &nbsp;-&nbsp; by @soybeanjs [<samp>(b4e12)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e12530)
### &nbsp;&nbsp;&nbsp;🚀 Features
- **components**:
- add the IconTooltip component. &nbsp;-&nbsp; 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. &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by @soybeanjs [<samp>(b2c91)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b2c919b6)
- **hooks**:
- add scrollX computation for total table width in useNaiveTable &nbsp;-&nbsp; by @Lruihao [<samp>(358e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/358e1297)
- **packages**:
- materials support slider-tab. closed #823 &nbsp;-&nbsp; 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. &nbsp;-&nbsp; by **Azir** [<samp>(8ba71)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8ba71a08)
- Add current time display option for watermark &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by @wenyuanw [<samp>(b4e5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e5c6d9)
- add prompt information for scrolling mode and tab bar caching. &nbsp;-&nbsp; by **Azir-11** [<samp>(29a2a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/29a2a5c6)
- support theme preset function. &nbsp;-&nbsp; 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'. &nbsp;-&nbsp; by **Azir-11** [<samp>(3c0a5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3c0a5282)
- optimize tabs cache cleaning strategy. close #820. &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by @wenyuanw [<samp>(a8d1e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a8d1e5d2)
- support set global redius &nbsp;-&nbsp; by **CyberShen123** [<samp>(24c6d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/24c6df52)
- support set global redius &nbsp;-&nbsp; by **CyberShen123** [<samp>(3549c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3549c4db)
- compatible with the new Echarts API and optimized styles. &nbsp;-&nbsp; by **Azir-11** [<samp>(9755c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9755c313)
- **styles**:
- add text-autospace property to improve text layout &nbsp;-&nbsp; by @wenyuanw [<samp>(345aa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/345aa293)
### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
- **hooks**:
- correct chart rendering logic in useEcharts &nbsp;-&nbsp; by @soybeanjs [<samp>(8a7cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8a7cd593)
- **layout**:
- fix getSiderWidth &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by @Lruihao [<samp>(9ea56)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9ea56c9b)
- axios: fix json response. fixed #815 &nbsp;-&nbsp; 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 &nbsp;-&nbsp; 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 &nbsp;-&nbsp; 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. &nbsp;-&nbsp; by **Azir-11** [<samp>(0b998)</samp>](https://github.com/soybeanjs/soybean-admin/commit/0b9982bd)
- **readme**:
- update GitHub stars and forks links for gitee &nbsp;-&nbsp; by @soybeanjs [<samp>(923eb)</samp>](https://github.com/soybeanjs/soybean-admin/commit/923eb98a)
- **scripts**:
- update command to use 'npm-check-updates' instead of 'ncu' &nbsp;-&nbsp; by @soybeanjs [<samp>(8dc17)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8dc17e62)
- **styles**:
- show light color scrollbar while dark mode is on &nbsp;-&nbsp; by **whyang** [<samp>(dac50)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dac5075b)
- **table**:
- add type annotations for records in useTable hook &nbsp;-&nbsp; by @soybeanjs [<samp>(32b8f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/32b8f990)
- **types**:
- fix proxy types &nbsp;-&nbsp; by @soybeanjs [<samp>(3d72f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3d72f954)
- fix proxy types &nbsp;-&nbsp; by @soybeanjs [<samp>(12b25)</samp>](https://github.com/soybeanjs/soybean-admin/commit/12b25e0d)
- fix ts type error &nbsp;-&nbsp; by @soybeanjs [<samp>(d5a3a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d5a3a25d)
### &nbsp;&nbsp;&nbsp;🛠 Optimizations
- **hooks**:
- optimize useEcharts &nbsp;-&nbsp; by @soybeanjs [<samp>(936b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/936b834e)
- **packages**:
- remove ofetch package &nbsp;-&nbsp; by @soybeanjs [<samp>(abaaa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/abaaa4a0)
- **projects**:
- improve theme drawer responsive width for mobile devices &nbsp;-&nbsp; by @wenyuanw [<samp>(8439a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8439a600)
- improve robustness of second-level menu key logic &nbsp;-&nbsp; by @wenyuanw [<samp>(8b8a2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8b8a2083)
- optimize theme drawer width &nbsp;-&nbsp; by @soybeanjs [<samp>(81468)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8146858b)
- optimize api type file &nbsp;-&nbsp; by @soybeanjs [<samp>(3a343)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3a343eea)
- optimize radius settings &nbsp;-&nbsp; by @soybeanjs [<samp>(87a66)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87a66a42)
- **request**:
- enhance request options and response handling with generic types &nbsp;-&nbsp; by @soybeanjs [<samp>(50a5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/50a5cba0)
- **typings**:
- update component typings &nbsp;-&nbsp; by @soybeanjs [<samp>(1d142)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1d142695)
### &nbsp;&nbsp;&nbsp;💅 Refactors
- **hooks**:
- optimize useContext and update useMixMenuContext &nbsp;-&nbsp; by @soybeanjs [<samp>(c9651)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c965140b)
- streamline column visibility handling in useTable and table components &nbsp;-&nbsp; by @soybeanjs [<samp>(ee434)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ee434145)
- remove useSignal hook and update exports &nbsp;-&nbsp; by @soybeanjs [<samp>(87adc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87adc35f)
- **menu**:
- optimize the margin on the menu &nbsp;-&nbsp; by **NicholasLD** [<samp>(d7311)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d7311111)
- **projects**:
- remove unnecessary logic in onRouteSwitchWhenLoggedIn &nbsp;-&nbsp; by @wenyuanw [<samp>(d6c81)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d6c8142b)
- **request**:
- unify response transformation methods and deprecate transformBackendResponse &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by **Azir** [<samp>(d37ce)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d37ce046)
### &nbsp;&nbsp;&nbsp;📖 Documentation
- **projects**:
- add github trendshift info. &nbsp;-&nbsp; by **恕瑞玛的皇帝** [<samp>(e18d3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e18d3972)
- add github trendshift info. &nbsp;-&nbsp; by **恕瑞玛的皇帝** [<samp>(2a0c9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2a0c9f1b)
- add contribution leaderboard &nbsp;-&nbsp; by @wenyuanw [<samp>(01744)</samp>](https://github.com/soybeanjs/soybean-admin/commit/017440c1)
### &nbsp;&nbsp;&nbsp;🏡 Chore
- **deps**:
- update NodeJS and pnpm version requirements in package.json and documentation &nbsp;-&nbsp; by **Junior25306** [<samp>(a5c4b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a5c4b4e3)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(5cb1c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5cb1cebd)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(aeb63)</samp>](https://github.com/soybeanjs/soybean-admin/commit/aeb63690)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(e89b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e89b86ce)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(c962f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c962f7b2)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(12135)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1213531b)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(e33f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e33f944a)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(9fa95)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9fa951aa)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(b041f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b041fdd8)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(d567c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d567c057)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(6cbf5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6cbf5705)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(6010f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6010f518)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(232f5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/232f56fd)
- **other**:
- update the ESLint validation configuration to support more file types. &nbsp;-&nbsp; by **Azir-11** [<samp>(8d7f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8d7f91dc)
- update the ESLint validation configuration to support more file types. &nbsp;-&nbsp; 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. &nbsp;-&nbsp; by **Azir** [<samp>(03dd6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/03dd64c5)
- add picomatch to fix scripts &nbsp;-&nbsp; by @soybeanjs [<samp>(805c3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/805c3381)
- **readme**:
- remove DartNode sponsorship badge from README files &nbsp;-&nbsp; by @soybeanjs [<samp>(33ade)</samp>](https://github.com/soybeanjs/soybean-admin/commit/33ade539)
- **vscode**:
- remove unused vue.server.hybridMode setting from .vscode/settings.json &nbsp;-&nbsp; by @soybeanjs [<samp>(13319)</samp>](https://github.com/soybeanjs/soybean-admin/commit/133196f3)
### &nbsp;&nbsp;&nbsp;🎨 Styles
- **projects**: format code. &nbsp;-&nbsp; by **Azir-11** [<samp>(100e0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/100e0ea5)
### &nbsp;&nbsp;&nbsp;❤️ Contributors
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![wenyuanw](https://github.com/wenyuanw.png?size=48)](https://github.com/wenyuanw)&nbsp;&nbsp;[![CyberShen](https://github.com/CyberShen.png?size=48)](https://github.com/CyberShen)&nbsp;&nbsp;[![Lruihao](https://github.com/Lruihao.png?size=48)](https://github.com/Lruihao)&nbsp;&nbsp;[![xiaobao0505](https://github.com/xiaobao0505.png?size=48)](https://github.com/xiaobao0505)&nbsp;&nbsp;
[Azir-11](mailto:2075125282@qq.com),&nbsp;[CyberShen123](mailto:s.lijun@qq.com),&nbsp;[whyang](mailto:whyang9701@gmail.com),&nbsp;[HongxuanG](mailto:1359774872@qq.com),&nbsp;[NicholasLD](mailto:878639947@qq.com),&nbsp;[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)
### &nbsp;&nbsp;&nbsp;🚀 Features
- **global-tab**: add support for switching tabs with right mouse button click &nbsp;-&nbsp; by @soybeanjs [<samp>(b2c91)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b2c919b6)
### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
- **layout**: fix getSiderWidth &nbsp;-&nbsp; by @soybeanjs [<samp>(e471e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e471e914)
- **packages**: axios: fix json response. fixed #815 &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by @soybeanjs [<samp>(923eb)</samp>](https://github.com/soybeanjs/soybean-admin/commit/923eb98a)
- **scripts**: update command to use 'npm-check-updates' instead of 'ncu' &nbsp;-&nbsp; by @soybeanjs [<samp>(8dc17)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8dc17e62)
- **types**: fix proxy types &nbsp;-&nbsp; by @soybeanjs [<samp>(12b25)</samp>](https://github.com/soybeanjs/soybean-admin/commit/12b25e0d)
### &nbsp;&nbsp;&nbsp;📖 Documentation
- **projects**:
- add github trendshift info. &nbsp;-&nbsp; by **恕瑞玛的皇帝** [<samp>(e18d3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e18d3972)
- add contribution leaderboard &nbsp;-&nbsp; by @wenyuanw [<samp>(01744)</samp>](https://github.com/soybeanjs/soybean-admin/commit/017440c1)
### &nbsp;&nbsp;&nbsp;🏡 Chore
- **deps**:
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(e33f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e33f944a)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(9fa95)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9fa951aa)
- **other**:
- update the ESLint validation configuration to support more file types. &nbsp;-&nbsp; by **Azir-11** [<samp>(8d7f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8d7f91dc)
- **readme**:
- remove DartNode sponsorship badge from README files &nbsp;-&nbsp; by @soybeanjs [<samp>(33ade)</samp>](https://github.com/soybeanjs/soybean-admin/commit/33ade539)
### &nbsp;&nbsp;&nbsp;❤️ Contributors
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![wenyuanw](https://github.com/wenyuanw.png?size=48)](https://github.com/wenyuanw)&nbsp;&nbsp;
[恕瑞玛的皇帝](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)
### &nbsp;&nbsp;&nbsp;🚨 Breaking Changes
- **hooks**: refactor useTable and enhance type definitions &nbsp;-&nbsp; 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. &nbsp;-&nbsp; by **Azir** [<samp>(b6ac3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b6ac310)
- **request**: remove cancelRequest method and related logic from request instances &nbsp;-&nbsp; by @soybeanjs [<samp>(b4e12)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e1253)
### &nbsp;&nbsp;&nbsp;🚀 Features
- **components**:
- add the IconTooltip component. &nbsp;-&nbsp; 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. &nbsp;-&nbsp; by **Azir-11** [<samp>(40057)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4005763)
- **hooks**:
- add scrollX computation for total table width in useNaiveTable &nbsp;-&nbsp; by @Lruihao [<samp>(358e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/358e129)
- **packages**:
- materials support slider-tab. closed #823 &nbsp;-&nbsp; 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. &nbsp;-&nbsp; by **Azir** [<samp>(8ba71)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8ba71a0)
- Add current time display option for watermark &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by @wenyuanw [<samp>(b4e5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e5c6d)
- add prompt information for scrolling mode and tab bar caching. &nbsp;-&nbsp; by **Azir-11** [<samp>(29a2a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/29a2a5c)
- support theme preset function. &nbsp;-&nbsp; 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'. &nbsp;-&nbsp; by **Azir-11** [<samp>(3c0a5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3c0a528)
- optimize tabs cache cleaning strategy. close #820. &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by @wenyuanw [<samp>(a8d1e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a8d1e5d)
- support set global redius &nbsp;-&nbsp; by **CyberShen123** [<samp>(24c6d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/24c6df5)
- support set global redius &nbsp;-&nbsp; by **CyberShen123** [<samp>(3549c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3549c4d)
- **styles**:
- add text-autospace property to improve text layout &nbsp;-&nbsp; by @wenyuanw [<samp>(345aa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/345aa29)
### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
- **hooks**:
- correct chart rendering logic in useEcharts &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by @Lruihao [<samp>(9ea56)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9ea56c9)
- axios: fix json response. fixed #815 &nbsp;-&nbsp; 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 &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by **whyang** [<samp>(dac50)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dac5075)
- **table**:
- add type annotations for records in useTable hook &nbsp;-&nbsp; by @soybeanjs [<samp>(32b8f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/32b8f99)
- **types**:
- fix proxy types &nbsp;-&nbsp; by @soybeanjs [<samp>(3d72f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3d72f95)
- fix ts type error &nbsp;-&nbsp; by @soybeanjs [<samp>(d5a3a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d5a3a25)
### &nbsp;&nbsp;&nbsp;🛠 Optimizations
- **hooks**:
- optimize useEcharts &nbsp;-&nbsp; by @soybeanjs [<samp>(936b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/936b834)
- **packages**:
- remove ofetch package &nbsp;-&nbsp; by @soybeanjs [<samp>(abaaa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/abaaa4a)
- **projects**:
- improve theme drawer responsive width for mobile devices &nbsp;-&nbsp; by @wenyuanw [<samp>(8439a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8439a60)
- improve robustness of second-level menu key logic &nbsp;-&nbsp; by @wenyuanw [<samp>(8b8a2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8b8a208)
- optimize theme drawer width &nbsp;-&nbsp; by @soybeanjs [<samp>(81468)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8146858)
- optimize api type file &nbsp;-&nbsp; by @soybeanjs [<samp>(3a343)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3a343ee)
- optimize radius settings &nbsp;-&nbsp; by @soybeanjs [<samp>(87a66)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87a66a4)
- **request**:
- enhance request options and response handling with generic types &nbsp;-&nbsp; by @soybeanjs [<samp>(50a5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/50a5cba)
### &nbsp;&nbsp;&nbsp;💅 Refactors
- **hooks**:
- optimize useContext and update useMixMenuContext &nbsp;-&nbsp; by @soybeanjs [<samp>(c9651)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c965140)
- streamline column visibility handling in useTable and table components &nbsp;-&nbsp; by @soybeanjs [<samp>(ee434)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ee43414)
- remove useSignal hook and update exports &nbsp;-&nbsp; by @soybeanjs [<samp>(87adc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87adc35)
- **menu**:
- optimize the margin on the menu &nbsp;-&nbsp; by **NicholasLD** [<samp>(d7311)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d731111)
- **projects**:
- remove unnecessary logic in onRouteSwitchWhenLoggedIn &nbsp;-&nbsp; by @wenyuanw [<samp>(d6c81)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d6c8142)
- **request**:
- unify response transformation methods and deprecate transformBackendResponse &nbsp;-&nbsp; 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 &nbsp;-&nbsp; by **Azir** [<samp>(d37ce)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d37ce04)
### &nbsp;&nbsp;&nbsp;📖 Documentation
- **projects**: add github trendshift info. &nbsp;-&nbsp; by **恕瑞玛的皇帝** [<samp>(2a0c9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2a0c9f1)
### &nbsp;&nbsp;&nbsp;🏡 Chore
- **deps**:
- update NodeJS and pnpm version requirements in package.json and documentation &nbsp;-&nbsp; by **Junior25306** [<samp>(a5c4b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a5c4b4e)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(5cb1c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5cb1ceb)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(aeb63)</samp>](https://github.com/soybeanjs/soybean-admin/commit/aeb6369)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(e89b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e89b86c)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(c962f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c962f7b)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(12135)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1213531)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(b041f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b041fdd)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(d567c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d567c05)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(6cbf5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6cbf570)
- **other**:
- update the ESLint validation configuration to support more file types. &nbsp;-&nbsp; 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. &nbsp;-&nbsp; by **Azir** [<samp>(03dd6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/03dd64c)
- add picomatch to fix scripts &nbsp;-&nbsp; by @soybeanjs [<samp>(805c3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/805c338)
- **vscode**:
- remove unused vue.server.hybridMode setting from .vscode/settings.json &nbsp;-&nbsp; by @soybeanjs [<samp>(13319)</samp>](https://github.com/soybeanjs/soybean-admin/commit/133196f)
### &nbsp;&nbsp;&nbsp;🎨 Styles
- **projects**: format code. &nbsp;-&nbsp; by **Azir-11** [<samp>(100e0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/100e0ea)
### &nbsp;&nbsp;&nbsp;❤️ Contributors
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![wenyuanw](https://github.com/wenyuanw.png?size=48)](https://github.com/wenyuanw)&nbsp;&nbsp;[![CyberShen](https://github.com/CyberShen.png?size=48)](https://github.com/CyberShen)&nbsp;&nbsp;[![Lruihao](https://github.com/Lruihao.png?size=48)](https://github.com/Lruihao)&nbsp;&nbsp;[![xiaobao0505](https://github.com/xiaobao0505.png?size=48)](https://github.com/xiaobao0505)&nbsp;&nbsp;
[CyberShen123](mailto:s.lijun@qq.com),&nbsp;[whyang](mailto:whyang9701@gmail.com),&nbsp;[HongxuanG](mailto:1359774872@qq.com),&nbsp;[Azir-11](mailto:2075125282@qq.com),&nbsp;[NicholasLD](mailto:878639947@qq.com),&nbsp;[Junior25306](mailto:dayu429@qq.com)
## [v1.3.15](https://github.com/soybeanjs/soybean-admin/compare/v1.3.14...v1.3.15) (2025-06-24)
### &nbsp;&nbsp;&nbsp;🚀 Features

View File

@@ -11,11 +11,10 @@
[![github forks](https://img.shields.io/github/forks/honghuangdc/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
[![gitee stars](https://gitee.com/honghuangdc/soybean-admin/badge/star.svg)](https://gitee.com/honghuangdc/soybean-admin)
[![gitcode star](https://gitcode.com/soybeanjs/soybean-admin/star/badge.svg)](https://gitcode.com/soybeanjs/soybean-admin)
[![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="FeaturedHelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<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://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="FeaturedHelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
</div>
> [!NOTE]
> If you think `SoybeanAdmin` is helpful to you, or you like our project, please give us a ⭐️ on GitHub. Your support is the driving force for us to continue to improve and add new features! Thank you for your support!
@@ -139,7 +138,7 @@ Refer to the [Code Synchronization](https://docs.soybeanjs.cn/guide/sync) docume
## 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.
- [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).
@@ -151,8 +150,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.
- [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
@@ -182,21 +179,13 @@ Thanks the following people for their contributions. If you want to contribute t
<img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" />
</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
`SoybeanAdmin` is a completely open source and free project, helping developers to develop medium and large-scale management systems more conveniently. It also provides WeChat and QQ communication groups. If you have any questions, please feel free to ask in the group.
<div>
<p>QQ Group</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-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>
<p>WeChat Group</p>

View File

@@ -11,11 +11,9 @@
[![github forks](https://img.shields.io/github/forks/honghuangdc/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
[![gitee stars](https://gitee.com/honghuangdc/soybean-admin/badge/star.svg)](https://gitee.com/honghuangdc/soybean-admin)
[![gitcode star](https://gitcode.com/soybeanjs/soybean-admin/star/badge.svg)](https://gitcode.com/soybeanjs/soybean-admin)
[![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
<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://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="FeaturedHelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
</div>
<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="FeaturedHelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
> [!NOTE]
> 如果您觉得 `SoybeanAdmin`对您有所帮助,或者您喜欢我们的项目,请在 GitHub 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持!
@@ -165,7 +163,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。
- [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) 开发的简单权限系统。
@@ -177,8 +175,6 @@ pnpm build
- [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分支,适配动态路由,接口鉴权限。
更多周边生态请翻阅 [周边生态](https://docs.soybeanjs.cn/zh/awesome) 文档。
## 如何贡献
@@ -210,21 +206,13 @@ pnpm build
<img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" />
</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 交流群,使用问题欢迎在群内提问。
<div>
<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>
<p>微信群</p>

View File

@@ -1,4 +1,4 @@
import type { ProxyOptions } from 'vite';
import type { HttpProxy, ProxyOptions } from 'vite';
import { bgRed, bgYellow, green, lightBlue } from 'kolorist';
import { consola } from 'consola';
import { createServiceConfig } from '../../src/utils/service';
@@ -33,7 +33,7 @@ function createProxyItem(item: App.Service.ServiceConfigItem, enableLog: boolean
proxy[item.proxyPattern] = {
target: item.baseURL,
changeOrigin: true,
configure: (_proxy, options) => {
configure: (_proxy: HttpProxy.Server, options: ProxyOptions) => {
_proxy.on('proxyReq', (_proxyReq, req, _res) => {
if (!enableLog) return;

View File

@@ -1,7 +1,7 @@
{
"name": "soybean-admin",
"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的清新优雅的中后台模版。",
"author": {
"name": "Soybean",
@@ -54,53 +54,53 @@
"@sa/hooks": "workspace:*",
"@sa/materials": "workspace:*",
"@sa/utils": "workspace:*",
"@vueuse/core": "14.1.0",
"@vueuse/core": "13.5.0",
"clipboard": "2.0.11",
"dayjs": "1.11.19",
"dayjs": "1.11.13",
"defu": "6.1.4",
"echarts": "6.0.0",
"echarts": "5.6.0",
"json5": "2.2.3",
"naive-ui": "2.43.2",
"naive-ui": "2.42.0",
"nprogress": "0.2.0",
"pinia": "3.0.4",
"tailwind-merge": "3.4.0",
"vue": "3.5.25",
"pinia": "3.0.3",
"tailwind-merge": "3.3.1",
"vue": "3.5.17",
"vue-draggable-plus": "0.6.0",
"vue-i18n": "11.2.2",
"vue-router": "4.6.3"
"vue-i18n": "11.1.10",
"vue-router": "4.5.1"
},
"devDependencies": {
"@elegant-router/vue": "0.3.8",
"@iconify/json": "2.2.414",
"@iconify/json": "2.2.359",
"@sa/scripts": "workspace:*",
"@sa/uno-preset": "workspace:*",
"@soybeanjs/eslint-config": "1.7.4",
"@types/node": "24.10.1",
"@soybeanjs/eslint-config": "1.7.1",
"@types/node": "24.0.15",
"@types/nprogress": "0.2.3",
"@unocss/eslint-config": "66.5.10",
"@unocss/preset-icons": "66.5.10",
"@unocss/preset-uno": "66.5.10",
"@unocss/transformer-directives": "66.5.10",
"@unocss/transformer-variant-group": "66.5.10",
"@unocss/vite": "66.5.10",
"@vitejs/plugin-vue": "6.0.2",
"@vitejs/plugin-vue-jsx": "5.1.2",
"@unocss/eslint-config": "66.3.3",
"@unocss/preset-icons": "66.3.3",
"@unocss/preset-uno": "66.3.3",
"@unocss/transformer-directives": "66.3.3",
"@unocss/transformer-variant-group": "66.3.3",
"@unocss/vite": "66.3.3",
"@vitejs/plugin-vue": "6.0.0",
"@vitejs/plugin-vue-jsx": "5.0.1",
"consola": "3.4.2",
"eslint": "9.39.1",
"eslint-plugin-vue": "10.6.2",
"eslint": "9.31.0",
"eslint-plugin-vue": "10.3.0",
"kolorist": "1.8.0",
"sass": "1.94.2",
"simple-git-hooks": "2.13.1",
"tsx": "4.21.0",
"typescript": "5.9.3",
"unplugin-icons": "22.5.0",
"unplugin-vue-components": "30.0.0",
"vite": "7.2.6",
"sass": "1.89.2",
"simple-git-hooks": "2.13.0",
"tsx": "4.20.3",
"typescript": "5.8.3",
"unplugin-icons": "22.1.0",
"unplugin-vue-components": "28.8.0",
"vite": "7.0.5",
"vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "8.0.5",
"vite-plugin-vue-devtools": "7.7.7",
"vue-eslint-parser": "10.2.0",
"vue-tsc": "3.1.5"
"vue-tsc": "3.0.3"
},
"simple-git-hooks": {
"commit-msg": "pnpm sa git-commit-verify",

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/alova",
"version": "2.0.1",
"version": "1.3.15",
"exports": {
".": "./src/index.ts",
"./fetch": "./src/fetch.ts",
@@ -15,6 +15,6 @@
"dependencies": {
"@alova/mock": "2.0.17",
"@sa/utils": "workspace:*",
"alova": "3.4.0"
"alova": "3.3.4"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/axios",
"version": "2.0.1",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},
@@ -11,7 +11,7 @@
},
"dependencies": {
"@sa/utils": "workspace:*",
"axios": "1.13.2",
"axios": "1.10.0",
"axios-retry": "4.5.0",
"qs": "6.14.0"
},

View File

@@ -3,7 +3,6 @@ import type { AxiosResponse, CreateAxiosDefaults, InternalAxiosRequestConfig } f
import axiosRetry from 'axios-retry';
import { nanoid } from '@sa/utils';
import { createAxiosConfig, createDefaultOptions, createRetryOptions } from './options';
import { transformResponse } from './shared';
import { BACKEND_ERROR_CODE, REQUEST_ID_KEY } from './constant';
import type {
CustomAxiosRequestConfig,
@@ -54,8 +53,6 @@ function createCommonRequest<
async response => {
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
await transformResponse(response);
if (responseType !== 'json' || opts.isBackendSuccess(response)) {
return Promise.resolve(response);
}

View File

@@ -1,5 +1,4 @@
import type { AxiosHeaderValue, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import type { ResponseType } from './type';
export function getContentType(config: InternalAxiosRequestConfig) {
const contentType: AxiosHeaderValue = config.headers?.['Content-Type'] || 'application/json';
@@ -27,53 +26,3 @@ export function isResponseJson(response: AxiosResponse) {
return responseType === 'json' || responseType === undefined;
}
export async function transformResponse(response: AxiosResponse) {
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
if (responseType === 'json') return;
const isJson = response.headers['content-type']?.includes('application/json');
if (!isJson) return;
if (responseType === 'blob') {
await transformBlobToJson(response);
}
if (responseType === 'arrayBuffer') {
await transformArrayBufferToJson(response);
}
}
export async function transformBlobToJson(response: AxiosResponse) {
try {
let data = response.data;
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (Object.prototype.toString.call(data) === '[object Blob]') {
const json = await data.text();
data = JSON.parse(json);
}
response.data = data;
} catch {}
}
export async function transformArrayBufferToJson(response: AxiosResponse) {
try {
let data = response.data;
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (Object.prototype.toString.call(data) === '[object ArrayBuffer]') {
const json = new TextDecoder().decode(data);
data = JSON.parse(json);
}
response.data = data;
} catch {}
}

View File

@@ -119,11 +119,8 @@ export type FlatResponseData<ResponseData, ApiData> =
| FlatResponseSuccessData<ResponseData, ApiData>
| FlatResponseFailData<ResponseData>;
export interface FlatRequestInstance<
ResponseData,
ApiData,
State extends Record<string, unknown>
> extends RequestInstanceCommon<State> {
export interface FlatRequestInstance<ResponseData, ApiData, State extends Record<string, unknown>>
extends RequestInstanceCommon<State> {
<T extends ApiData = ApiData, R extends ResponseType = 'json'>(
config: CustomAxiosRequestConfig<R>
): Promise<FlatResponseData<ResponseData, MappedType<R, T>>>;

View File

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

View File

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

View File

@@ -26,11 +26,8 @@ export type HookRequestInstanceResponseData<ResponseData, ApiData> = {
loading: Ref<boolean>;
} & (HookRequestInstanceResponseSuccessData<ApiData> | HookRequestInstanceResponseFailData<ResponseData>);
export interface HookRequestInstance<
ResponseData,
ApiData,
State extends Record<string, unknown>
> extends RequestInstanceCommon<State> {
export interface HookRequestInstance<ResponseData, ApiData, State extends Record<string, unknown>>
extends RequestInstanceCommon<State> {
<T extends ApiData = ApiData, R extends ResponseType = 'json'>(
config: CustomAxiosRequestConfig
): HookRequestInstanceResponseData<ResponseData, MappedType<R, T>>;

View File

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

View File

@@ -95,27 +95,3 @@
.chrome-tab_dark .chrome-tab-divider {
background-color: rgba(255, 255, 255, 0.9);
}
.slider-tab {
background-color: transparent;
height: 100%;
border-bottom: 2px solid transparent;
}
.slider-tab_dark {
background-color: transparent;
}
.slider-tab:hover {
color: var(--soy-primary-color);
}
.slider-tab_active {
color: var(--soy-primary-color);
background-color: var(--soy-primary-color-opacity1);
border-bottom-color: var(--soy-primary-color);
}
.slider-tab_active_dark {
background-color: var(--soy-primary-color-opacity2);
}

View File

@@ -10,10 +10,6 @@ declare const styles: {
readonly 'chrome-tab_dark': string;
readonly 'chrome-tab-divider': string;
readonly 'svg-close': string;
readonly 'slider-tab': string;
readonly 'slider-tab_active': string;
readonly 'slider-tab_active_dark': string;
readonly 'slider-tab_dark': string;
};
export default styles;

View File

@@ -5,7 +5,6 @@ import type { PageTabMode, PageTabProps } from '../../types';
import { ACTIVE_COLOR, createTabCssVars } from './shared';
import ChromeTab from './chrome-tab.vue';
import ButtonTab from './button-tab.vue';
import SliderTab from './slider-tab.vue';
import SvgClose from './svg-close.vue';
import style from './index.module.css';
@@ -27,7 +26,7 @@ interface Emits {
const emit = defineEmits<Emits>();
const activeTabComponent = computed(() => {
const { mode, chromeClass, buttonClass, sliderClass } = props;
const { mode, chromeClass, buttonClass } = props;
const tabComponentMap = {
chrome: {
@@ -37,10 +36,6 @@ const activeTabComponent = computed(() => {
button: {
component: ButtonTab,
class: buttonClass
},
slider: {
component: SliderTab,
class: sliderClass
}
} satisfies Record<PageTabMode, { component: Component; class?: string }>;
@@ -50,7 +45,7 @@ const activeTabComponent = computed(() => {
const cssVars = computed(() => createTabCssVars(props.activeColor));
const bindProps = computed(() => {
const { chromeClass: _chromeCls, buttonClass: _btnCls, sliderClass: _sliderCls, ...rest } = props;
const { chromeClass: _chromeCls, buttonClass: _btnCls, ...rest } = props;
return rest;
});

View File

@@ -1,53 +0,0 @@
<script setup lang="ts">
import type { PageTabProps } from '../../types';
import style from './index.module.css';
defineOptions({
name: 'SliderTab'
});
defineProps<PageTabProps>();
type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = {
/**
* Slot
*
* The center content of the tab
*/
default?: SlotFn;
/**
* Slot
*
* The left content of the tab
*/
prefix?: SlotFn;
/**
* Slot
*
* The right content of the tab
*/
suffix?: SlotFn;
};
defineSlots<Slots>();
</script>
<template>
<div
class=":soy: relative inline-flex cursor-pointer items-center justify-center gap-6px whitespace-nowrap px-12px py-4px"
:class="[
style['slider-tab'],
{ [style['slider-tab_dark']]: darkMode },
{ [style['slider-tab_active']]: active },
{ [style['slider-tab_active_dark']]: active && darkMode }
]"
>
<slot name="prefix"></slot>
<slot></slot>
<slot name="suffix"></slot>
</div>
</template>
<style scoped></style>

View File

@@ -146,8 +146,7 @@ export type LayoutScrollMode = 'wrapper' | 'content';
/** Admin layout props */
export interface AdminLayoutProps
extends
AdminLayoutHeaderConfig,
extends AdminLayoutHeaderConfig,
AdminLayoutTabConfig,
AdminLayoutSiderConfig,
AdminLayoutContentConfig,
@@ -240,7 +239,7 @@ export type LayoutCssVars = {
*
* @default chrome
*/
export type PageTabMode = 'button' | 'chrome' | 'slider';
export type PageTabMode = 'button' | 'chrome';
export interface PageTabProps {
/** Whether is dark mode */
@@ -263,8 +262,6 @@ export interface PageTabProps {
buttonClass?: string;
/** The class of the chrome tab */
chromeClass?: string;
/** The class of the title tab */
sliderClass?: string;
/** Whether the tab is active */
active?: boolean;
/** The color of the active tab */

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/scripts",
"version": "2.0.1",
"version": "1.3.15",
"bin": {
"sa": "./bin.ts"
},
@@ -13,16 +13,15 @@
}
},
"devDependencies": {
"@soybeanjs/changelog": "0.3.25",
"bumpp": "10.3.2",
"c12": "3.3.2",
"@soybeanjs/changelog": "0.3.24",
"bumpp": "10.2.0",
"c12": "3.1.0",
"cac": "6.7.14",
"consola": "3.4.2",
"enquirer": "2.4.1",
"execa": "9.6.1",
"execa": "9.6.0",
"kolorist": "1.8.0",
"npm-check-updates": "19.1.2",
"picomatch": "4.0.3",
"rimraf": "6.1.2"
"npm-check-updates": "18.0.1",
"rimraf": "6.0.1"
}
}

View File

@@ -1,5 +1,5 @@
import { execCommand } from '../shared';
export async function updatePkg(args: string[] = ['--deep', '-u']) {
execCommand('npx', ['npm-check-updates', ...args], { stdio: 'inherit' });
execCommand('npx', ['ncu', ...args], { stdio: 'inherit' });
}

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/utils",
"version": "2.0.1",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},
@@ -14,7 +14,7 @@
"crypto-js": "4.2.0",
"klona": "2.0.6",
"localforage": "1.10.0",
"nanoid": "5.1.6"
"nanoid": "5.1.5"
},
"devDependencies": {
"@types/crypto-js": "4.2.2"

3541
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +0,0 @@
<script lang="ts" setup>
import { computed, useSlots } from 'vue';
import type { PopoverPlacement } from 'naive-ui';
defineOptions({ name: 'IconTooltip' });
interface Props {
icon?: string;
localIcon?: string;
desc?: string;
placement?: PopoverPlacement;
}
const props = withDefaults(defineProps<Props>(), {
icon: 'mdi-help-circle',
localIcon: '',
desc: '',
placement: 'top'
});
const slots = useSlots();
const hasCustomTrigger = computed(() => Boolean(slots.trigger));
if (!hasCustomTrigger.value && !props.icon && !props.localIcon) {
throw new Error('icon or localIcon is required when no custom trigger slot is provided');
}
</script>
<template>
<NTooltip :placement="placement">
<template #trigger>
<slot name="trigger">
<div class="cursor-pointer">
<SvgIcon :icon="icon" :local-icon="localIcon" />
</div>
</slot>
</template>
<slot>
<span>{{ desc }}</span>
</slot>
</NTooltip>
</template>

View File

@@ -40,8 +40,7 @@ export const themeScrollModeOptions = transformRecordToOption(themeScrollModeRec
export const themeTabModeRecord: Record<UnionKey.ThemeTabMode, App.I18n.I18nKey> = {
chrome: 'theme.layout.tab.mode.chrome',
button: 'theme.layout.tab.mode.button',
slider: 'theme.layout.tab.mode.slider'
button: 'theme.layout.tab.mode.button'
};
export const themeTabModeOptions = transformRecordToOption(themeTabModeRecord);
@@ -58,6 +57,13 @@ export const themePageAnimationModeRecord: Record<UnionKey.ThemePageAnimateMode,
export const themePageAnimationModeOptions = transformRecordToOption(themePageAnimationModeRecord);
export const resetCacheStrategyRecord: Record<UnionKey.ResetCacheStrategy, App.I18n.I18nKey> = {
close: 'theme.layout.resetCacheStrategy.close',
refresh: 'theme.layout.resetCacheStrategy.refresh'
};
export const resetCacheStrategyOptions = transformRecordToOption(resetCacheStrategyRecord);
export const DARK_CLASS = 'dark';
export const watermarkTimeFormatOptions = [

View File

@@ -38,13 +38,6 @@ export function useNaiveTable<ResponseData, ApiData>(options: UseNaiveTableOptio
getColumns
});
// calculate the total width of the table this is used for horizontal scrolling
const scrollX = computed(() => {
return result.columns.value.reduce((acc, column) => {
return acc + Number(column.width ?? column.minWidth ?? 120);
}, 0);
});
scope.run(() => {
watch(
() => appStore.locale,
@@ -58,10 +51,7 @@ export function useNaiveTable<ResponseData, ApiData>(options: UseNaiveTableOptio
scope.stop();
});
return {
...result,
scrollX
};
return result;
}
type PaginationParams = Pick<PaginationProps, 'page' | 'pageSize'>;

View File

@@ -18,7 +18,7 @@ defineOptions({
const appStore = useAppStore();
const themeStore = useThemeStore();
const { secondLevelMenus, childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext();
const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext();
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 siderWidth = computed(() => getSiderAndCollapsedWidth(false));
const siderWidth = computed(() => getSiderWidth());
const siderCollapsedWidth = computed(() => getSiderAndCollapsedWidth(true));
const siderCollapsedWidth = computed(() => getSiderCollapsedWidth());
function getSiderAndCollapsedWidth(isCollapsed: boolean) {
const {
@@ -104,7 +104,7 @@ function getSiderAndCollapsedWidth(isCollapsed: boolean) {
const isMixMode = isVerticalMix.value || isTopHybridSidebarFirst.value || isVerticalHybridHeaderFirst.value;
let finalWidth = isMixMode ? mixWidth : width;
if (isVerticalMix.value && appStore.mixSiderFixed && secondLevelMenus.value.length) {
if (isVerticalMix.value && appStore.mixSiderFixed && childLevelMenus.value.length) {
finalWidth += mixChildMenuWidth;
}
@@ -114,6 +114,14 @@ function getSiderAndCollapsedWidth(isCollapsed: boolean) {
return finalWidth;
}
function getSiderWidth() {
return getSiderAndCollapsedWidth(false);
}
function getSiderCollapsedWidth() {
return getSiderAndCollapsedWidth(true);
}
</script>
<template>

View File

@@ -3,7 +3,6 @@ import { useRoute } from 'vue-router';
import { useContext } from '@sa/hooks';
import type { RouteKey } from '@elegant-router/types';
import { useRouteStore } from '@/store/modules/route';
import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router';
export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu', useMixMenu);
@@ -11,7 +10,6 @@ export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu',
function useMixMenu() {
const route = useRoute();
const routeStore = useRouteStore();
const themeStore = useThemeStore();
const { selectedKey } = useMenu();
const { routerPushByKeyWithMetaQuery } = useRouterPush();
@@ -102,46 +100,10 @@ function useMixMenu() {
() => 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(
() => route.name,
() => {
getActiveFirstLevelMenuKey();
// if there are child level menus, get the active second level menu key
if (hasChildLevelMenus.value) {
getActiveSecondLevelMenuKey();
}
},
{ immediate: true }
);
@@ -159,10 +121,7 @@ function useMixMenu() {
isActiveSecondLevelMenuHasChildren,
handleSelectSecondLevelMenu,
getActiveSecondLevelMenuKey,
childLevelMenus,
hasChildLevelMenus,
getDeepestLevelMenuKey,
activeDeepestLevelMenuKey
childLevelMenus
};
}

View File

@@ -2,7 +2,6 @@
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
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 { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
@@ -19,28 +18,12 @@ const appStore = useAppStore();
const themeStore = useThemeStore();
const routeStore = useRouteStore();
const { routerPushByKeyWithMetaQuery } = useRouterPush();
const {
firstLevelMenus,
secondLevelMenus,
activeFirstLevelMenuKey,
handleSelectFirstLevelMenu,
activeDeepestLevelMenuKey
} = useMixMenuContext('TopHybridHeaderFirst');
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
useMixMenuContext('TopHybridHeaderFirst');
const { selectedKey } = useMenu();
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() {
if (appStore.siderCollapse || !selectedKey.value) {
expandedKeys.value = [];
@@ -66,7 +49,7 @@ watch(
:options="firstLevelMenus"
:indent="18"
responsive
@update:value="handleSelectMenu"
@update:value="handleSelectFirstLevelMenu"
/>
</Teleport>
<Teleport :to="`#${GLOBAL_SIDER_MENU_ID}`">

View File

@@ -1,5 +1,4 @@
<script setup lang="ts">
import type { RouteKey } from '@elegant-router/types';
import { GLOBAL_HEADER_MENU_ID, GLOBAL_SIDER_MENU_ID } from '@/constants/app';
import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
@@ -14,25 +13,9 @@ defineOptions({
const appStore = useAppStore();
const themeStore = useThemeStore();
const { routerPushByKeyWithMetaQuery } = useRouterPush();
const {
firstLevelMenus,
secondLevelMenus,
activeFirstLevelMenuKey,
handleSelectFirstLevelMenu,
activeDeepestLevelMenuKey
} = useMixMenuContext('TopHybridSidebarFirst');
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
useMixMenuContext('TopHybridSidebarFirst');
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>
<template>
@@ -47,17 +30,15 @@ function handleSelectMenu(key: RouteKey) {
/>
</Teleport>
<Teleport :to="`#${GLOBAL_SIDER_MENU_ID}`">
<div class="h-full pt-2">
<FirstLevelMenu
:menus="firstLevelMenus"
:active-menu-key="activeFirstLevelMenuKey"
:sider-collapse="appStore.siderCollapse"
:dark-mode="themeStore.darkMode"
:theme-color="themeStore.themeColor"
@select="handleSelectMenu"
@toggle-sider-collapse="appStore.toggleSiderCollapse"
/>
</div>
<FirstLevelMenu
:menus="firstLevelMenus"
:active-menu-key="activeFirstLevelMenuKey"
:sider-collapse="appStore.siderCollapse"
:dark-mode="themeStore.darkMode"
:theme-color="themeStore.themeColor"
@select="handleSelectFirstLevelMenu"
@toggle-sider-collapse="appStore.toggleSiderCollapse"
/>
</Teleport>
</template>

View File

@@ -33,15 +33,15 @@ const {
isActiveSecondLevelMenuHasChildren,
handleSelectSecondLevelMenu,
getActiveSecondLevelMenuKey,
childLevelMenus,
hasChildLevelMenus,
activeDeepestLevelMenuKey
childLevelMenus
} = useMixMenuContext('VerticalHybridHeaderFirst');
const { selectedKey } = useMenu();
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) {
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) {
handleSelectFirstLevelMenu(key);
if (secondLevelMenus.value.length === 0) return;
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;
if (secondLevelMenus.value.length > 0) {
handleSelectMixMenu(secondLevelMenus.value[0].routeKey);
}
// Case 2: autoSelectFirstMenu enabled - navigate to deepest menu
activeDeepestLevelMenuKey();
setDrawerVisible(false);
}
function handleResetActiveMenu() {
@@ -135,9 +114,7 @@ watch(
</FirstLevelMenu>
<div
class="relative h-full transition-width-300"
:style="{
width: appStore.mixSiderFixed && hasChildLevelMenus ? themeStore.sider.mixChildMenuWidth + 'px' : '0px'
}"
:style="{ width: appStore.mixSiderFixed && hasChildMenus ? themeStore.sider.mixChildMenuWidth + 'px' : '0px' }"
>
<DarkModeContainer
class="absolute-lt h-full flex-col-stretch nowrap-hidden shadow-sm transition-all-300"

View File

@@ -26,7 +26,7 @@ const props = withDefaults(defineProps<Props>(), {
const visible = defineModel<boolean>('visible');
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs, fixTab, unfixTab, isTabRetain } = useTabStore();
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs } = useTabStore();
const { SvgIconVNode } = useSvgIcon();
type DropdownOption = {
@@ -64,23 +64,6 @@ const options = computed(() => {
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 result = opts.filter(opt => !excludeKeys.includes(opt.key));
@@ -115,12 +98,6 @@ const dropdownAction: Record<App.Global.DropdownKey, () => void> = {
},
closeAll() {
clearTabs();
},
pin() {
fixTab(props.tabId);
},
unpin() {
unfixTab(props.tabId);
}
};

View File

@@ -26,8 +26,6 @@ const tabRef = ref<HTMLElement>();
const isPCFlag = isPC();
const TAB_DATA_ID = 'data-tab-id';
const MIDDLE_MOUSE_BUTTON = 1;
const RIGHT_MOUSE_BUTTON = 2;
type TabNamedNodeMap = NamedNodeMap & {
[TAB_DATA_ID]: Attr;
@@ -86,26 +84,6 @@ function handleCloseTab(tab: App.Global.Tab) {
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() {
appStore.reloadPage(500);
}
@@ -191,9 +169,7 @@ init();
<div
ref="tabRef"
class="h-full flex pr-18px"
:class="[
themeStore.tab.mode === 'chrome' || themeStore.tab.mode === 'slider' ? 'items-end' : 'items-center gap-12px'
]"
:class="[themeStore.tab.mode === 'chrome' ? 'items-end' : 'items-center gap-12px']"
>
<PageTab
v-for="tab in tabStore.tabs"
@@ -204,8 +180,7 @@ init();
:active="tab.id === tabStore.activeTabId"
:active-color="themeStore.themeColor"
:closable="!tabStore.isTabRetain(tab.id)"
@pointerdown="switchTab($event, tab)"
@mousedown="handleMousedown($event, tab)"
@pointerdown="tabStore.switchRouteByTab(tab)"
@close="handleCloseTab(tab)"
@contextmenu="handleContextMenu($event, tab.id)"
>

View File

@@ -80,7 +80,7 @@ function handleChangeMode(mode: UnionKey.ThemeLayoutMode) {
class="flex-col-center cursor-pointer"
@click="handleChangeMode(key)"
>
<IconTooltip :placement="item.placement">
<NTooltip :placement="item.placement">
<template #trigger>
<div
class="h-64px w-96px gap-6px rd-4px p-6px shadow ring-2 ring-transparent transition-all hover:ring-primary"
@@ -92,7 +92,7 @@ function handleChangeMode(mode: UnionKey.ThemeLayoutMode) {
</div>
</template>
{{ $t(`theme.layout.layoutMode.${key}_detail`) }}
</IconTooltip>
</NTooltip>
<p class="mt-8px text-12px">{{ $t(themeLayoutModeRecord[key]) }}</p>
</div>
</div>

View File

@@ -13,7 +13,7 @@ defineProps<Props>();
<template>
<div class="w-full flex-y-center justify-between">
<div class="flex-y-center">
<div>
<span class="pr-8px text-base-text">{{ label }}</span>
<slot name="suffix"></slot>
</div>

View File

@@ -6,7 +6,6 @@ import AppearanceSettings from './modules/appearance/index.vue';
import LayoutSettings from './modules/layout/index.vue';
import GeneralSettings from './modules/general/index.vue';
import ConfigOperation from './modules/config-operation.vue';
import PresetSettings from './modules/preset/index.vue';
defineOptions({
name: 'ThemeDrawer'
@@ -34,7 +33,6 @@ const drawerWidth = computed(() => {
<NTab name="appearance" :tab="$t('theme.tabs.appearance')"></NTab>
<NTab name="layout" :tab="$t('theme.tabs.layout')"></NTab>
<NTab name="general" :tab="$t('theme.tabs.general')"></NTab>
<NTab name="preset" :tab="$t('theme.tabs.preset')"></NTab>
</NTabs>
<div class="min-h-400px">
@@ -42,7 +40,6 @@ const drawerWidth = computed(() => {
<AppearanceSettings v-if="activeTab === 'appearance'" />
<LayoutSettings v-else-if="activeTab === 'layout'" />
<GeneralSettings v-else-if="activeTab === 'general'" />
<PresetSettings v-else-if="activeTab === 'preset'" />
</KeepAlive>
</div>

View File

@@ -1,7 +1,6 @@
<script setup lang="ts">
import ThemeSchema from './modules/theme-schema.vue';
import ThemeColor from './modules/theme-color.vue';
import ThemeRadius from './modules/theme-radius.vue';
defineOptions({
name: 'AppearanceSettings'
@@ -12,7 +11,6 @@ defineOptions({
<div class="flex-col-stretch gap-16px">
<ThemeSchema />
<ThemeColor />
<ThemeRadius />
</div>
</template>

View File

@@ -36,28 +36,27 @@ const swatches: string[] = [
<template>
<NDivider>{{ $t('theme.appearance.themeColor.title') }}</NDivider>
<div class="flex-col-stretch gap-12px">
<SettingItem key="recommend-color" :label="$t('theme.appearance.recommendColor')">
<template #suffix>
<IconTooltip>
<p>
<span class="pr-12px">{{ $t('theme.appearance.recommendColorDesc') }}</span>
<br />
<NButton
text
tag="a"
href="https://uicolors.app/create"
target="_blank"
rel="noopener noreferrer"
class="text-gray"
>
https://uicolors.app/create
</NButton>
</p>
</IconTooltip>
<NTooltip placement="top-start">
<template #trigger>
<SettingItem key="recommend-color" :label="$t('theme.appearance.recommendColor')">
<NSwitch v-model:value="themeStore.recommendColor" />
</SettingItem>
</template>
<NSwitch v-model:value="themeStore.recommendColor" />
</SettingItem>
<p>
<span class="pr-12px">{{ $t('theme.appearance.recommendColorDesc') }}</span>
<br />
<NButton
text
tag="a"
href="https://uicolors.app/create"
target="_blank"
rel="noopener noreferrer"
class="text-gray"
>
https://uicolors.app/create
</NButton>
</p>
</NTooltip>
<SettingItem
v-for="(_, key) in themeStore.themeColors"
:key="key"

View File

@@ -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>

View File

@@ -19,9 +19,6 @@ const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wra
<NDivider>{{ $t('theme.layout.content.title') }}</NDivider>
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
<SettingItem key="1" :label="$t('theme.layout.content.scrollMode.title')">
<template #suffix>
<IconTooltip :desc="$t('theme.layout.content.scrollMode.tip')" />
</template>
<NSelect
v-model:value="themeStore.layout.scrollMode"
:options="translateOptions(themeScrollModeOptions)"

View File

@@ -12,7 +12,6 @@ const themeStore = useThemeStore();
const layoutMode = computed(() => themeStore.layout.mode);
const isMixLayoutMode = computed(() => layoutMode.value.includes('mix') || layoutMode.value.includes('hybrid'));
const isHybridLayoutMode = computed(() => layoutMode.value.includes('hybrid'));
</script>
<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')">
<NInputNumber v-model:value="themeStore.sider.mixChildMenuWidth" size="small" :step="1" class="w-120px" />
</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>
</template>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { themeTabModeOptions } from '@/constants/app';
import { resetCacheStrategyOptions, themeTabModeOptions } from '@/constants/app';
import { useThemeStore } from '@/store/modules/theme';
import { translateOptions } from '@/utils/common';
import { $t } from '@/locales';
@@ -15,13 +15,18 @@ const themeStore = useThemeStore();
<template>
<NDivider>{{ $t('theme.layout.tab.title') }}</NDivider>
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
<SettingItem key="0" :label="$t('theme.layout.resetCacheStrategy.title')">
<NSelect
v-model:value="themeStore.resetCacheStrategy"
:options="translateOptions(resetCacheStrategyOptions)"
size="small"
class="w-120px"
/>
</SettingItem>
<SettingItem key="1" :label="$t('theme.layout.tab.visible')">
<NSwitch v-model:value="themeStore.tab.visible" />
</SettingItem>
<SettingItem v-if="themeStore.tab.visible" key="2" :label="$t('theme.layout.tab.cache')">
<template #suffix>
<IconTooltip :desc="$t('theme.layout.tab.cacheTip')" />
</template>
<NSwitch v-model:value="themeStore.tab.cache" />
</SettingItem>
<SettingItem v-if="themeStore.tab.visible" key="3" :label="$t('theme.layout.tab.height')">
@@ -35,12 +40,6 @@ const themeStore = useThemeStore();
class="w-120px"
/>
</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>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
import ThemePreset from './modules/theme-preset.vue';
defineOptions({
name: 'PresetSettings'
});
</script>
<template>
<div class="flex-col-stretch gap-16px">
<ThemePreset />
</div>
</template>
<style scoped></style>

View File

@@ -1,157 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue';
import { defu } from 'defu';
import { useThemeStore } from '@/store/modules/theme';
import { themeSettings } from '@/theme/settings';
import { $t } from '@/locales';
defineOptions({
name: 'ThemePreset'
});
type ThemePreset = Pick<
App.Theme.ThemeSetting,
| 'themeScheme'
| 'grayscale'
| 'colourWeakness'
| 'recommendColor'
| 'themeColor'
| 'themeRadius'
| 'otherColor'
| 'isInfoFollowPrimary'
| 'layout'
| 'page'
| 'header'
| 'tab'
| 'fixedHeaderAndTab'
| 'sider'
| 'footer'
| 'watermark'
| 'tokens'
> & {
name: string;
desc: string;
i18nkey?: string;
version: string;
/** Optional NaiveUI theme overrides */
naiveui?: App.Theme.NaiveUIThemeOverride;
};
const presetModules = import.meta.glob('@/theme/preset/*.json', { eager: true, import: 'default' });
const themeStore = useThemeStore();
// Extract preset data
const presets = computed(() =>
Object.entries(presetModules)
.map(([path, presetData]) => {
const fileName = path.split('/').pop()?.replace('.json', '') || '';
return {
id: fileName,
...(presetData as ThemePreset)
};
})
.sort((a, b) => {
if (a.name === 'default') return -1;
if (b.name === 'default') return 1;
return a.name.localeCompare(b.name);
})
);
const getPresetName = (preset: ThemePreset): string => {
if (!preset.i18nkey) return preset.name;
try {
const key = `${preset.i18nkey}.name` as App.I18n.I18nKey;
const translated = $t(key);
return translated !== key ? translated : preset.name;
} catch {
return preset.name;
}
};
const getPresetDesc = (preset: ThemePreset): string => {
if (!preset.i18nkey) return preset.desc;
try {
const key = `${preset.i18nkey}.desc` as App.I18n.I18nKey;
const translated = $t(key);
return translated !== key ? translated : preset.desc;
} catch {
return preset.desc;
}
};
const applyPreset = (preset: ThemePreset): void => {
const mergedPreset = defu(preset, themeSettings);
const { themeScheme, grayscale, colourWeakness, layout, watermark, naiveui, ...rest } = mergedPreset;
themeStore.setThemeScheme(themeScheme);
themeStore.setGrayscale(grayscale);
themeStore.setColourWeakness(colourWeakness);
themeStore.setThemeLayout(layout.mode);
themeStore.setWatermarkEnableUserName(watermark.enableUserName);
themeStore.setWatermarkEnableTime(watermark.enableTime);
Object.assign(themeStore, {
...rest,
layout: { ...themeStore.layout, scrollMode: layout.scrollMode },
page: { ...rest.page },
header: { ...rest.header },
tab: { ...rest.tab },
sider: { ...rest.sider },
footer: { ...rest.footer },
watermark: { ...watermark },
tokens: { ...rest.tokens }
});
// Apply NaiveUI theme overrides if present
themeStore.setNaiveThemeOverrides(naiveui);
window.$message?.success($t('theme.appearance.preset.applySuccess'));
};
</script>
<template>
<NDivider>{{ $t('theme.appearance.preset.title') }}</NDivider>
<div class="flex flex-col gap-3">
<div
v-for="preset in presets"
:key="preset.id"
class="border border-primary/10 rounded-lg border-solid bg-white/5 p-3 backdrop-blur-10 transition-all duration-300 hover:(shadow-md -translate-y-0.5)"
>
<div class="mb-2 flex items-center justify-between">
<div class="min-w-0 w-full flex flex-1 items-center justify-between gap-2">
<h5 class="m-0 truncate text-sm text-primary font-600">
{{ getPresetName(preset) }}
</h5>
<NBadge :value="`v${preset.version}`" type="info" size="small" class="flex-shrink-0 opacity-80" />
</div>
<NButton type="primary" size="tiny" ghost round class="ml-2 flex-shrink-0" @click="applyPreset(preset)">
{{ $t('theme.appearance.preset.apply') }}
</NButton>
</div>
<p class="line-clamp-2 mb-3 text-xs text-gray-500 leading-4">{{ getPresetDesc(preset) }}</p>
<div class="flex items-center justify-between">
<div class="flex gap-1">
<div
v-for="(color, key) in { primary: preset.themeColor, ...preset.otherColor }"
:key="key"
class="h-3 w-3 cursor-pointer border border-white/30 rounded-full transition-transform hover:scale-110"
:style="{ backgroundColor: color }"
:class="{ 'ring-1 ring-primary/50': key === 'primary' }"
:title="key"
/>
</div>
<div class="flex items-center gap-1">
<div class="text-lg">
{{ preset.themeScheme === 'dark' ? '🌙' : '☀️' }}
</div>
<div class="text-lg">
{{ preset.grayscale ? '🎨' : '' }}
</div>
</div>
</div>
</div>
</div>
</template>

View File

@@ -62,8 +62,7 @@ const local: App.I18n.Schema = {
tabs: {
appearance: 'Appearance',
layout: 'Layout',
general: 'General',
preset: 'Preset'
general: 'General'
},
appearance: {
themeSchema: {
@@ -83,32 +82,8 @@ const local: App.I18n.Schema = {
error: 'Error',
followPrimary: 'Follow Primary'
},
themeRadius: {
title: 'Theme Radius'
},
recommendColor: 'Apply Recommended Color Algorithm',
recommendColorDesc: 'The recommended color algorithm refers to',
preset: {
title: 'Theme Presets',
apply: 'Apply',
applySuccess: 'Preset applied successfully',
default: {
name: 'Default Preset',
desc: 'Default theme preset with balanced settings'
},
dark: {
name: 'Dark Preset',
desc: 'Dark theme preset for night time usage'
},
compact: {
name: 'Compact Preset',
desc: 'Compact layout preset for small screens'
},
azir: {
name: "Azir's Preset",
desc: 'It is a cold and elegant preset that Azir likes'
}
}
recommendColorDesc: 'The recommended color algorithm refers to'
},
layout: {
layoutMode: {
@@ -134,16 +109,12 @@ const local: App.I18n.Schema = {
title: 'Tab Settings',
visible: 'Tab Visible',
cache: 'Tag Bar Info Cache',
cacheTip: 'One-click to open/close global keepalive',
height: 'Tab Height',
mode: {
title: 'Tab Mode',
slider: 'Slider',
chrome: 'Chrome',
button: 'Button'
},
closeByMiddleClick: 'Close Tab by Middle Click',
closeByMiddleClickTip: 'Enable closing tabs by clicking with the middle mouse button'
}
},
header: {
title: 'Header Settings',
@@ -160,10 +131,7 @@ const local: App.I18n.Schema = {
collapsedWidth: 'Sider Collapsed Width',
mixWidth: 'Mix Sider Width',
mixCollapsedWidth: 'Mix Sider Collapse 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'
mixChildMenuWidth: 'Mix Child Menu Width'
},
footer: {
title: 'Footer Settings',
@@ -176,7 +144,6 @@ const local: App.I18n.Schema = {
title: 'Content Area Settings',
scrollMode: {
title: 'Scroll Mode',
tip: 'The theme scroll only scrolls the main part, the outer scroll can carry the header and footer together',
wrapper: 'Wrapper',
content: 'Content'
},
@@ -194,6 +161,11 @@ const local: App.I18n.Schema = {
}
},
fixedHeaderAndTab: 'Fixed Header And Tab'
},
resetCacheStrategy: {
title: 'Reset Cache Strategy',
close: 'Close Page',
refresh: 'Refresh Page'
}
},
general: {
@@ -339,9 +311,7 @@ const local: App.I18n.Schema = {
closeOther: 'Close Other',
closeLeft: 'Close Left',
closeRight: 'Close Right',
closeAll: 'Close All',
pin: 'Pin Tab',
unpin: 'Unpin Tab'
closeAll: 'Close All'
},
icon: {
themeConfig: 'Theme Configuration',

View File

@@ -62,8 +62,7 @@ const local: App.I18n.Schema = {
tabs: {
appearance: '外观',
layout: '布局',
general: '通用',
preset: '预设'
general: '通用'
},
appearance: {
themeSchema: {
@@ -83,32 +82,8 @@ const local: App.I18n.Schema = {
error: '错误色',
followPrimary: '跟随主色'
},
themeRadius: {
title: '主题圆角'
},
recommendColor: '应用推荐算法的颜色',
recommendColorDesc: '推荐颜色的算法参照',
preset: {
title: '主题预设',
apply: '应用',
applySuccess: '预设应用成功',
default: {
name: '默认预设',
desc: 'Soybean 默认主题预设'
},
dark: {
name: '暗色预设',
desc: '适用于夜间使用的暗色主题预设'
},
compact: {
name: '紧凑型',
desc: '适用于小屏幕的紧凑布局预设'
},
azir: {
name: 'Azir的预设',
desc: '是 Azir 比较喜欢的莫兰迪色系冷淡风'
}
}
recommendColorDesc: '推荐颜色的算法参照'
},
layout: {
layoutMode: {
@@ -131,16 +106,12 @@ const local: App.I18n.Schema = {
title: '标签栏设置',
visible: '显示标签栏',
cache: '标签栏信息缓存',
cacheTip: '一键开启/关闭全局 keepalive',
height: '标签栏高度',
mode: {
title: '标签栏风格',
slider: '滑块风格',
chrome: '谷歌风格',
button: '按钮风格'
},
closeByMiddleClick: '鼠标中键关闭标签页',
closeByMiddleClickTip: '启用后可以使用鼠标中键点击标签页进行关闭'
}
},
header: {
title: '头部设置',
@@ -157,22 +128,19 @@ const local: App.I18n.Schema = {
collapsedWidth: '侧边栏折叠宽度',
mixWidth: '混合布局侧边栏宽度',
mixCollapsedWidth: '混合布局侧边栏折叠宽度',
mixChildMenuWidth: '混合布局子菜单宽度',
autoSelectFirstMenu: '自动选择第一个子菜单',
autoSelectFirstMenuTip: '点击一级菜单时,自动选择并导航到第一个子菜单的最深层级'
mixChildMenuWidth: '混合布局子菜单宽度'
},
footer: {
title: '底部设置',
visible: '显示底部',
fixed: '固定底部',
height: '底部高度',
right: '底部右'
right: '底部右'
},
content: {
title: '内容区域设置',
scrollMode: {
title: '滚动模式',
tip: '主题滚动仅 main 部分滚动,外层滚动可携带头部底部一起滚动',
wrapper: '外层滚动',
content: '主体滚动'
},
@@ -190,6 +158,11 @@ const local: App.I18n.Schema = {
}
},
fixedHeaderAndTab: '固定头部和标签栏'
},
resetCacheStrategy: {
title: '重置缓存策略',
close: '关闭页面',
refresh: '刷新页面'
}
},
general: {
@@ -335,9 +308,7 @@ const local: App.I18n.Schema = {
closeOther: '关闭其它',
closeLeft: '关闭左侧',
closeRight: '关闭右侧',
closeAll: '关闭所有',
pin: '固定标签',
unpin: '取消固定'
closeAll: '关闭所有'
},
icon: {
themeConfig: '主题配置',

View File

@@ -46,7 +46,10 @@ export const useAppStore = defineStore(SetupStoreId.App, () => {
});
setReloadFlag(true);
routeStore.resetRouteCache();
if (themeStore.resetCacheStrategy === 'refresh') {
routeStore.resetRouteCache();
}
}
const locale = ref<App.I18n.LangType>(localStg.get('lang') || 'zh-CN');

View File

@@ -18,7 +18,6 @@ import {
getTabByRoute,
getTabIdByRoute,
isTabInTabs,
reorderFixedTabs,
updateTabByI18nKey,
updateTabsByI18nKey
} from './shared';
@@ -113,8 +112,10 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
await switchRouteByTab(nextTab);
}
// reset route cache
routeStore.resetRouteCache(removedTabRouteKey);
// reset route cache if cache strategy is close
if (themeStore.resetCacheStrategy === 'close') {
routeStore.resetRouteCache(removedTabRouteKey);
}
}
/** remove active tab */
@@ -146,8 +147,10 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
const tabsToRemove = tabs.value.filter(tab => !remainTabIds.includes(tab.id));
const routeKeysToReset: RouteKey[] = [];
for (const tab of tabsToRemove) {
routeKeysToReset.push(tab.routeKey);
if (themeStore.resetCacheStrategy === 'close') {
for (const tab of tabsToRemove) {
routeKeysToReset.push(tab.routeKey);
}
}
const removedTabsIds = tabsToRemove.map(tab => tab.id);
@@ -249,48 +252,6 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
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
*
@@ -371,8 +332,6 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
clearTabs,
clearLeftTabs,
clearRightTabs,
fixTab,
unfixTab,
switchRouteByTab,
setTabLabel,
resetTabLabel,

View File

@@ -198,18 +198,6 @@ export function getFixedTabIds(tabs: App.Global.Tab[]) {
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
*

View File

@@ -24,9 +24,6 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
/** Theme settings */
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 */
const { now: watermarkTime, pause: pauseWatermarkTime, resume: resumeWatermarkTime } = useNow({ controls: true });
@@ -56,7 +53,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
});
/** Naive theme */
const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value, naiveThemeOverrides.value));
const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value.recommendColor));
/**
* 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 */
function updateWatermarkTimer() {
const { watermark } = settings.value;
@@ -296,7 +284,6 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
updateThemeColors,
setThemeLayout,
setWatermarkEnableUserName,
setWatermarkEnableTime,
setNaiveThemeOverrides
setWatermarkEnableTime
};
});

View File

@@ -237,30 +237,23 @@ function getNaiveThemeColors(colors: App.Theme.ThemeColor, recommended = false)
* Get naive theme
*
* @param colors Theme colors
* @param settings Theme settings object
* @param overrides Optional manual overrides from preset
* @param [recommended=false] Use recommended color. Default is `false`
*/
export function getNaiveTheme(
colors: App.Theme.ThemeColor,
settings: App.Theme.ThemeSetting,
overrides?: GlobalThemeOverrides
) {
export function getNaiveTheme(colors: App.Theme.ThemeColor, recommended = false) {
const { primary: colorLoading } = colors;
const theme: GlobalThemeOverrides = {
common: {
...getNaiveThemeColors(colors, settings.recommendColor),
borderRadius: `${settings.themeRadius}px`
...getNaiveThemeColors(colors, recommended),
borderRadius: '6px'
},
LoadingBar: {
colorLoading
},
Tag: {
borderRadius: `${settings.themeRadius}px`
borderRadius: '6px'
}
};
// If there are overrides, merge them with priority
// overrides has higher priority than auto-generated theme
return overrides ? defu(overrides, theme) : theme;
return theme;
}

View File

@@ -6,7 +6,6 @@ html,
body,
#app {
height: 100%;
text-autospace: normal;
}
html {

View File

@@ -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-color: $color transparent;
// Dark theme override
.dark & {
// guide safari use light color scrollbar
color-scheme: dark;
scrollbar-color: $dark-color transparent;
}
&::-webkit-scrollbar-thumb {
background-color: $color;
border-radius: $size;

View File

@@ -1,52 +0,0 @@
{
"name": "Azir's Preset",
"desc": "It is a cold and elegant preset that Azir likes",
"i18nkey": "theme.appearance.preset.azir",
"version": "1.0.1",
"themeScheme": "light",
"recommendColor": true,
"themeColor": "#78a878",
"otherColor": {
"info": "#89b989",
"success": "#99c299",
"warning": "#d4bb9d",
"error": "#c49a9a"
},
"isInfoFollowPrimary": true,
"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)"
}
}
},
"naiveui": {
"Alert": {
"borderRadiusMedium": "12px",
"fontWeightStrong": "600",
"paddingMedium": "0 20px"
},
"Card": {
"borderRadius": "16px",
"paddingMedium": "24px"
},
"Input": {
"borderRadius": "10px"
}
}
}

View File

@@ -1,37 +0,0 @@
{
"name": "Compact Preset",
"desc": "Compact layout preset for small screens",
"i18nkey": "theme.appearance.preset.compact",
"version": "1.0.1",
"themeRadius": 6,
"header": {
"height": 48,
"breadcrumb": {
"visible": false
},
"multilingual": {
"visible": false
},
"globalSearch": {
"visible": false
}
},
"tab": {
"visible": true,
"cache": true,
"height": 36,
"mode": "button",
"closeTabByMiddleClick": false
},
"sider": {
"inverted": false,
"width": 180,
"collapsedWidth": 48,
"mixWidth": 80,
"mixCollapsedWidth": 48,
"mixChildMenuWidth": 180
},
"footer": {
"visible": false
}
}

View File

@@ -1,91 +0,0 @@
{
"name": "Dark Preset",
"desc": "Dark theme preset for night time usage",
"i18nkey": "theme.appearance.preset.dark",
"version": "1.0.1",
"themeScheme": "dark",
"grayscale": false,
"colourWeakness": false,
"recommendColor": false,
"themeColor": "#646cff",
"otherColor": {
"info": "#2080f0",
"success": "#52c41a",
"warning": "#faad14",
"error": "#f5222d"
},
"themeRadius": 6,
"isInfoFollowPrimary": true,
"layout": {
"mode": "vertical",
"scrollMode": "content"
},
"page": {
"animate": true,
"animateMode": "fade-slide"
},
"header": {
"height": 56,
"breadcrumb": {
"visible": true,
"showIcon": true
},
"multilingual": {
"visible": true
},
"globalSearch": {
"visible": true
}
},
"tab": {
"visible": true,
"cache": true,
"height": 44,
"mode": "chrome",
"closeTabByMiddleClick": false
},
"fixedHeaderAndTab": true,
"sider": {
"inverted": false,
"width": 220,
"collapsedWidth": 64,
"mixWidth": 90,
"mixCollapsedWidth": 64,
"mixChildMenuWidth": 200
},
"footer": {
"visible": true,
"fixed": false,
"height": 48,
"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)"
}
}
}
}

View File

@@ -1,91 +0,0 @@
{
"name": "default",
"desc": "Default theme preset with balanced settings",
"i18nkey": "theme.appearance.preset.default",
"version": "1.0.0",
"themeScheme": "light",
"grayscale": false,
"colourWeakness": false,
"recommendColor": false,
"themeColor": "#646cff",
"otherColor": {
"info": "#2080f0",
"success": "#52c41a",
"warning": "#faad14",
"error": "#f5222d"
},
"themeRadius": 6,
"isInfoFollowPrimary": true,
"layout": {
"mode": "vertical",
"scrollMode": "content"
},
"page": {
"animate": true,
"animateMode": "fade-slide"
},
"header": {
"height": 56,
"breadcrumb": {
"visible": true,
"showIcon": true
},
"multilingual": {
"visible": true
},
"globalSearch": {
"visible": true
}
},
"tab": {
"visible": true,
"cache": true,
"height": 44,
"mode": "chrome",
"closeTabByMiddleClick": false
},
"fixedHeaderAndTab": true,
"sider": {
"inverted": false,
"width": 220,
"collapsedWidth": 64,
"mixWidth": 90,
"mixCollapsedWidth": 64,
"mixChildMenuWidth": 200
},
"footer": {
"visible": true,
"fixed": false,
"height": 48,
"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)"
}
}
}
}

View File

@@ -5,7 +5,6 @@ export const themeSettings: App.Theme.ThemeSetting = {
colourWeakness: false,
recommendColor: false,
themeColor: '#646cff',
themeRadius: 6,
otherColor: {
info: '#2080f0',
success: '#52c41a',
@@ -13,6 +12,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
error: '#f5222d'
},
isInfoFollowPrimary: true,
resetCacheStrategy: 'close',
layout: {
mode: 'vertical',
scrollMode: 'content'
@@ -38,8 +38,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
visible: true,
cache: true,
height: 44,
mode: 'chrome',
closeTabByMiddleClick: false
mode: 'chrome'
},
fixedHeaderAndTab: true,
sider: {
@@ -48,8 +47,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
collapsedWidth: 64,
mixWidth: 90,
mixCollapsedWidth: 64,
mixChildMenuWidth: 200,
autoSelectFirstMenu: false
mixChildMenuWidth: 200
},
footer: {
visible: true,

38
src/typings/app.d.ts vendored
View File

@@ -4,9 +4,6 @@ declare namespace App {
namespace Theme {
type ColorPaletteNumber = import('@sa/color').ColorPaletteNumber;
/** NaiveUI theme overrides that can be specified in preset */
type NaiveUIThemeOverride = import('naive-ui').GlobalThemeOverrides;
/** Theme setting */
interface ThemeSetting {
/** Theme scheme */
@@ -19,12 +16,12 @@ declare namespace App {
recommendColor: boolean;
/** Theme color */
themeColor: string;
/** Theme radius */
themeRadius: number;
/** Other color */
otherColor: OtherColor;
/** Whether info color is followed by the primary color */
isInfoFollowPrimary: boolean;
/** Reset cache strategy */
resetCacheStrategy: UnionKey.ResetCacheStrategy;
/** Layout */
layout: {
/** Layout mode */
@@ -74,8 +71,6 @@ declare namespace App {
height: number;
/** Tab mode */
mode: UnionKey.ThemeTabMode;
/** Whether to close tab by middle click */
closeTabByMiddleClick: boolean;
};
/** Fixed header and tab */
fixedHeaderAndTab: boolean;
@@ -96,8 +91,6 @@ declare namespace App {
mixCollapsedWidth: number;
/** Child menu width when the layout is 'vertical-mix', 'top-hybrid-sidebar-first', or 'top-hybrid-header-first' */
mixChildMenuWidth: number;
/** Whether to auto select the first submenu */
autoSelectFirstMenu: boolean;
};
/** Footer */
footer: {
@@ -284,7 +277,7 @@ declare namespace App {
type FormRule = import('naive-ui').FormItemRule;
/** The global dropdown key */
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll' | 'pin' | 'unpin';
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll';
}
/**
@@ -374,7 +367,6 @@ declare namespace App {
appearance: string;
layout: string;
general: string;
preset: string;
};
appearance: {
themeSchema: { title: string } & Record<UnionKey.ThemeScheme, string>;
@@ -383,23 +375,9 @@ declare namespace App {
themeColor: {
title: string;
followPrimary: string;
} & Record<Theme.ThemeColorKey, string>;
} & Theme.ThemeColor;
recommendColor: string;
recommendColorDesc: string;
themeRadius: {
title: string;
};
preset: {
title: string;
apply: string;
applySuccess: string;
[key: string]:
| {
name: string;
desc: string;
}
| string;
};
};
layout: {
layoutMode: { title: string } & Record<UnionKey.ThemeLayoutMode, string> & {
@@ -409,11 +387,8 @@ declare namespace App {
title: string;
visible: string;
cache: string;
cacheTip: string;
height: string;
mode: { title: string } & Record<UnionKey.ThemeTabMode, string>;
closeByMiddleClick: string;
closeByMiddleClickTip: string;
};
header: {
title: string;
@@ -431,8 +406,6 @@ declare namespace App {
mixWidth: string;
mixCollapsedWidth: string;
mixChildMenuWidth: string;
autoSelectFirstMenu: string;
autoSelectFirstMenuTip: string;
};
footer: {
title: string;
@@ -443,13 +416,14 @@ declare namespace App {
};
content: {
title: string;
scrollMode: { title: string; tip: string } & Record<UnionKey.ThemeScrollMode, string>;
scrollMode: { title: string } & Record<UnionKey.ThemeScrollMode, string>;
page: {
animate: string;
mode: { title: string } & Record<UnionKey.ThemePageAnimateMode, string>;
};
fixedHeaderAndTab: string;
};
resetCacheStrategy: { title: string } & Record<UnionKey.ResetCacheStrategy, string>;
};
general: {
title: string;

View File

@@ -1,12 +1,8 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import { GlobalComponents } from 'vue'
// biome-ignore lint: disable
export {}
/* prettier-ignore */
@@ -29,13 +25,11 @@ declare module 'vue' {
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
IconUilSearch: typeof import('~icons/uil/search')['default']
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
NAlert: typeof import('naive-ui')['NAlert']
NBadge: typeof import('naive-ui')['NBadge']
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
NButton: typeof import('naive-ui')['NButton']
@@ -62,7 +56,6 @@ declare module 'vue' {
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModal: typeof import('naive-ui')['NModal']
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPopover: typeof import('naive-ui')['NPopover']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect']
NSpace: typeof import('naive-ui')['NSpace']
@@ -86,79 +79,3 @@ declare module 'vue' {
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']
}

View File

@@ -14,6 +14,14 @@ declare namespace UnionKey {
/** Theme scheme */
type ThemeScheme = 'light' | 'dark' | 'auto';
/**
* Reset cache strategy
*
* - close: re-cache when close page
* - refresh: re-cache when refresh page
*/
type ResetCacheStrategy = 'close' | 'refresh';
/**
* The layout mode
*

View File

@@ -23,7 +23,7 @@ const appStore = useAppStore();
const themeStore = useThemeStore();
interface LoginModule {
label: App.I18n.I18nKey;
label: string;
component: Component;
}

View File

@@ -15,7 +15,7 @@ const gap = computed(() => (appStore.isMobile ? 0 : 16));
<template>
<NSpace vertical :size="16">
<NAlert :title="$t('common.tip')" type="warning">
<NAlert :title="$t('common.warning')" type="warning">
{{ $t('page.home.branchDesc') }}
</NAlert>
<HeaderBanner />

View File

@@ -1,7 +1,6 @@
<script setup lang="ts">
import { computed } from 'vue';
import { createReusableTemplate } from '@vueuse/core';
import { useThemeStore } from '@/store/modules/theme';
import { $t } from '@/locales';
defineOptions({
@@ -73,8 +72,6 @@ interface GradientBgProps {
const [DefineGradientBg, GradientBg] = createReusableTemplate<GradientBgProps>();
const themeStore = useThemeStore();
function getGradientColor(color: CardData['color']) {
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">
<!-- define component start: GradientBg -->
<DefineGradientBg v-slot="{ $slots, gradientColor }">
<div
class="px-16px pb-4px pt-8px text-white"
:style="{ backgroundImage: gradientColor, borderRadius: themeStore.themeRadius + 'px' }"
>
<div class="rd-8px px-16px pb-4px pt-8px text-white" :style="{ backgroundImage: gradientColor }">
<component :is="$slots.default" />
</div>
</DefineGradientBg>

View File

@@ -21,14 +21,13 @@ const { domRef, updateOptions } = useEcharts(() => ({
}
},
legend: {
data: [$t('page.home.downloadCount'), $t('page.home.registerCount')],
top: '0'
data: [$t('page.home.downloadCount'), $t('page.home.registerCount')]
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '15%'
containLabel: true
},
xAxis: {
type: 'category',