mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-12-04 15:26:07 +08:00
Compare commits
21 Commits
v2.0.0-bet
...
v2.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e675474b43 | ||
|
|
098cd50e23 | ||
|
|
7cf4083b22 | ||
|
|
9401925f41 | ||
|
|
b8a767d704 | ||
|
|
605173a1cc | ||
|
|
73e9a0fe0b | ||
|
|
c6d97dba21 | ||
|
|
9da847fb6f | ||
|
|
c472a94395 | ||
|
|
91a261c1ef | ||
|
|
bb232bf884 | ||
|
|
f8dc639e05 | ||
|
|
46081c365c | ||
|
|
4a9cf6c3da | ||
|
|
ec792fa749 | ||
|
|
232f56fd62 | ||
|
|
6010f51801 | ||
|
|
9755c31361 | ||
|
|
0b9982bdc6 | ||
|
|
1d14269557 |
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@@ -5,7 +5,6 @@
|
||||
"antfu.unocss",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"editorconfig.editorconfig",
|
||||
"esbenp.prettier-vscode",
|
||||
"lokalise.i18n-ally",
|
||||
"mhutchie.git-graph",
|
||||
"mikestead.dotenv",
|
||||
|
||||
179
CHANGELOG.md
179
CHANGELOG.md
@@ -1,6 +1,185 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## [v2.0.1](https://github.com/soybeanjs/soybean-admin/compare/v2.0.0...v2.0.1) (2025-12-04)
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- **docs**:
|
||||
- update QQ group image in README - by @soybeanjs [<samp>(46081)</samp>](https://github.com/soybeanjs/soybean-admin/commit/46081c36)
|
||||
- **projects**:
|
||||
- support theme presets to only set partial content. - by **Azir-11** [<samp>(9da84)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9da847fb)
|
||||
- support theme perset to override component library presets. - by **Azir-11** [<samp>(60517)</samp>](https://github.com/soybeanjs/soybean-admin/commit/605173a1)
|
||||
- support pinning and unpinning of tabs - by **hooke** [<samp>(b8a76)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b8a767d7)
|
||||
- hybrid layout mode auto select first deepest child menu - by @paynezhuang [<samp>(94019)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9401925f)
|
||||
|
||||
### 🐞 Bug Fixes
|
||||
|
||||
- **docs**: update project name in ecosystem section of README - by @soybeanjs [<samp>(bb232)</samp>](https://github.com/soybeanjs/soybean-admin/commit/bb232bf8)
|
||||
- **types**: add missing property in theme presets - by **刘璐** [<samp>(4a9cf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4a9cf6c3)
|
||||
|
||||
### 🛠 Optimizations
|
||||
|
||||
- **projects**: simplify some theme preset configurations. - by **Azir-11** [<samp>(c6d97)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c6d97dba)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- **projects**: add link to ecosystem document. - by **Azir-11** [<samp>(c472a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c472a943)
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- **deps**:
|
||||
- update deps - by @soybeanjs [<samp>(f8dc6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f8dc639e)
|
||||
- update deps - by @soybeanjs [<samp>(7cf40)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7cf4083b)
|
||||
- **other**:
|
||||
- remove Prettier's recommendation. - by **Azir-11** [<samp>(73e9a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/73e9a0fe)
|
||||
- **styles**:
|
||||
- format code - by @soybeanjs [<samp>(098cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/098cd50e)
|
||||
|
||||
### 🎨 Styles
|
||||
|
||||
- **projects**: modify homepage prompt title to tip. - by **Azir-11** [<samp>(91a26)</samp>](https://github.com/soybeanjs/soybean-admin/commit/91a261c1)
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
[](https://github.com/soybeanjs) [](https://github.com/paynezhuang)
|
||||
[hooke](mailto:hellohooke@foxmail.com), [Azir-11](mailto:2075125282@qq.com), [刘璐](mailto:hi.alue@qq.com)
|
||||
|
||||
## [v2.0.0](https://github.com/soybeanjs/soybean-admin/compare/v1.3.15...v2.0.0) (2025-11-02)
|
||||
|
||||
### 🚨 Breaking Changes
|
||||
|
||||
- **hooks**: refactor useTable and enhance type definitions - by @soybeanjs [<samp>(8cc51)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8cc5177c)
|
||||
- **projects**: optimize layout mode, split horizontal mix component into two layouts, and rename the component. - by **Azir** [<samp>(b6ac3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b6ac3106)
|
||||
- **request**: remove cancelRequest method and related logic from request instances - by @soybeanjs [<samp>(b4e12)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e12530)
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- **components**:
|
||||
- add the IconTooltip component. - by **Azir-11** [<samp>(a55b4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a55b4dc0)
|
||||
- replace NTooltip with IconTooltip and optimize the layout of related components. - by **Azir-11** [<samp>(40057)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4005763c)
|
||||
- **global-tab**:
|
||||
- add support for switching tabs with right mouse button click - by @soybeanjs [<samp>(b2c91)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b2c919b6)
|
||||
- **hooks**:
|
||||
- add scrollX computation for total table width in useNaiveTable - by @Lruihao [<samp>(358e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/358e1297)
|
||||
- **packages**:
|
||||
- materials support slider-tab. closed #823 - by @CyberShen in https://github.com/soybeanjs/soybean-admin/issues/823 [<samp>(61fa4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/61fa4b7f)
|
||||
- **projects**:
|
||||
- refactor theme drawer with tabbed layout for better UX. - by **Azir** [<samp>(8ba71)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8ba71a08)
|
||||
- Add current time display option for watermark - by @wenyuanw in https://github.com/soybeanjs/soybean-admin/issues/772 [<samp>(f238f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f238fcbd)
|
||||
- add 'vertical-hybrid-header-first' layout mode - by @wenyuanw [<samp>(b4e5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b4e5c6d9)
|
||||
- add prompt information for scrolling mode and tab bar caching. - by **Azir-11** [<samp>(29a2a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/29a2a5c6)
|
||||
- support theme preset function. - by **Azir-11** [<samp>(257f1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/257f1183)
|
||||
- modify the default value of the reset cache policy to 'refresh'. - by **Azir-11** [<samp>(3c0a5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3c0a5282)
|
||||
- optimize tabs cache cleaning strategy. close #820. - by **Azir-11** in https://github.com/soybeanjs/soybean-admin/issues/820 [<samp>(ef7ac)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ef7acc62)
|
||||
- support closing tabs with middle mouse button click - by @wenyuanw [<samp>(a8d1e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a8d1e5d2)
|
||||
- support set global redius - by **CyberShen123** [<samp>(24c6d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/24c6df52)
|
||||
- support set global redius - by **CyberShen123** [<samp>(3549c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3549c4db)
|
||||
- compatible with the new Echarts API and optimized styles. - by **Azir-11** [<samp>(9755c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9755c313)
|
||||
- **styles**:
|
||||
- add text-autospace property to improve text layout - by @wenyuanw [<samp>(345aa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/345aa293)
|
||||
|
||||
### 🐞 Bug Fixes
|
||||
|
||||
- **hooks**:
|
||||
- correct chart rendering logic in useEcharts - by @soybeanjs [<samp>(8a7cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8a7cd593)
|
||||
- **layout**:
|
||||
- fix getSiderWidth - by @soybeanjs [<samp>(e471e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e471e914)
|
||||
- **packages**:
|
||||
- fix the parsing logic for stored data to ensure correct return of boolean values - by @Lruihao [<samp>(9ea56)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9ea56c9b)
|
||||
- axios: fix json response. fixed #815 - by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/815 [<samp>(fd087)</samp>](https://github.com/soybeanjs/soybean-admin/commit/fd087f59)
|
||||
- axios: fix json response. fixed #815 - by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/815 [<samp>(5be86)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5be864a8)
|
||||
- **projects**:
|
||||
- Fix i18n-ally not working when setting moduleResolution to bundler. fixed #780 - by @xiaobao0505 in https://github.com/soybeanjs/soybean-admin/issues/780 [<samp>(41191)</samp>](https://github.com/soybeanjs/soybean-admin/commit/41191d54)
|
||||
- adjust legend position in line chart options. - by **Azir-11** [<samp>(0b998)</samp>](https://github.com/soybeanjs/soybean-admin/commit/0b9982bd)
|
||||
- **readme**:
|
||||
- update GitHub stars and forks links for gitee - by @soybeanjs [<samp>(923eb)</samp>](https://github.com/soybeanjs/soybean-admin/commit/923eb98a)
|
||||
- **scripts**:
|
||||
- update command to use 'npm-check-updates' instead of 'ncu' - by @soybeanjs [<samp>(8dc17)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8dc17e62)
|
||||
- **styles**:
|
||||
- show light color scrollbar while dark mode is on - by **whyang** [<samp>(dac50)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dac5075b)
|
||||
- **table**:
|
||||
- add type annotations for records in useTable hook - by @soybeanjs [<samp>(32b8f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/32b8f990)
|
||||
- **types**:
|
||||
- fix proxy types - by @soybeanjs [<samp>(3d72f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3d72f954)
|
||||
- fix proxy types - by @soybeanjs [<samp>(12b25)</samp>](https://github.com/soybeanjs/soybean-admin/commit/12b25e0d)
|
||||
- fix ts type error - by @soybeanjs [<samp>(d5a3a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d5a3a25d)
|
||||
|
||||
### 🛠 Optimizations
|
||||
|
||||
- **hooks**:
|
||||
- optimize useEcharts - by @soybeanjs [<samp>(936b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/936b834e)
|
||||
- **packages**:
|
||||
- remove ofetch package - by @soybeanjs [<samp>(abaaa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/abaaa4a0)
|
||||
- **projects**:
|
||||
- improve theme drawer responsive width for mobile devices - by @wenyuanw [<samp>(8439a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8439a600)
|
||||
- improve robustness of second-level menu key logic - by @wenyuanw [<samp>(8b8a2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8b8a2083)
|
||||
- optimize theme drawer width - by @soybeanjs [<samp>(81468)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8146858b)
|
||||
- optimize api type file - by @soybeanjs [<samp>(3a343)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3a343eea)
|
||||
- optimize radius settings - by @soybeanjs [<samp>(87a66)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87a66a42)
|
||||
- **request**:
|
||||
- enhance request options and response handling with generic types - by @soybeanjs [<samp>(50a5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/50a5cba0)
|
||||
- **typings**:
|
||||
- update component typings - by @soybeanjs [<samp>(1d142)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1d142695)
|
||||
|
||||
### 💅 Refactors
|
||||
|
||||
- **hooks**:
|
||||
- optimize useContext and update useMixMenuContext - by @soybeanjs [<samp>(c9651)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c965140b)
|
||||
- streamline column visibility handling in useTable and table components - by @soybeanjs [<samp>(ee434)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ee434145)
|
||||
- remove useSignal hook and update exports - by @soybeanjs [<samp>(87adc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87adc35f)
|
||||
- **menu**:
|
||||
- optimize the margin on the menu - by **NicholasLD** [<samp>(d7311)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d7311111)
|
||||
- **projects**:
|
||||
- remove unnecessary logic in onRouteSwitchWhenLoggedIn - by @wenyuanw [<samp>(d6c81)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d6c8142b)
|
||||
- **request**:
|
||||
- unify response transformation methods and deprecate transformBackendResponse - by @soybeanjs [<samp>(f83ee)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f83eefbc)
|
||||
- **types**:
|
||||
- move Auth and Route namespaces to separate files and clean up api.d.ts - by **Azir** [<samp>(d37ce)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d37ce046)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- **projects**:
|
||||
- add github trendshift info. - by **恕瑞玛的皇帝** [<samp>(e18d3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e18d3972)
|
||||
- add github trendshift info. - by **恕瑞玛的皇帝** [<samp>(2a0c9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2a0c9f1b)
|
||||
- add contribution leaderboard - by @wenyuanw [<samp>(01744)</samp>](https://github.com/soybeanjs/soybean-admin/commit/017440c1)
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- **deps**:
|
||||
- update NodeJS and pnpm version requirements in package.json and documentation - by **Junior25306** [<samp>(a5c4b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a5c4b4e3)
|
||||
- update deps - by @soybeanjs [<samp>(5cb1c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5cb1cebd)
|
||||
- update deps - by @soybeanjs [<samp>(aeb63)</samp>](https://github.com/soybeanjs/soybean-admin/commit/aeb63690)
|
||||
- update deps - by @soybeanjs [<samp>(e89b8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e89b86ce)
|
||||
- update deps - by @soybeanjs [<samp>(c962f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c962f7b2)
|
||||
- update deps - by @soybeanjs [<samp>(12135)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1213531b)
|
||||
- update deps - by @soybeanjs [<samp>(e33f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e33f944a)
|
||||
- update deps - by @soybeanjs [<samp>(9fa95)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9fa951aa)
|
||||
- update deps - by @soybeanjs [<samp>(b041f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b041fdd8)
|
||||
- update deps - by @soybeanjs [<samp>(d567c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d567c057)
|
||||
- update deps - by @soybeanjs [<samp>(6cbf5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6cbf5705)
|
||||
- update deps - by @soybeanjs [<samp>(6010f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6010f518)
|
||||
- update deps - by @soybeanjs [<samp>(232f5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/232f56fd)
|
||||
- **other**:
|
||||
- update the ESLint validation configuration to support more file types. - by **Azir-11** [<samp>(8d7f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8d7f91dc)
|
||||
- update the ESLint validation configuration to support more file types. - by **Azir-11** [<samp>(be8f9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/be8f915a)
|
||||
- **packages**:
|
||||
- update Vite version to 7 in package.json and documentation. - by **Azir** [<samp>(03dd6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/03dd64c5)
|
||||
- add picomatch to fix scripts - by @soybeanjs [<samp>(805c3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/805c3381)
|
||||
- **readme**:
|
||||
- remove DartNode sponsorship badge from README files - by @soybeanjs [<samp>(33ade)</samp>](https://github.com/soybeanjs/soybean-admin/commit/33ade539)
|
||||
- **vscode**:
|
||||
- remove unused vue.server.hybridMode setting from .vscode/settings.json - by @soybeanjs [<samp>(13319)</samp>](https://github.com/soybeanjs/soybean-admin/commit/133196f3)
|
||||
|
||||
### 🎨 Styles
|
||||
|
||||
- **projects**: format code. - by **Azir-11** [<samp>(100e0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/100e0ea5)
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
[](https://github.com/soybeanjs) [](https://github.com/wenyuanw) [](https://github.com/CyberShen) [](https://github.com/Lruihao) [](https://github.com/xiaobao0505)
|
||||
[Azir-11](mailto:2075125282@qq.com), [CyberShen123](mailto:s.lijun@qq.com), [whyang](mailto:whyang9701@gmail.com), [HongxuanG](mailto:1359774872@qq.com), [NicholasLD](mailto:878639947@qq.com), [Junior25306](mailto:dayu429@qq.com)
|
||||
|
||||
## [v2.0.0-beta.2](https://github.com/soybeanjs/soybean-admin/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2025-10-27)
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
@@ -139,7 +139,7 @@ Refer to the [Code Synchronization](https://docs.soybeanjs.cn/guide/sync) docume
|
||||
|
||||
## Ecosystem
|
||||
|
||||
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): SoybeanAdmin based version of React.
|
||||
- [skyroc-admin](https://github.com/Ohh-889/skyroc-admin): SoybeanAdmin's React version implementation.
|
||||
- [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,6 +151,8 @@ 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
|
||||
|
||||
@@ -194,7 +196,7 @@ Here are the most active contributors from the past year. Thank you all for your
|
||||
|
||||
<div>
|
||||
<p>QQ Group</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-4.jpg" style="width:200px" />
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-5.jpg" style="width:200px" />
|
||||
</div>
|
||||
<!-- <div>
|
||||
<p>WeChat Group</p>
|
||||
|
||||
@@ -165,7 +165,7 @@ pnpm build
|
||||
|
||||
## 周边生态
|
||||
|
||||
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): 基于SoybeanAdmin的React版本.
|
||||
- [skyroc-admin](https://github.com/Ohh-889/skyroc-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,6 +177,8 @@ 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) 文档。
|
||||
|
||||
|
||||
## 如何贡献
|
||||
|
||||
@@ -222,7 +224,7 @@ pnpm build
|
||||
|
||||
<div>
|
||||
<p>QQ交流群</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-4.jpg" style="width:200px" />
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-5.jpg" style="width:200px" />
|
||||
</div>
|
||||
<!-- <div>
|
||||
<p>微信群</p>
|
||||
|
||||
52
package.json
52
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "soybean-admin",
|
||||
"type": "module",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.1",
|
||||
"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.0.0",
|
||||
"@vueuse/core": "14.1.0",
|
||||
"clipboard": "2.0.11",
|
||||
"dayjs": "1.11.18",
|
||||
"dayjs": "1.11.19",
|
||||
"defu": "6.1.4",
|
||||
"echarts": "6.0.0",
|
||||
"json5": "2.2.3",
|
||||
"naive-ui": "2.43.1",
|
||||
"naive-ui": "2.43.2",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "3.0.3",
|
||||
"tailwind-merge": "3.3.1",
|
||||
"vue": "3.5.22",
|
||||
"pinia": "3.0.4",
|
||||
"tailwind-merge": "3.4.0",
|
||||
"vue": "3.5.25",
|
||||
"vue-draggable-plus": "0.6.0",
|
||||
"vue-i18n": "11.1.12",
|
||||
"vue-i18n": "11.2.2",
|
||||
"vue-router": "4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@elegant-router/vue": "0.3.8",
|
||||
"@iconify/json": "2.2.400",
|
||||
"@iconify/json": "2.2.414",
|
||||
"@sa/scripts": "workspace:*",
|
||||
"@sa/uno-preset": "workspace:*",
|
||||
"@soybeanjs/eslint-config": "1.7.1",
|
||||
"@types/node": "24.9.1",
|
||||
"@soybeanjs/eslint-config": "1.7.4",
|
||||
"@types/node": "24.10.1",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@unocss/eslint-config": "66.5.4",
|
||||
"@unocss/preset-icons": "66.5.4",
|
||||
"@unocss/preset-uno": "66.5.4",
|
||||
"@unocss/transformer-directives": "66.5.4",
|
||||
"@unocss/transformer-variant-group": "66.5.4",
|
||||
"@unocss/vite": "66.5.4",
|
||||
"@vitejs/plugin-vue": "6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "5.1.1",
|
||||
"@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",
|
||||
"consola": "3.4.2",
|
||||
"eslint": "9.38.0",
|
||||
"eslint-plugin-vue": "10.5.1",
|
||||
"eslint": "9.39.1",
|
||||
"eslint-plugin-vue": "10.6.2",
|
||||
"kolorist": "1.8.0",
|
||||
"sass": "1.93.2",
|
||||
"sass": "1.94.2",
|
||||
"simple-git-hooks": "2.13.1",
|
||||
"tsx": "4.20.6",
|
||||
"tsx": "4.21.0",
|
||||
"typescript": "5.9.3",
|
||||
"unplugin-icons": "22.5.0",
|
||||
"unplugin-vue-components": "30.0.0",
|
||||
"vite": "7.1.12",
|
||||
"vite": "7.2.6",
|
||||
"vite-plugin-progress": "0.0.7",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"vite-plugin-vue-devtools": "8.0.3",
|
||||
"vite-plugin-vue-devtools": "8.0.5",
|
||||
"vue-eslint-parser": "10.2.0",
|
||||
"vue-tsc": "3.1.2"
|
||||
"vue-tsc": "3.1.5"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"commit-msg": "pnpm sa git-commit-verify",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/alova",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./fetch": "./src/fetch.ts",
|
||||
@@ -15,6 +15,6 @@
|
||||
"dependencies": {
|
||||
"@alova/mock": "2.0.17",
|
||||
"@sa/utils": "workspace:*",
|
||||
"alova": "3.3.4"
|
||||
"alova": "3.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/axios",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
@@ -11,7 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sa/utils": "workspace:*",
|
||||
"axios": "1.12.2",
|
||||
"axios": "1.13.2",
|
||||
"axios-retry": "4.5.0",
|
||||
"qs": "6.14.0"
|
||||
},
|
||||
|
||||
@@ -119,8 +119,11 @@ 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>>>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/color",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/hooks",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
@@ -26,8 +26,11 @@ 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>>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/materials",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
@@ -146,7 +146,8 @@ export type LayoutScrollMode = 'wrapper' | 'content';
|
||||
|
||||
/** Admin layout props */
|
||||
export interface AdminLayoutProps
|
||||
extends AdminLayoutHeaderConfig,
|
||||
extends
|
||||
AdminLayoutHeaderConfig,
|
||||
AdminLayoutTabConfig,
|
||||
AdminLayoutSiderConfig,
|
||||
AdminLayoutContentConfig,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/scripts",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.1",
|
||||
"bin": {
|
||||
"sa": "./bin.ts"
|
||||
},
|
||||
@@ -14,15 +14,15 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@soybeanjs/changelog": "0.3.25",
|
||||
"bumpp": "10.3.1",
|
||||
"c12": "3.3.1",
|
||||
"bumpp": "10.3.2",
|
||||
"c12": "3.3.2",
|
||||
"cac": "6.7.14",
|
||||
"consola": "3.4.2",
|
||||
"enquirer": "2.4.1",
|
||||
"execa": "9.6.0",
|
||||
"execa": "9.6.1",
|
||||
"kolorist": "1.8.0",
|
||||
"npm-check-updates": "19.1.1",
|
||||
"npm-check-updates": "19.1.2",
|
||||
"picomatch": "4.0.3",
|
||||
"rimraf": "6.0.1"
|
||||
"rimraf": "6.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/uno-preset",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/utils",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
2474
pnpm-lock.yaml
generated
2474
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ 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);
|
||||
@@ -10,6 +11,7 @@ export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu',
|
||||
function useMixMenu() {
|
||||
const route = useRoute();
|
||||
const routeStore = useRouteStore();
|
||||
const themeStore = useThemeStore();
|
||||
const { selectedKey } = useMenu();
|
||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||
|
||||
@@ -100,10 +102,46 @@ 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 }
|
||||
);
|
||||
@@ -121,7 +159,10 @@ function useMixMenu() {
|
||||
isActiveSecondLevelMenuHasChildren,
|
||||
handleSelectSecondLevelMenu,
|
||||
getActiveSecondLevelMenuKey,
|
||||
childLevelMenus
|
||||
childLevelMenus,
|
||||
hasChildLevelMenus,
|
||||
getDeepestLevelMenuKey,
|
||||
activeDeepestLevelMenuKey
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
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';
|
||||
@@ -18,12 +19,28 @@ const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
const routeStore = useRouteStore();
|
||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||
useMixMenuContext('TopHybridHeaderFirst');
|
||||
const {
|
||||
firstLevelMenus,
|
||||
secondLevelMenus,
|
||||
activeFirstLevelMenuKey,
|
||||
handleSelectFirstLevelMenu,
|
||||
activeDeepestLevelMenuKey
|
||||
} = 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 = [];
|
||||
@@ -49,7 +66,7 @@ watch(
|
||||
:options="firstLevelMenus"
|
||||
:indent="18"
|
||||
responsive
|
||||
@update:value="handleSelectFirstLevelMenu"
|
||||
@update:value="handleSelectMenu"
|
||||
/>
|
||||
</Teleport>
|
||||
<Teleport :to="`#${GLOBAL_SIDER_MENU_ID}`">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<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';
|
||||
@@ -13,9 +14,25 @@ defineOptions({
|
||||
const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||
useMixMenuContext('TopHybridSidebarFirst');
|
||||
const {
|
||||
firstLevelMenus,
|
||||
secondLevelMenus,
|
||||
activeFirstLevelMenuKey,
|
||||
handleSelectFirstLevelMenu,
|
||||
activeDeepestLevelMenuKey
|
||||
} = 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>
|
||||
@@ -37,7 +54,7 @@ const { selectedKey } = useMenu();
|
||||
:sider-collapse="appStore.siderCollapse"
|
||||
:dark-mode="themeStore.darkMode"
|
||||
:theme-color="themeStore.themeColor"
|
||||
@select="handleSelectFirstLevelMenu"
|
||||
@select="handleSelectMenu"
|
||||
@toggle-sider-collapse="appStore.toggleSiderCollapse"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -33,15 +33,15 @@ const {
|
||||
isActiveSecondLevelMenuHasChildren,
|
||||
handleSelectSecondLevelMenu,
|
||||
getActiveSecondLevelMenuKey,
|
||||
childLevelMenus
|
||||
childLevelMenus,
|
||||
hasChildLevelMenus,
|
||||
activeDeepestLevelMenuKey
|
||||
} = useMixMenuContext('VerticalHybridHeaderFirst');
|
||||
const { selectedKey } = useMenu();
|
||||
|
||||
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
||||
|
||||
const hasChildMenus = computed(() => childLevelMenus.value.length > 0);
|
||||
|
||||
const showDrawer = computed(() => hasChildMenus.value && (drawerVisible.value || appStore.mixSiderFixed));
|
||||
const showDrawer = computed(() => hasChildLevelMenus.value && (drawerVisible.value || appStore.mixSiderFixed));
|
||||
|
||||
function handleSelectMixMenu(key: RouteKey) {
|
||||
handleSelectSecondLevelMenu(key);
|
||||
@@ -51,12 +51,33 @@ 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) {
|
||||
handleSelectMixMenu(secondLevelMenus.value[0].routeKey);
|
||||
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;
|
||||
}
|
||||
|
||||
// Case 2: autoSelectFirstMenu enabled - navigate to deepest menu
|
||||
activeDeepestLevelMenuKey();
|
||||
setDrawerVisible(false);
|
||||
}
|
||||
|
||||
function handleResetActiveMenu() {
|
||||
@@ -114,7 +135,9 @@ watch(
|
||||
</FirstLevelMenu>
|
||||
<div
|
||||
class="relative h-full transition-width-300"
|
||||
:style="{ width: appStore.mixSiderFixed && hasChildMenus ? themeStore.sider.mixChildMenuWidth + 'px' : '0px' }"
|
||||
:style="{
|
||||
width: appStore.mixSiderFixed && hasChildLevelMenus ? themeStore.sider.mixChildMenuWidth + 'px' : '0px'
|
||||
}"
|
||||
>
|
||||
<DarkModeContainer
|
||||
class="absolute-lt h-full flex-col-stretch nowrap-hidden shadow-sm transition-all-300"
|
||||
|
||||
@@ -26,7 +26,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
const visible = defineModel<boolean>('visible');
|
||||
|
||||
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs } = useTabStore();
|
||||
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs, fixTab, unfixTab, isTabRetain } = useTabStore();
|
||||
const { SvgIconVNode } = useSvgIcon();
|
||||
|
||||
type DropdownOption = {
|
||||
@@ -64,6 +64,23 @@ 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));
|
||||
@@ -98,6 +115,12 @@ const dropdownAction: Record<App.Global.DropdownKey, () => void> = {
|
||||
},
|
||||
closeAll() {
|
||||
clearTabs();
|
||||
},
|
||||
pin() {
|
||||
fixTab(props.tabId);
|
||||
},
|
||||
unpin() {
|
||||
unfixTab(props.tabId);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ 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>
|
||||
@@ -32,6 +33,12 @@ const isMixLayoutMode = computed(() => layoutMode.value.includes('mix') || layou
|
||||
<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>
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<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({
|
||||
@@ -31,6 +33,8 @@ type ThemePreset = Pick<
|
||||
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' });
|
||||
@@ -76,7 +80,9 @@ const getPresetDesc = (preset: ThemePreset): string => {
|
||||
}
|
||||
};
|
||||
|
||||
const applyPreset = ({ themeScheme, grayscale, colourWeakness, layout, watermark, ...rest }: ThemePreset): void => {
|
||||
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);
|
||||
@@ -96,6 +102,9 @@ const applyPreset = ({ themeScheme, grayscale, colourWeakness, layout, watermark
|
||||
tokens: { ...rest.tokens }
|
||||
});
|
||||
|
||||
// Apply NaiveUI theme overrides if present
|
||||
themeStore.setNaiveThemeOverrides(naiveui);
|
||||
|
||||
window.$message?.success($t('theme.appearance.preset.applySuccess'));
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -160,7 +160,10 @@ const local: App.I18n.Schema = {
|
||||
collapsedWidth: 'Sider Collapsed Width',
|
||||
mixWidth: 'Mix Sider Width',
|
||||
mixCollapsedWidth: 'Mix Sider Collapse Width',
|
||||
mixChildMenuWidth: 'Mix Child Menu Width'
|
||||
mixChildMenuWidth: 'Mix Child Menu Width',
|
||||
autoSelectFirstMenu: 'Auto Select First Submenu',
|
||||
autoSelectFirstMenuTip:
|
||||
'When a first-level menu is clicked, the first submenu is automatically selected and navigated to the deepest level'
|
||||
},
|
||||
footer: {
|
||||
title: 'Footer Settings',
|
||||
@@ -336,7 +339,9 @@ const local: App.I18n.Schema = {
|
||||
closeOther: 'Close Other',
|
||||
closeLeft: 'Close Left',
|
||||
closeRight: 'Close Right',
|
||||
closeAll: 'Close All'
|
||||
closeAll: 'Close All',
|
||||
pin: 'Pin Tab',
|
||||
unpin: 'Unpin Tab'
|
||||
},
|
||||
icon: {
|
||||
themeConfig: 'Theme Configuration',
|
||||
|
||||
@@ -157,7 +157,9 @@ const local: App.I18n.Schema = {
|
||||
collapsedWidth: '侧边栏折叠宽度',
|
||||
mixWidth: '混合布局侧边栏宽度',
|
||||
mixCollapsedWidth: '混合布局侧边栏折叠宽度',
|
||||
mixChildMenuWidth: '混合布局子菜单宽度'
|
||||
mixChildMenuWidth: '混合布局子菜单宽度',
|
||||
autoSelectFirstMenu: '自动选择第一个子菜单',
|
||||
autoSelectFirstMenuTip: '点击一级菜单时,自动选择并导航到第一个子菜单的最深层级'
|
||||
},
|
||||
footer: {
|
||||
title: '底部设置',
|
||||
@@ -333,7 +335,9 @@ const local: App.I18n.Schema = {
|
||||
closeOther: '关闭其它',
|
||||
closeLeft: '关闭左侧',
|
||||
closeRight: '关闭右侧',
|
||||
closeAll: '关闭所有'
|
||||
closeAll: '关闭所有',
|
||||
pin: '固定标签',
|
||||
unpin: '取消固定'
|
||||
},
|
||||
icon: {
|
||||
themeConfig: '主题配置',
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
getTabByRoute,
|
||||
getTabIdByRoute,
|
||||
isTabInTabs,
|
||||
reorderFixedTabs,
|
||||
updateTabByI18nKey,
|
||||
updateTabsByI18nKey
|
||||
} from './shared';
|
||||
@@ -248,6 +249,48 @@ 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
|
||||
*
|
||||
@@ -328,6 +371,8 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
|
||||
clearTabs,
|
||||
clearLeftTabs,
|
||||
clearRightTabs,
|
||||
fixTab,
|
||||
unfixTab,
|
||||
switchRouteByTab,
|
||||
setTabLabel,
|
||||
resetTabLabel,
|
||||
|
||||
@@ -198,6 +198,18 @@ 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
|
||||
*
|
||||
|
||||
@@ -24,6 +24,9 @@ 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 });
|
||||
|
||||
@@ -53,7 +56,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
||||
});
|
||||
|
||||
/** Naive theme */
|
||||
const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value));
|
||||
const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value, naiveThemeOverrides.value));
|
||||
|
||||
/**
|
||||
* Settings json
|
||||
@@ -198,6 +201,15 @@ 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;
|
||||
@@ -284,6 +296,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
||||
updateThemeColors,
|
||||
setThemeLayout,
|
||||
setWatermarkEnableUserName,
|
||||
setWatermarkEnableTime
|
||||
setWatermarkEnableTime,
|
||||
setNaiveThemeOverrides
|
||||
};
|
||||
});
|
||||
|
||||
@@ -236,11 +236,15 @@ function getNaiveThemeColors(colors: App.Theme.ThemeColor, recommended = false)
|
||||
/**
|
||||
* Get naive theme
|
||||
*
|
||||
* @param settings Theme settings object.
|
||||
* @param settings.recommendColor Whether to use recommended color palette.
|
||||
* @param settings.themeRadius Border radius to use in the theme (in px).
|
||||
* @param colors Theme colors
|
||||
* @param settings Theme settings object
|
||||
* @param overrides Optional manual overrides from preset
|
||||
*/
|
||||
export function getNaiveTheme(colors: App.Theme.ThemeColor, settings: App.Theme.ThemeSetting) {
|
||||
export function getNaiveTheme(
|
||||
colors: App.Theme.ThemeColor,
|
||||
settings: App.Theme.ThemeSetting,
|
||||
overrides?: GlobalThemeOverrides
|
||||
) {
|
||||
const { primary: colorLoading } = colors;
|
||||
|
||||
const theme: GlobalThemeOverrides = {
|
||||
@@ -256,5 +260,7 @@ export function getNaiveTheme(colors: App.Theme.ThemeColor, settings: App.Theme.
|
||||
}
|
||||
};
|
||||
|
||||
return theme;
|
||||
// If there are overrides, merge them with priority
|
||||
// overrides has higher priority than auto-generated theme
|
||||
return overrides ? defu(overrides, theme) : theme;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
"name": "Azir's Preset",
|
||||
"desc": "It is a cold and elegant preset that Azir likes",
|
||||
"i18nkey": "theme.appearance.preset.azir",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"themeScheme": "light",
|
||||
"grayscale": false,
|
||||
"colourWeakness": false,
|
||||
"recommendColor": true,
|
||||
"themeColor": "#78a878",
|
||||
"otherColor": {
|
||||
@@ -14,57 +12,7 @@
|
||||
"warning": "#d4bb9d",
|
||||
"error": "#c49a9a"
|
||||
},
|
||||
"themeRadius": 6,
|
||||
"isInfoFollowPrimary": true,
|
||||
"layout": {
|
||||
"mode": "vertical-mix",
|
||||
"scrollMode": "wrapper"
|
||||
},
|
||||
"page": {
|
||||
"animate": true,
|
||||
"animateMode": "zoom-fade"
|
||||
},
|
||||
"header": {
|
||||
"height": 64,
|
||||
"breadcrumb": {
|
||||
"visible": true,
|
||||
"showIcon": true
|
||||
},
|
||||
"multilingual": {
|
||||
"visible": true
|
||||
},
|
||||
"globalSearch": {
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"visible": true,
|
||||
"cache": true,
|
||||
"height": 48,
|
||||
"mode": "chrome"
|
||||
},
|
||||
"fixedHeaderAndTab": true,
|
||||
"sider": {
|
||||
"inverted": false,
|
||||
"width": 220,
|
||||
"collapsedWidth": 64,
|
||||
"mixWidth": 90,
|
||||
"mixCollapsedWidth": 64,
|
||||
"mixChildMenuWidth": 200
|
||||
},
|
||||
"footer": {
|
||||
"visible": true,
|
||||
"fixed": true,
|
||||
"height": 56,
|
||||
"right": true
|
||||
},
|
||||
"watermark": {
|
||||
"visible": false,
|
||||
"text": "SoybeanAdmin",
|
||||
"enableUserName": false,
|
||||
"enableTime": true,
|
||||
"timeFormat": "YYYY-MM-DD HH:mm:ss"
|
||||
},
|
||||
"tokens": {
|
||||
"light": {
|
||||
"colors": {
|
||||
@@ -86,5 +34,19 @@
|
||||
"base-text": "rgb(224, 224, 224)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"naiveui": {
|
||||
"Alert": {
|
||||
"borderRadiusMedium": "12px",
|
||||
"fontWeightStrong": "600",
|
||||
"paddingMedium": "0 20px"
|
||||
},
|
||||
"Card": {
|
||||
"borderRadius": "16px",
|
||||
"paddingMedium": "24px"
|
||||
},
|
||||
"Input": {
|
||||
"borderRadius": "10px"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,12 @@
|
||||
"name": "Compact Preset",
|
||||
"desc": "Compact layout preset for small screens",
|
||||
"i18nkey": "theme.appearance.preset.compact",
|
||||
"version": "1.0.0",
|
||||
"themeScheme": "light",
|
||||
"grayscale": false,
|
||||
"colourWeakness": false,
|
||||
"recommendColor": false,
|
||||
"themeColor": "#646cff",
|
||||
"otherColor": {
|
||||
"info": "#2080f0",
|
||||
"success": "#52c41a",
|
||||
"warning": "#faad14",
|
||||
"error": "#f5222d"
|
||||
},
|
||||
"version": "1.0.1",
|
||||
"themeRadius": 6,
|
||||
"isInfoFollowPrimary": true,
|
||||
"layout": {
|
||||
"mode": "vertical",
|
||||
"scrollMode": "content"
|
||||
},
|
||||
"page": {
|
||||
"animate": true,
|
||||
"animateMode": "fade-slide"
|
||||
},
|
||||
"header": {
|
||||
"height": 48,
|
||||
"breadcrumb": {
|
||||
"visible": true,
|
||||
"showIcon": true
|
||||
"visible": false
|
||||
},
|
||||
"multilingual": {
|
||||
"visible": false
|
||||
@@ -41,9 +20,9 @@
|
||||
"visible": true,
|
||||
"cache": true,
|
||||
"height": 36,
|
||||
"mode": "button"
|
||||
"mode": "button",
|
||||
"closeTabByMiddleClick": false
|
||||
},
|
||||
"fixedHeaderAndTab": true,
|
||||
"sider": {
|
||||
"inverted": false,
|
||||
"width": 180,
|
||||
@@ -53,38 +32,6 @@
|
||||
"mixChildMenuWidth": 180
|
||||
},
|
||||
"footer": {
|
||||
"visible": false,
|
||||
"fixed": false,
|
||||
"height": 40,
|
||||
"right": true
|
||||
},
|
||||
"watermark": {
|
||||
"visible": false,
|
||||
"text": "SoybeanAdmin",
|
||||
"enableUserName": false,
|
||||
"enableTime": false,
|
||||
"timeFormat": "YYYY-MM-DD HH:mm"
|
||||
},
|
||||
"tokens": {
|
||||
"light": {
|
||||
"colors": {
|
||||
"container": "rgb(255, 255, 255)",
|
||||
"layout": "rgb(247, 250, 252)",
|
||||
"inverted": "rgb(0, 20, 40)",
|
||||
"base-text": "rgb(31, 31, 31)"
|
||||
},
|
||||
"boxShadow": {
|
||||
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||
}
|
||||
},
|
||||
"dark": {
|
||||
"colors": {
|
||||
"container": "rgb(28, 28, 28)",
|
||||
"layout": "rgb(18, 18, 18)",
|
||||
"base-text": "rgb(224, 224, 224)"
|
||||
}
|
||||
}
|
||||
"visible": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
"name": "Dark Preset",
|
||||
"desc": "Dark theme preset for night time usage",
|
||||
"i18nkey": "theme.appearance.preset.dark",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"themeScheme": "dark",
|
||||
"grayscale": false,
|
||||
"colourWeakness": false,
|
||||
"recommendColor": false,
|
||||
"themeColor": "#409eff",
|
||||
"themeColor": "#646cff",
|
||||
"otherColor": {
|
||||
"info": "#2080f0",
|
||||
"success": "#52c41a",
|
||||
@@ -41,11 +41,12 @@
|
||||
"visible": true,
|
||||
"cache": true,
|
||||
"height": 44,
|
||||
"mode": "chrome"
|
||||
"mode": "chrome",
|
||||
"closeTabByMiddleClick": false
|
||||
},
|
||||
"fixedHeaderAndTab": true,
|
||||
"sider": {
|
||||
"inverted": true,
|
||||
"inverted": false,
|
||||
"width": 220,
|
||||
"collapsedWidth": 64,
|
||||
"mixWidth": 90,
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
"visible": true,
|
||||
"cache": true,
|
||||
"height": 44,
|
||||
"mode": "chrome"
|
||||
"mode": "chrome",
|
||||
"closeTabByMiddleClick": false
|
||||
},
|
||||
"fixedHeaderAndTab": true,
|
||||
"sider": {
|
||||
|
||||
@@ -48,7 +48,8 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
||||
collapsedWidth: 64,
|
||||
mixWidth: 90,
|
||||
mixCollapsedWidth: 64,
|
||||
mixChildMenuWidth: 200
|
||||
mixChildMenuWidth: 200,
|
||||
autoSelectFirstMenu: false
|
||||
},
|
||||
footer: {
|
||||
visible: true,
|
||||
|
||||
9
src/typings/app.d.ts
vendored
9
src/typings/app.d.ts
vendored
@@ -4,6 +4,9 @@ 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 */
|
||||
@@ -93,6 +96,8 @@ 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: {
|
||||
@@ -279,7 +284,7 @@ declare namespace App {
|
||||
type FormRule = import('naive-ui').FormItemRule;
|
||||
|
||||
/** The global dropdown key */
|
||||
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll';
|
||||
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll' | 'pin' | 'unpin';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,6 +431,8 @@ declare namespace App {
|
||||
mixWidth: string;
|
||||
mixCollapsedWidth: string;
|
||||
mixChildMenuWidth: string;
|
||||
autoSelectFirstMenu: string;
|
||||
autoSelectFirstMenuTip: string;
|
||||
};
|
||||
footer: {
|
||||
title: string;
|
||||
|
||||
82
src/typings/components.d.ts
vendored
82
src/typings/components.d.ts
vendored
@@ -1,8 +1,12 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// biome-ignore lint: disable
|
||||
// oxlint-disable
|
||||
// ------
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
// biome-ignore lint: disable
|
||||
import { GlobalComponents } from 'vue'
|
||||
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
@@ -82,3 +86,79 @@ 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']
|
||||
}
|
||||
@@ -15,7 +15,7 @@ const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
||||
|
||||
<template>
|
||||
<NSpace vertical :size="16">
|
||||
<NAlert :title="$t('common.warning')" type="warning">
|
||||
<NAlert :title="$t('common.tip')" type="warning">
|
||||
{{ $t('page.home.branchDesc') }}
|
||||
</NAlert>
|
||||
<HeaderBanner />
|
||||
|
||||
@@ -21,13 +21,14 @@ const { domRef, updateOptions } = useEcharts(() => ({
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: [$t('page.home.downloadCount'), $t('page.home.registerCount')]
|
||||
data: [$t('page.home.downloadCount'), $t('page.home.registerCount')],
|
||||
top: '0'
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
top: '15%'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
|
||||
Reference in New Issue
Block a user