Compare commits

..

1 Commits

Author SHA1 Message Date
Soybean
ebdef724b0 chore(release): release tauri v1.0.2 2024-04-08 08:03:18 +08:00
153 changed files with 12478 additions and 11962 deletions

6
.env
View File

@@ -40,9 +40,3 @@ VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998
# when the route mode is static, the defined super role
VITE_STATIC_SUPER_ROLE=R_SUPER
# sourcemap
VITE_SOURCE_MAP=N
# Used to differentiate storage across different domains
VITE_STORAGE_PREFIX=SOY_

View File

@@ -1,5 +1,5 @@
# backend service base url, prod environment
VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
VITE_SERVICE_BASE_URL=https://mock.apifox.com/m1/3109515-0-default
# other backend service base url, prod environment
VITE_OTHER_SERVICE_BASE_URL= `{

View File

@@ -1,5 +1,5 @@
# backend service base url, test environment
VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
VITE_SERVICE_BASE_URL=https://mock.apifox.com/m1/3109515-0-default
# other backend service base url, test environment
VITE_OTHER_SERVICE_BASE_URL= `{

1
.npmrc
View File

@@ -1,4 +1,3 @@
registry=https://registry.npmmirror.com/
shamefully-hoist=true
ignore-workspace-root-check=true
link-workspace-packages=true

2
.vscode/launch.json vendored
View File

@@ -12,7 +12,7 @@
"type": "node",
"request": "launch",
"name": "TS Debugger",
"runtimeExecutable": "tsx",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/tsx",
"skipFiles": ["<node_internals>/**", "${workspaceFolder}/node_modules/**"],
"program": "${file}"
}

View File

@@ -13,7 +13,7 @@
"i18n-ally.keystyle": "nested",
"i18n-ally.localesPaths": ["src/locales/langs"],
"prettier.enable": false,
"typescript.tsdk": "node_modules/typescript/lib",
"unocss.root": ["./"],
"typescript.tsdk": "node_modules/typescript/lib",
"vue.server.hybridMode": true
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,340 +0,0 @@
# 更新日志
## [v1.2.4](https://github.com/soybeanjs/soybean-admin/compare/v1.2.3...v1.2.4) (2024-06-14)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**:
- 优化 `setupAppVersionNotification` &nbsp;-&nbsp;@soybeanjs 提交 [<samp>(b5a72)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b5a723c)
- 获取 'Asia/Shanghai' 时区的构建时间 &nbsp;-&nbsp;@soybeanjs 提交 [<samp>(069fa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/069fa8a)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
## [v1.2.3](https://github.com/soybeanjs/soybean-admin/compare/v1.2.2...v1.2.3) (2024-06-13)
### &nbsp;&nbsp;&nbsp;🐞 修复错误
- **项目**:
- 通过在index.html中添加color-scheme元标签修复移动浏览器主题问题 &nbsp;-&nbsp;@KickCashew 在 https://github.com/soybeanjs/soybean-admin/issues/488 中提交 [<samp>(c2125)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c212565)
- 修复二级目录组件为空 &nbsp;-&nbsp;@paynezhuang 在 https://github.com/soybeanjs/soybean-admin/issues/491 中提交 [<samp>(aabb2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/aabb2a4)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**:
- 修复超链接指向错误 &nbsp;-&nbsp;**Azir** 提交 [<samp>(20a81)</samp>](https://github.com/soybeanjs/soybean-admin/commit/20a8127)
- 更新 README &nbsp;-&nbsp;@soybeanjs 提交 [<samp>(70261)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7026126)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **依赖**:
- 更新依赖 &nbsp;-&nbsp;@soybeanjs 提交 [<samp>(813d8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/813d8ce)
- 更新依赖 &nbsp;-&nbsp;@soybeanjs 提交 [<samp>(bf718)</samp>](https://github.com/soybeanjs/soybean-admin/commit/bf71837)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![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;[![KickCashew](https://github.com/KickCashew.png?size=48)](https://github.com/KickCashew)&nbsp;&nbsp;
[Azir](mailto:2075125282@qq.com)
## [v1.2.2](https://github.com/honghuangdc/soybean-admin/compare/v1.2.1...v1.2.2) (2024-06-12)
### &nbsp;&nbsp;&nbsp;🚀 特性
- **项目**: 切换标签时重置滚动位置 &nbsp;-&nbsp;@soybeanjs 提交 [<samp>(9094b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/9094b21)
### &nbsp;&nbsp;&nbsp;🐞 修复错误
- **项目**:
- 在DEV模式下隐藏AppVersionNotification &nbsp;-&nbsp;@sigma-plus 在 https://github.com/honghuangdc/soybean-admin/issues/482 中提交 [<samp>(62592)</samp>](https://github.com/honghuangdc/soybean-admin/commit/6259287)
- 修复在移动布局中隐藏menu-toggler。fixed #483 &nbsp;-&nbsp;@soybeanjs 在 https://github.com/honghuangdc/soybean-admin/issues/483 中提交 [<samp>(4470c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4470cb4)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新 README &nbsp;-&nbsp;@soybeanjs 提交 [<samp>(8f9a7)</samp>](https://github.com/honghuangdc/soybean-admin/commit/8f9a705)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![sigma-plus](https://github.com/sigma-plus.png?size=48)](https://github.com/sigma-plus)&nbsp;&nbsp;
## [v1.2.1](https://github.com/honghuangdc/soybean-admin/compare/v1.2.0...v1.2.1) (2024-06-07)
### &nbsp;&nbsp;&nbsp;🐞 修复错误
- **项目**:
- 修复页面重新加载时获取用户信息 &nbsp;-&nbsp;@soybeanjs 提交 [<samp>(ff51b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ff51b72)
- 修复setupAppVersionNotification渲染 &nbsp;-&nbsp;@soybeanjs 提交 [<samp>(6a6eb)</samp>](https://github.com/honghuangdc/soybean-admin/commit/6a6eb9a)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新CHANGELOG &nbsp;-&nbsp;@soybeanjs [<samp>(fe06b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/fe06b8c)完成
### &nbsp;&nbsp;&nbsp;🏡 日常任务
- **依赖**: 更新依赖 &nbsp;-&nbsp;@soybeanjs [<samp>(08827)</samp>](https://github.com/honghuangdc/soybean-admin/commit/08827a4)完成
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
## [v1.2.0](https://github.com/soybeanjs/soybean-admin/compare/v1.1.5...v1.2.0) (2024-06-06)
### &nbsp;&nbsp;&nbsp;🚀 功能
- **项目**:
- 支持系统新版本更新通知。关闭 #420 &nbsp;-&nbsp;@soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/420 [<samp>(584cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/584cd54)
- 在路由守卫中获取用户信息并从localStorage中移除。关闭 #459 &nbsp;-&nbsp;@soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/459 [<samp>(5531a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5531a68)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新CHANGELOG &nbsp;-&nbsp;@soybeanjs [<samp>(2bec8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2bec899)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
## [v1.1.5](https://github.com/soybeanjs/soybean-admin/compare/v1.1.4...v1.1.5) (2024-06-06)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **项目**: 修复注册组件名CodeLogin => Register &nbsp;-&nbsp;@m-xlsea 在 https://github.com/soybeanjs/soybean-admin/issues/478 [<samp>(ddf38)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ddf3823)
### &nbsp;&nbsp;&nbsp;🏡 杂务
- **依赖**: 更新依赖 &nbsp;-&nbsp;@soybeanjs [<samp>(060c0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/060c0a9)
- **项目**: 更新 vscode 设置: vue 官方 &nbsp;-&nbsp;@soybeanjs [<samp>(76649)</samp>](https://github.com/soybeanjs/soybean-admin/commit/76649e2)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![m-xlsea](https://github.com/m-xlsea.png?size=48)](https://github.com/m-xlsea)&nbsp;&nbsp;
## [v1.1.4](https://github.com/honghuangdc/soybean-admin/compare/v1.1.3...v1.1.4) (2024-06-06)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **utils**: 修复了按esc键时modalLogout的错误 &nbsp;-&nbsp;@sigma-plus 在 https://github.com/honghuangdc/soybean-admin/issues/470 中提出 [<samp>(bd69c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/bd69c00)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **projects**: 优化了RouteMeta的备注 &nbsp;-&nbsp;@soybeanjs 提出 [<samp>(ffb48)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ffb48b1)
### &nbsp;&nbsp;&nbsp;📖 文档
- **projects**:
- 更新了CHANGELOG &nbsp;-&nbsp;@soybeanjs 提出 [<samp>(756f8)</samp>](https://github.com/honghuangdc/soybean-admin/commit/756f84a)
- 更新了Node&pnpm版本 &nbsp;-&nbsp;@Azir-11 在 https://github.com/honghuangdc/soybean-admin/issues/472 中提出 [<samp>(9b05d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/9b05d73)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **deps**:
- 更新了依赖 &nbsp;-&nbsp;@soybeanjs 提出 [<samp>(d0380)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d0380ce)
- 更新了依赖 &nbsp;-&nbsp;@soybeanjs 提出 [<samp>(1f464)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1f4647b)
- **projects**:
- 关闭了http代理 &nbsp;-&nbsp;@soybeanjs 提出 [<samp>(d08a3)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d08a381)
- 更新了mock url &nbsp;-&nbsp;@soybeanjs 提出 [<samp>(e6086)</samp>](https://github.com/honghuangdc/soybean-admin/commit/e6086f0)
- 更新了vscode设置 &nbsp;-&nbsp;@soybeanjs 提出 [<samp>(910df)</samp>](https://github.com/honghuangdc/soybean-admin/commit/910dfca)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;[![sigma-plus](https://github.com/sigma-plus.png?size=48)](https://github.com/sigma-plus)&nbsp;&nbsp;
## [v1.1.3](https://github.com/soybeanjs/soybean-admin/compare/v1.1.2...v1.1.3) (2024-06-02)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **组件**:
- 修复了由于回车导致搜索框反复弹出的问题 &nbsp;-&nbsp;@Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/468 中修复 [<samp>(5bd96)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5bd96b8)
- **项目**:
- 修复点击菜单搜索。修复了 #466,关闭 #467 &nbsp;-&nbsp;@soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/466 和 https://github.com/soybeanjs/soybean-admin/issues/467 中修复 [<samp>(8efdb)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8efdb10)
- 修复 reCacheRoute。修复了 #464 &nbsp;-&nbsp;@soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/464 中修复 [<samp>(59faf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/59faf15)
- **样式**:
- 修复 FirstLevelMenu 样式。修复了 #450 &nbsp;-&nbsp;@soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/450 中修复 [<samp>(db64b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/db64b0e)
- 修复 PinToggler 样式。修复了 #451 &nbsp;-&nbsp;@soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/451 中修复 [<samp>(42b12)</samp>](https://github.com/soybeanjs/soybean-admin/commit/42b121a)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **组件**: 提高 TableColumnSetting 的拖动区域精度,带有动画 &nbsp;-&nbsp;@orangelckc 在 https://github.com/soybeanjs/soybean-admin/issues/465 中优化 [<samp>(2aa85)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2aa85c6)
- **项目**: unocss 边框快捷方式 &nbsp;-&nbsp;@soybeanjs 优化 [<samp>(40d0f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/40d0f8a)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 更新 CHANGELOG &nbsp;-&nbsp;@soybeanjs 更新 [<samp>(87b18)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87b1838)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **其他**:
- 纠正拼写错误 &nbsp;-&nbsp;@orangelckc 在 https://github.com/soybeanjs/soybean-admin/issues/460 中纠正 [<samp>(086ba)</samp>](https://github.com/soybeanjs/soybean-admin/commit/086bad4)
- 纠正拼写错误 &nbsp;-&nbsp;@Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/462 中纠正 [<samp>(f1850)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f185041)
- **项目**:
- 更新 vscode launch.json &nbsp;-&nbsp;@soybeanjs 更新 [<samp>(4c1c7)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4c1c7e6)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;[![orangelckc](https://github.com/orangelckc.png?size=48)](https://github.com/orangelckc)&nbsp;&nbsp;
## [v1.1.2](https://github.com/soybeanjs/soybean-admin/compare/v1.1.1...v1.1.2) (2024-05-24)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **项目**:
- 修复头部样式 & 修复点击全局标签时按钮高亮。修复了 #446 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/446 [<samp>(64fc0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/64fc099)
- 修复多标签页只渲染一次。修复了 #441 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/441 [<samp>(e379d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e379d6c)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**: 优化代码 &nbsp;-&nbsp;@honghuangdc [<samp>(bc8dc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/bc8dc47)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
## [v1.1.1](https://github.com/soybeanjs/soybean-admin/compare/v1.1.0...v1.1.1) (2024-05-20)
### &nbsp;&nbsp;&nbsp;🚀 功能
- **hooks**: 为 useEcharts 添加 setOptions &nbsp;-&nbsp;@honghuangdc 提交 [<samp>(e4d53)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e4d53aa)
### &nbsp;&nbsp;&nbsp;🐞 修复错误
- **projects**:
- 修复 useRouter。修复了 #436 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/436 提交 [<samp>(0774a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/0774a51)
- 在动态路由模式下获取路由时添加错误处理。修复了 440 &nbsp;-&nbsp;@honghuangdc 提交 [<samp>(57b4a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/57b4a9d)
- **styles**:
- 修复 useTable 类型 &nbsp;-&nbsp;@honghuangdc 提交 [<samp>(07124)</samp>](https://github.com/soybeanjs/soybean-admin/commit/071241f)
### &nbsp;&nbsp;&nbsp;📖 文档
- **projects**:
- 更新 CHANGELOG &nbsp;-&nbsp;@honghuangdc 提交 [<samp>(19783)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1978397)
- 更新 README.md &nbsp;-&nbsp;@honghuangdc 提交 [<samp>(fa56e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/fa56e9c)
- 更新 README.md &nbsp;-&nbsp;@honghuangdc 提交 [<samp>(419ea)</samp>](https://github.com/soybeanjs/soybean-admin/commit/419ea42)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **projects**:
- 更新依赖并修复 TS 错误 &nbsp;-&nbsp;@honghuangdc 提交 [<samp>(4ea9c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4ea9c85)
- 更新 eslint-config 并修复代码 &nbsp;-&nbsp;@honghuangdc 提交 [<samp>(68ea9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/68ea974)
- 更新 @elegant-router/vue 并为 resolve route 添加错误处理。修复了 #442 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/442 提交 [<samp>(24ff8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/24ff852)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
## [v1.1.0](https://github.com/honghuangdc/soybean-admin/compare/v1.0.9...v1.1.0) (2024-05-07)
### &nbsp;&nbsp;&nbsp;🚀 功能
- **项目**:
- 支持灰度。修复了 #385 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/385 [<samp>(d335d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d335df6)
- 添加前缀到本地存储 &nbsp;-&nbsp;**Azir** [<samp>(1fc34)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1fc34cc)
- 添加表格显示总数选项 &nbsp;-&nbsp;**paynezhuang** [<samp>(3e61e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3e61eab)
- 添加推荐颜色切换。关闭了 #388 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/388 [<samp>(a1920)</samp>](https://github.com/honghuangdc/soybean-admin/commit/a1920fc)
- 添加菜单路由字段 &nbsp;-&nbsp;**paynezhuang** [<samp>(dbe31)</samp>](https://github.com/honghuangdc/soybean-admin/commit/dbe31eb)
- 支持短时间内重复请求错误只出现一次。关闭了 #368, 关闭了 #369 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/368 和 https://github.com/honghuangdc/soybean-admin/issues/369 [<samp>(e3bd3)</samp>](https://github.com/honghuangdc/soybean-admin/commit/e3bd397)
- 通过鼠标滚轮按钮点击关闭标签 &nbsp;-&nbsp;**JianJroh** [<samp>(d3849)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d3849ba)
- 页面:支持管理菜单更多选项。关闭了 #366 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/366 [<samp>(c4b5c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/c4b5c65)
- useTable 添加展开显示 &nbsp;-&nbsp;**paynezhuang** [<samp>(0a90d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/0a90dd3)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **项目**:
- 菜单 fixedIndexInTab 默认为 null &nbsp;-&nbsp;**paynezhuang** [<samp>(3d10e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3d10ef1)
- 修复菜单切换器 zIndex &nbsp;-&nbsp;@honghuangdc [<samp>(7bd43)</samp>](https://github.com/honghuangdc/soybean-admin/commit/7bd43df)
- 修复管理菜单模态样式 &nbsp;-&nbsp;@honghuangdc [<samp>(60f3b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/60f3b14)
- 当角色改变时修复菜单数据。修复了 #391 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/391 [<samp>(3b47b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3b47b5a)
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**: 移除废弃的代码 &nbsp;-&nbsp;@honghuangdc [<samp>(72ccb)</samp>](https://github.com/honghuangdc/soybean-admin/commit/72ccb6b)
### &nbsp;&nbsp;&nbsp;💅 重构
- **项目**:
- 重构 @sa/color-palette => @sa/color & 优化 @sa/utils 代码 &nbsp;-&nbsp;@honghuangdc [<samp>(34999)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3499997)
- menu-operate-drawer => menu-operate-modal &nbsp;-&nbsp;@honghuangdc [<samp>(003e1)</samp>](https://github.com/honghuangdc/soybean-admin/commit/003e145)
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**:
- 添加 CHANGELOG.zh_CN.md &nbsp;-&nbsp;@honghuangdc [<samp>(18b3f)</samp>](https://github.com/honghuangdc/soybean-admin/commit/18b3f05)
- 更新 CHANGELOG &nbsp;-&nbsp;@honghuangdc [<samp>(4d17c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4d17cfd)
### &nbsp;&nbsp;&nbsp;🏡 杂项
- **依赖**:
- 更新依赖 &nbsp;-&nbsp;@honghuangdc [<samp>(1cb38)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1cb3816)
- 更新依赖 &nbsp;-&nbsp;@honghuangdc [<samp>(599b4)</samp>](https://github.com/honghuangdc/soybean-admin/commit/599b4e1)
- **项目**:
- 合并 main 到 v1.1.0 &nbsp;-&nbsp;@honghuangdc [<samp>(ebe55)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ebe55af)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
[paynezhuang](mailto:paynezhuang@gmail.com),&nbsp;[JianJroh](mailto:rhjian@foxmail.com),&nbsp;[Azir](mailto:2075125282@qq.com)
## [v1.1.0-beta.2](https://github.com/honghuangdc/soybean-admin/compare/v1.1.0-beta.1...v1.1.0-beta.2) (2024-05-07)
### &nbsp;&nbsp;&nbsp;🚀 特性
- **项目**: useTable添加展开以显示 &nbsp;-&nbsp;**paynezhuang** [<samp>(0a90d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/0a90dd3) 提供
### &nbsp;&nbsp;&nbsp;🐞 修复的错误
- **项目**:
- 修复 manage_menu 模态样式 &nbsp;-&nbsp;@honghuangdc [<samp>(60f3b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/60f3b14) 提供
- 当角色改变时修复菜单数据。修复了 #391 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/391 [<samp>(3b47b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3b47b5a) 提供
### &nbsp;&nbsp;&nbsp;🛠 优化
- **项目**: 删除废弃的代码 &nbsp;-&nbsp;@honghuangdc [<samp>(72ccb)</samp>](https://github.com/honghuangdc/soybean-admin/commit/72ccb6b) 提供
### &nbsp;&nbsp;&nbsp;📖 文档
- **项目**: 添加 CHANGELOG.zh_CN.md &nbsp;-&nbsp;@honghuangdc [<samp>(18b3f)</samp>](https://github.com/honghuangdc/soybean-admin/commit/18b3f05) 提供
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
[paynezhuang](mailto:paynezhuang@gmail.com)
## [v1.1.0-beta.1](https://github.com/soybeanjs/soybean-admin/compare/v1.0.9...v1.1.0-beta.1) (2024-05-07)
### &nbsp;&nbsp;&nbsp;🚀 功能
- **项目**:
- 支持灰度。修复了 #385 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/385 [<samp>(d335d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d335df6)
- 添加前缀到本地存储 &nbsp;-&nbsp;**Azir** [<samp>(1fc34)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1fc34cc)
- 添加表格 showTotal 选项 &nbsp;-&nbsp;**paynezhuang** [<samp>(3e61e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3e61eab)
- 添加推荐颜色切换。关闭了 #388 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/388 [<samp>(a1920)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a1920fc)
- 添加菜单路由字段 &nbsp;-&nbsp;**paynezhuang** [<samp>(dbe31)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dbe31eb)
- 支持短时间内重复请求错误只发生一次。关闭了 #368, 关闭了 #369 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/368 和 https://github.com/soybeanjs/soybean-admin/issues/369 [<samp>(e3bd3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e3bd397)
- 通过鼠标滚轮按钮点击关闭标签 &nbsp;-&nbsp;**JianJroh** [<samp>(d3849)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d3849ba)
- 页面:支持更多的 manage_menu 选项。关闭了 #366 &nbsp;-&nbsp;@honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/366 [<samp>(c4b5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c4b5c65)
### &nbsp;&nbsp;&nbsp;🐞 错误修复
- **项目**:
- 修复菜单 fixedIndexInTab 默认为 null &nbsp;-&nbsp;**paynezhuang** [<samp>(3d10e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3d10ef1)完成
- 修复菜单切换器 zIndex &nbsp;-&nbsp;@honghuangdc [<samp>(7bd43)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7bd43df)完成
### &nbsp;&nbsp;&nbsp;💅 重构
- **项目**:
- 重构 @sa/color-palette => @sa/color & 性能优化 @sa/utils &nbsp;-&nbsp;@honghuangdc [<samp>(34999)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3499997)完成
- menu-operate-drawer => menu-operate-modal &nbsp;-&nbsp;@honghuangdc [<samp>(003e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/003e145)完成
### &nbsp;&nbsp;&nbsp;🏡 杂务
- **依赖**:
- 更新依赖 &nbsp;-&nbsp;@honghuangdc [<samp>(1cb38)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1cb3816)完成
- 更新依赖 &nbsp;-&nbsp;@honghuangdc [<samp>(599b4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/599b4e1)完成
- **项目**:
- 合并主分支到 v1.1.0 &nbsp;-&nbsp;@honghuangdc [<samp>(ebe55)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ebe55af)完成
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;
[JianJroh](mailto:rhjian@foxmail.com),&nbsp;[paynezhuang](mailto:paynezhuang@gmail.com),&nbsp;[Azir](mailto:2075125282@qq.com)

View File

@@ -1,25 +1,22 @@
<div align="center">
<img src="./public/favicon.svg" width="160" />
<h1>SoybeanAdmin</h1>
<h1>Soybean Admin</h1>
<span>English | <a href="./README.zh_CN.md">中文</a></span>
</div>
<br />
---
[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) ![](https://img.shields.io/github/stars/soybeanjs/soybean-admin) ![](https://img.shields.io/github/forks/soybeanjs/soybean-admin)
[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
[![github stars](https://img.shields.io/github/stars/soybeanjs/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
[![github forks](https://img.shields.io/github/forks/soybeanjs/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)
<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>
>[!CAUTION]
> the old version of `Soybean Admin` is moved to branch [legacy](https://github.com/soybeanjs/soybean-admin/tree/legacy). It is recommended to use the latest version of `Soybean Admin`.
> [!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!
> If you think `Soybean Admin` 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!
## Introduction
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) is a clean, elegant, beautiful and powerful admin template, based on the latest front-end technology stack, including Vue3, Vite5, TypeScript, Pinia and UnoCSS. It has built-in rich theme configuration and components, strict code specifications, and an automated file routing system. In addition, it also uses the online mock data solution based on ApiFox. `SoybeanAdmin` provides you with a one-stop admin solution, no additional configuration, and out of the box. It is also a best practice for learning cutting-edge technologies quickly.
[`Soybean Admin`](https://github.com/soybeanjs/soybean-admin) is a clean, elegant, beautiful and powerful admin template, based on the latest front-end technology stack, including Vue3, Vite5, TypeScript, Pinia and UnoCSS. It has built-in rich theme configuration and components, strict code specifications, and an automated file routing system. In addition, it also uses the online mock data solution based on ApiFox. `Soybean Admin` provides you with a one-stop admin solution, no additional configuration, and out of the box. It is also a best practice for learning cutting-edge technologies quickly.
## Features
@@ -81,8 +78,9 @@
Make sure your environment meets the following requirements:
- **git**: you need git to clone and manage project versions.
- **NodeJS**: >=18.12.0, recommended 18.19.0 or higher.
- **pnpm**: >= 8.7.0, recommended 8.14.0 or higher.
- **NodeJS**: >=18.0.0, recommended 18.19.0 or higher.
> You can use [fnm](https://github.com/Schniz/fnm) to manage your NodeJS version, [installation tutorial](https://juejin.cn/post/7113462239734022158).
- **pnpm**: >= 8.0.0, recommended 8.14.0 or higher.
**Clone Project**
@@ -109,25 +107,13 @@ pnpm dev
pnpm build
```
## Ecosystem
- [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).
- [MalusAdmin](https://github.com/pridejoy/MalusAdmin): A backend management framework developed based on Vue3/TypeScript/NaiveUI and NET7 & Sqlsugar. It is implemented in the most original and simplest way, with a fresh and elegant front-end, a clear and elegant backend structure, and powerful functions.
- [PanisAdmin](https://github.com/paynezhuang/panis-admin): Adopting SpringBoot 3, SaToken, MySQL and other frameworks to develop and modify [soybean-admin](https://github.com/soybeanjs/soybean-admin) for the second time, adapting dynamic menu/button-level authorization. Retaining the original flavor, fresh and elegant, high-value back-end management system scaffold.
- [snail-job](https://github.com/aizuda/snail-job): A distributed task retry and task scheduling platform with "high performance, high value and high activity".
- [SuperApi](https://github.com/TmmTop/SuperApi): Quickly turn your idea into an online stable product! Entity-less library and table building, add, delete, change and check entity-less library table, support 15 kinds of condition query, as well as paging, list, unlimited tree list and other functions of the API deployment! With interface documentation, Auth authorisation, interface flow restriction, access to the client's real IP, advanced server caching components, dynamic APIs and other features, we look forward to your experience!
- [FastSoyAdmin](https://github.com/sleep1223/fast-soy-admin): A modern Management Platform based on FastAPI+Vue3+Naive UI.
## How to Contribute
We warmly welcome and appreciate all forms of contributions. If you have any ideas or suggestions, please feel free to share them by submitting [pull requests](https://github.com/soybeanjs/soybean-admin/pulls) or creating GitHub [issue](https://github.com/soybeanjs/soybean-admin/issues/new).
## Git Commit Guidelines
This project has built-in `commit` command, you can execute `pnpm commit` to generate commit information that conforms to [Conventional Commits](https://www.conventionalcommits.org/) specification. When submitting PR, please be sure to use `commit` command to create commit information to ensure the standardization of information.
This project has built-in `commit` command, you can execute `pnpm commit` to generate commit information that conforms to [Conventional Commits](conventionalcommits) specification. When submitting PR, please be sure to use `commit` command to create commit information to ensure the standardization of information.
## Browser Support
@@ -151,7 +137,7 @@ Thanks the following people for their contributions. If you want to contribute t
## 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.
`Soybean Admin` 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>
@@ -159,15 +145,15 @@ Thanks the following people for their contributions. If you want to contribute t
</div>
<div>
<p>WeChat Group</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/picgo/soybean-admin-wechat-0620.jpg" style="width:200px" />
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-0402.jpg" style="width:200px" />
</div>
<div>
<p>Add the following WeChat to invite to the WeChat group</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybeanjs.jpg" style="width:200px" />
</div>
<div>
<!-- <div>
<p>Add Soybean's WeChat for business consultation, cooperation, project architecture, one-on-one guidance, etc.</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybean.jpg" style="width:200px" />
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybean.jpg" style="width:200px" /> -->
</div>
## Star Trend

View File

@@ -1,24 +1,26 @@
<div align="center">
<img src="./public/favicon.svg" width="160" />
<h1>SoybeanAdmin</h1>
<span><a href="./README.md">English</a> | 中文</span>
<h1>Soybean Admin</h1>
<span><a href="./README.zh_CN.md">English</a> | 中文</span>
</div>
<br />
---
[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) ![](https://img.shields.io/github/stars/soybeanjs/soybean-admin) ![](https://img.shields.io/github/forks/soybeanjs/soybean-admin)
[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
[![github stars](https://img.shields.io/github/stars/honghuangdc/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
[![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)
<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>
>[!CAUTION]
> 旧版本的 `Soybean Admin` 已经移动到分支 [legacy](https://github.com/soybeanjs/soybean-admin/tree/legacy)。建议使用最新版本的 `Soybean Admin`。
> [!NOTE]
> 如果您觉得 `SoybeanAdmin`对您有所帮助,或者您喜欢我们的项目,请在 GitHub 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持!
> 如果您觉得 `Soybean Admin`对您有所帮助,或者您喜欢我们的项目,请在 GitHub 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持!
<br />
[![Star History Chart](https://api.star-history.com/svg?repos=soybeanjs/soybean-admin&type=Date)](https://star-history.com/#soybeanjs/soybean-admin&Date)
## 简介
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件代码规范严谨实现了自动化的文件路由系统。此外它还采用了基于 ApiFox 的在线Mock数据方案。`SoybeanAdmin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
[`Soybean Admin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件代码规范严谨实现了自动化的文件路由系统。此外它还采用了基于 ApiFox 的在线Mock数据方案。`Soybean Admin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
## 特性
@@ -79,8 +81,9 @@
确保你的环境满足以下要求:
- **git**: 你需要git来克隆和管理项目版本。
- **NodeJS**: >=18.12.0,推荐 18.19.0 或更高。
- **pnpm**: >= 8.7.0,推荐 8.14.0 或更高
- **NodeJS**: >=18.0.0,推荐 18.19.0 或更高。
> 你可以使用 [fnm](https://github.com/Schniz/fnm) 来管理你的NodeJS版本[安装教程](https://juejin.cn/post/7113462239734022158)
- **pnpm**: >= 8.0.0,推荐 8.14.0 或更高。
**克隆项目**
@@ -107,25 +110,13 @@ pnpm dev
pnpm build
```
## 周边生态
- [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) 开发的简单权限系统。
- [MalusAdmin](https://github.com/pridejoy/MalusAdmin): 基于 Vue3/TypeScript/NaiveUI 和 NET7 & Sqlsugar 开发的后台管理框架。采用最原生最简洁的方式来实现, 前端清新优雅高颜值,后端 结构清晰,优雅易懂,功能强大。
- [PanisAdmin](https://github.com/paynezhuang/panis-admin): 采用SpringBoot3、SaToken、MySQL等框架开发二次修改 [soybean-admin](https://github.com/soybeanjs/soybean-admin),适配动态菜单/按钮级别的鉴权,保留原汁原味、清新优雅、高颜值的后台管理系统脚手架。
- [snail-job](https://github.com/aizuda/snail-job): 一款兼具 “高性能、高颜值、高活跃” 的分布式任务重试和分布式任务调度平台。
- [SuperApi](https://github.com/TmmTop/SuperApi): 快速将你的 idea 变成线上稳定运行的产品! 无实体建库建表,对无实体库表进行增删改查,支持 15 种条件查询,以及分页,列表,无限级树形列表 等功能的 API 部署! 拥有接口文档Auth 授权,接口限流,获取客户端真实 IP先进的服务器缓存组件动态 API 等功能,期待您的体验!
- [FastSoyAdmin](https://github.com/sleep1223/fast-soy-admin): 基于 FastAPI+Vue3+Naive UI 的现代化轻量管理平台.
## 如何贡献
我们热烈欢迎并感谢所有形式的贡献。如果您有任何想法或建议,欢迎通过提交 [pull requests](https://github.com/soybeanjs/soybean-admin/pulls) 或创建 GitHub [issue](https://github.com/soybeanjs/soybean-admin/issues/new) 来分享。
## Git 提交规范
本项目已内置 `commit` 命令,您可以通过执行 `pnpm commit` 来生成符合 [Conventional Commits]([conventionalcommits](https://www.conventionalcommits.org/)) 规范的提交信息。在提交PR时请务必使用 `commit` 命令来创建提交信息,以确保信息的规范性。
本项目已内置 `commit` 命令,您可以通过执行 `pnpm commit` 来生成符合 [Conventional Commits](conventionalcommits) 规范的提交信息。在提交PR时请务必使用 `commit` 命令来创建提交信息,以确保信息的规范性。
## 浏览器支持
@@ -140,7 +131,6 @@ pnpm build
[Soybean](https://github.com/honghuangdc)
## 贡献者
感谢以下贡献者的贡献。如果您想为本项目做出贡献,请参考 [如何贡献](#如何贡献)。
@@ -151,16 +141,16 @@ pnpm build
## 交流
`SoybeanAdmin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。
`Soybean Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。
<div>
<p>QQ交流群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-2.jpg" style="width:200px" />
</div>
<div>
<!-- <div>
<p>微信群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/picgo/soybean-admin-wechat-0620.jpg" style="width:200px" />
</div>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-0402.jpg" style="width:200px" />
</div> -->
<div>
<p>添加下面微信邀请进微信群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybeanjs.jpg" style="width:200px" />

View File

@@ -1,2 +1 @@
export * from './proxy';
export * from './time';

View File

@@ -1,12 +0,0 @@
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
export function getBuildTime() {
dayjs.extend(utc);
dayjs.extend(timezone);
const buildTime = dayjs.tz(Date.now(), 'Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss');
return buildTime;
}

View File

@@ -1,13 +0,0 @@
import type { Plugin } from 'vite';
export function setupHtmlPlugin(buildTime: string) {
const plugin: Plugin = {
name: 'html-plugin',
apply: 'build',
transformIndexHtml(html) {
return html.replace('<head>', `<head>\n <meta name="buildTime" content="${buildTime}">`);
}
};
return plugin;
}

View File

@@ -6,9 +6,8 @@ import progress from 'vite-plugin-progress';
import { setupElegantRouter } from './router';
import { setupUnocss } from './unocss';
import { setupUnplugin } from './unplugin';
import { setupHtmlPlugin } from './html';
export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) {
export function setupVitePlugins(viteEnv: Env.ImportMeta) {
const plugins: PluginOption = [
vue({
script: {
@@ -20,8 +19,7 @@ export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) {
setupElegantRouter(),
setupUnocss(viteEnv),
...setupUnplugin(viteEnv),
progress(),
setupHtmlPlugin(buildTime)
progress()
];
return plugins;

View File

@@ -9,18 +9,7 @@ export function setupElegantRouter() {
blank: 'src/layouts/blank-layout/index.vue'
},
customRoutes: {
names: [
'exception_403',
'exception_404',
'exception_500',
'document_project',
'document_project-link',
'document_vue',
'document_vite',
'document_unocss',
'document_naive',
'document_antd'
]
names: ['exception_403', 'exception_404', 'exception_500']
},
routePathTransformer(routeName, routePath) {
const key = routeName as RouteKey;

View File

@@ -1,13 +1,13 @@
import { defineConfig } from '@soybeanjs/eslint-config';
export default defineConfig(
{ vue: true, unocss: true },
{ vue: true, unocss: true, ignores: ['src-tauri/target'] },
{
rules: {
'vue/multi-word-component-names': [
'warn',
{
ignores: ['index', 'App', 'Register', '[id]', '[url]']
ignores: ['index', 'App', '[id]']
}
],
'vue/component-name-in-template-casing': [
@@ -18,7 +18,7 @@ export default defineConfig(
ignores: ['/^icon-/']
}
],
'unocss/order-attributify': 'off'
'order-attributify': 'off'
}
}
);

View File

@@ -4,7 +4,6 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<title>%VITE_APP_TITLE%</title>
</head>
<body>

View File

@@ -1,7 +1,8 @@
{
"name": "soybean-admin",
"type": "module",
"version": "1.2.5",
"version": "1.0.2",
"packageManager": "pnpm@8.15.6",
"description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
"author": {
"name": "Soybean",
@@ -26,79 +27,78 @@
"ant-design-vue v4",
"UnoCSS"
],
"engines": {
"node": ">=18.12.0",
"pnpm": ">=8.7.0"
},
"scripts": {
"build": "vite build --mode prod",
"build:tauri": "pnpm tauri build",
"build:test": "vite build --mode test",
"cleanup": "sa cleanup",
"commit": "sa git-commit",
"dev": "vite --mode test",
"dev:prod": "vite --mode prod",
"dev:tauri": "pnpm tauri dev",
"gen-route": "sa gen-route",
"lint": "eslint . --fix",
"prepare": "simple-git-hooks",
"preview": "vite preview",
"release": "sa release",
"tauri-icon": "pnpm tauri icon ./public/logo.png",
"typecheck": "vue-tsc --noEmit --skipLibCheck",
"update-pkg": "sa update-pkg"
},
"dependencies": {
"@better-scroll/core": "2.5.1",
"@iconify/vue": "4.1.2",
"@iconify/vue": "4.1.1",
"@sa/axios": "workspace:*",
"@sa/color": "workspace:*",
"@sa/color-palette": "workspace:*",
"@sa/hooks": "workspace:*",
"@sa/materials": "workspace:*",
"@sa/utils": "workspace:*",
"@vueuse/core": "10.11.0",
"@vueuse/core": "10.9.0",
"clipboard": "2.0.11",
"dayjs": "1.11.11",
"dayjs": "1.11.10",
"echarts": "5.5.0",
"lodash-es": "4.17.21",
"naive-ui": "2.38.2",
"naive-ui": "2.38.1",
"nprogress": "0.2.0",
"pinia": "2.1.7",
"tailwind-merge": "2.3.0",
"vue": "3.4.29",
"vue-draggable-plus": "0.5.0",
"vue-i18n": "9.13.1",
"vue-router": "4.3.3"
"vue": "3.4.21",
"vue-draggable-plus": "0.4.0",
"vue-i18n": "9.11.0",
"vue-router": "4.3.0"
},
"devDependencies": {
"@elegant-router/vue": "0.3.7",
"@iconify/json": "2.2.219",
"@elegant-router/vue": "0.3.6",
"@iconify/json": "2.2.198",
"@sa/scripts": "workspace:*",
"@sa/uno-preset": "workspace:*",
"@soybeanjs/eslint-config": "1.3.7",
"@soybeanjs/eslint-config": "1.2.5",
"@tauri-apps/cli": "1.5.11",
"@types/lodash-es": "4.17.12",
"@types/node": "20.14.2",
"@types/node": "20.12.5",
"@types/nprogress": "0.2.3",
"@unocss/eslint-config": "0.61.0",
"@unocss/preset-icons": "0.61.0",
"@unocss/preset-uno": "0.61.0",
"@unocss/transformer-directives": "0.61.0",
"@unocss/transformer-variant-group": "0.61.0",
"@unocss/vite": "0.61.0",
"@vitejs/plugin-vue": "5.0.5",
"@vitejs/plugin-vue-jsx": "4.0.0",
"eslint": "9.4.0",
"eslint-plugin-vue": "9.26.0",
"lint-staged": "15.2.7",
"sass": "1.77.5",
"@unocss/eslint-config": "0.59.0",
"@unocss/preset-icons": "0.59.0",
"@unocss/preset-uno": "0.59.0",
"@unocss/transformer-directives": "0.59.0",
"@unocss/transformer-variant-group": "0.59.0",
"@unocss/vite": "0.59.0",
"@vitejs/plugin-vue": "5.0.4",
"@vitejs/plugin-vue-jsx": "3.1.0",
"eslint": "8.57.0",
"eslint-plugin-vue": "9.24.0",
"lint-staged": "15.2.2",
"sass": "1.74.1",
"simple-git-hooks": "2.11.1",
"tsx": "4.15.4",
"typescript": "5.4.5",
"unplugin-icons": "0.19.0",
"unplugin-vue-components": "0.27.0",
"vite": "5.3.1",
"tsx": "4.7.2",
"typescript": "5.4.4",
"unplugin-icons": "0.18.5",
"unplugin-vue-components": "0.26.0",
"vite": "5.2.8",
"vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "7.2.1",
"vue-eslint-parser": "9.4.3",
"vue-tsc": "2.0.21"
"vite-plugin-vue-devtools": "7.0.25",
"vue-eslint-parser": "9.4.2",
"vue-tsc": "2.0.11"
},
"simple-git-hooks": {
"commit-msg": "pnpm sa git-commit-verify",

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/axios",
"version": "1.2.5",
"version": "1.0.2",
"exports": {
".": "./src/index.ts"
},
@@ -11,11 +11,11 @@
},
"dependencies": {
"@sa/utils": "workspace:*",
"axios": "1.7.2",
"axios-retry": "4.4.0",
"qs": "6.12.1"
"axios": "1.6.8",
"axios-retry": "4.1.0",
"qs": "6.12.0"
},
"devDependencies": {
"@types/qs": "6.9.15"
"@types/qs": "6.9.14"
}
}

View File

@@ -48,9 +48,7 @@ function createCommonRequest<ResponseData = any>(
instance.interceptors.response.use(
async response => {
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
if (responseType !== 'json' || opts.isBackendSuccess(response)) {
if (opts.isBackendSuccess(response)) {
return Promise.resolve(response);
}
@@ -129,7 +127,6 @@ export function createRequest<ResponseData = any, State = Record<string, unknown
request.cancelRequest = cancelRequest;
request.cancelAllRequest = cancelAllRequest;
request.state = {} as State;
return request;
}

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/color",
"version": "1.2.5",
"name": "@sa/color-palette",
"version": "1.0.2",
"exports": {
".": "./src/index.ts"
},
@@ -10,7 +10,6 @@
}
},
"dependencies": {
"@sa/utils": "workspace:*",
"colord": "2.9.3"
}
}

View File

@@ -0,0 +1,29 @@
import { colord, extend } from 'colord';
import type { HslColor } from 'colord';
import labPlugin from 'colord/plugins/lab';
extend([labPlugin]);
export function isValidColor(color: string) {
return colord(color).isValid();
}
export function getHex(color: string) {
return colord(color).toHex();
}
export function getRgb(color: string) {
return colord(color).toRgb();
}
export function getHsl(color: string) {
return colord(color).toHsl();
}
export function getDeltaE(color1: string, color2: string) {
return colord(color1).delta(color2);
}
export function transformHslToHex(color: HslColor) {
return colord(color).toHex();
}

View File

@@ -0,0 +1,56 @@
import { getColorPaletteFamily } from './palette';
import { getColorName } from './name';
import type { ColorPalette, ColorPaletteFamily, ColorPaletteItem, ColorPaletteNumber } from './type';
import defaultPalettes from './json/palette.json';
/**
* Get color palette by provided color and color name
*
* @param color The provided color
* @param colorName Color name
*/
export function getColorPalette(color: string, colorName: string) {
const colorPaletteFamily = getColorPaletteFamily(color, colorName);
const colorMap = new Map<ColorPaletteNumber, ColorPaletteItem>();
colorPaletteFamily.palettes.forEach(palette => {
colorMap.set(palette.number, palette);
});
const mainColor = colorMap.get(500) as ColorPaletteItem;
const matchColor = colorPaletteFamily.palettes.find(palette => palette.hexcode === color) as ColorPaletteItem;
const colorPalette: ColorPalette = {
...colorPaletteFamily,
colorMap,
main: mainColor,
match: matchColor
};
return colorPalette;
}
/**
* Get color by color palette number
*
* @param color Color
* @param num Color palette number
* @returns Color hexcode
*/
export function getColorByColorPaletteNumber(color: string, num: ColorPaletteNumber) {
const colorPalette = getColorPalette(color, color);
const colorItem = colorPalette.colorMap.get(num) as ColorPaletteItem;
return colorItem.hexcode;
}
export default getColorPalette;
/** The builtin color palettes */
const colorPalettes = defaultPalettes as ColorPaletteFamily[];
export { getColorName, colorPalettes };
export type { ColorPalette, ColorPaletteNumber, ColorPaletteItem, ColorPaletteFamily };

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,274 @@
[
{
"key": "red",
"palettes": [
{ "hexcode": "#fef2f2", "number": 50, "name": "Bridesmaid" },
{ "hexcode": "#fee2e2", "number": 100, "name": "Pippin" },
{ "hexcode": "#fecaca", "number": 200, "name": "Your Pink" },
{ "hexcode": "#fca5a5", "number": 300, "name": "Cornflower Lilac" },
{ "hexcode": "#f87171", "number": 400, "name": "Bittersweet" },
{ "hexcode": "#ef4444", "number": 500, "name": "Cinnabar" },
{ "hexcode": "#dc2626", "number": 600, "name": "Persian Red" },
{ "hexcode": "#b91c1c", "number": 700, "name": "Thunderbird" },
{ "hexcode": "#991b1b", "number": 800, "name": "Old Brick" },
{ "hexcode": "#7f1d1d", "number": 900, "name": "Falu Red" },
{ "hexcode": "#450a0a", "number": 950, "name": "Mahogany" }
]
},
{
"key": "orange",
"palettes": [
{ "hexcode": "#fff7ed", "number": 50, "name": "Serenade" },
{ "hexcode": "#ffedd5", "number": 100, "name": "Derby" },
{ "hexcode": "#fed7aa", "number": 200, "name": "Caramel" },
{ "hexcode": "#fdba74", "number": 300, "name": "Macaroni and Cheese" },
{ "hexcode": "#fb923c", "number": 400, "name": "Neon Carrot" },
{ "hexcode": "#f97316", "number": 500, "name": "Ecstasy" },
{ "hexcode": "#ea580c", "number": 600, "name": "Trinidad" },
{ "hexcode": "#c2410c", "number": 700, "name": "Tia Maria" },
{ "hexcode": "#9a3412", "number": 800, "name": "Tabasco" },
{ "hexcode": "#7c2d12", "number": 900, "name": "Pueblo" },
{ "hexcode": "#431407", "number": 950, "name": "Rebel" }
]
},
{
"key": "amber",
"palettes": [
{ "hexcode": "#fffbeb", "number": 50, "name": "Island Spice" },
{ "hexcode": "#fef3c7", "number": 100, "name": "Beeswax" },
{ "hexcode": "#fde68a", "number": 200, "name": "Sweet Corn" },
{ "hexcode": "#fcd34d", "number": 300, "name": "Mustard" },
{ "hexcode": "#fbbf24", "number": 400, "name": "Lightning Yellow" },
{ "hexcode": "#f59e0b", "number": 500, "name": "California" },
{ "hexcode": "#d97706", "number": 600, "name": "Christine" },
{ "hexcode": "#b45309", "number": 700, "name": "Vesuvius" },
{ "hexcode": "#92400e", "number": 800, "name": "Korma" },
{ "hexcode": "#78350f", "number": 900, "name": "Copper Canyon" },
{ "hexcode": "#451a03", "number": 950, "name": "Brown Pod" }
]
},
{
"key": "yellow",
"palettes": [
{ "hexcode": "#fefce8", "number": 50, "name": "Orange White" },
{ "hexcode": "#fef9c3", "number": 100, "name": "Lemon Chiffon" },
{ "hexcode": "#fef08a", "number": 200, "name": "Sweet Corn" },
{ "hexcode": "#fde047", "number": 300, "name": "Bright Sun" },
{ "hexcode": "#facc15", "number": 400, "name": "Candlelight" },
{ "hexcode": "#eab308", "number": 500, "name": "Corn" },
{ "hexcode": "#ca8a04", "number": 600, "name": "Pirate Gold" },
{ "hexcode": "#a16207", "number": 700, "name": "Mai Tai" },
{ "hexcode": "#854d0e", "number": 800, "name": "Korma" },
{ "hexcode": "#713f12", "number": 900, "name": "Sepia" },
{ "hexcode": "#422006", "number": 950, "name": "Dark Ebony" }
]
},
{
"key": "lime",
"palettes": [
{ "hexcode": "#f7fee7", "number": 50, "name": "Spring Sun" },
{ "hexcode": "#ecfccb", "number": 100, "name": "Chiffon" },
{ "hexcode": "#d9f99d", "number": 200, "name": "Gossip" },
{ "hexcode": "#bef264", "number": 300, "name": "Sulu" },
{ "hexcode": "#a3e635", "number": 400, "name": "Conifer" },
{ "hexcode": "#84cc16", "number": 500, "name": "Lima" },
{ "hexcode": "#65a30d", "number": 600, "name": "Christi" },
{ "hexcode": "#4d7c0f", "number": 700, "name": "Green Leaf" },
{ "hexcode": "#3f6212", "number": 800, "name": "Dell" },
{ "hexcode": "#365314", "number": 900, "name": "Clover" },
{ "hexcode": "#1a2e05", "number": 950, "name": "Deep Forest Green" }
]
},
{
"key": "green",
"palettes": [
{ "hexcode": "#f0fdf4", "number": 50, "name": "Ottoman" },
{ "hexcode": "#dcfce7", "number": 100, "name": "Blue Romance" },
{ "hexcode": "#bbf7d0", "number": 200, "name": "Magic Mint" },
{ "hexcode": "#86efac", "number": 300, "name": "Algae Green" },
{ "hexcode": "#4ade80", "number": 400, "name": "Emerald" },
{ "hexcode": "#22c55e", "number": 500, "name": "Malachite" },
{ "hexcode": "#16a34a", "number": 600, "name": "Salem" },
{ "hexcode": "#15803d", "number": 700, "name": "Jewel" },
{ "hexcode": "#166534", "number": 800, "name": "Jewel" },
{ "hexcode": "#14532d", "number": 900, "name": "Green Pea" },
{ "hexcode": "#052e16", "number": 950, "name": "English Holly" }
]
},
{
"key": "emerald",
"palettes": [
{ "hexcode": "#ecfdf5", "number": 50, "name": "White Ice" },
{ "hexcode": "#d1fae5", "number": 100, "name": "Granny Apple" },
{ "hexcode": "#a7f3d0", "number": 200, "name": "Magic Mint" },
{ "hexcode": "#6ee7b7", "number": 300, "name": "Bermuda" },
{ "hexcode": "#34d399", "number": 400, "name": "Shamrock" },
{ "hexcode": "#10b981", "number": 500, "name": "Mountain Meadow" },
{ "hexcode": "#059669", "number": 600, "name": "Green Haze" },
{ "hexcode": "#047857", "number": 700, "name": "Watercourse" },
{ "hexcode": "#065f46", "number": 800, "name": "Watercourse" },
{ "hexcode": "#064e3b", "number": 900, "name": "Evening Sea" },
{ "hexcode": "#022c22", "number": 950, "name": "Burnham" }
]
},
{
"key": "teal",
"palettes": [
{ "hexcode": "#f0fdfa", "number": 50, "name": "White Ice" },
{ "hexcode": "#ccfbf1", "number": 100, "name": "Scandal" },
{ "hexcode": "#99f6e4", "number": 200, "name": "Ice Cold" },
{ "hexcode": "#5eead4", "number": 300, "name": "Turquoise Blue" },
{ "hexcode": "#2dd4bf", "number": 400, "name": "Turquoise" },
{ "hexcode": "#14b8a6", "number": 500, "name": "Java" },
{ "hexcode": "#0d9488", "number": 600, "name": "Blue Chill" },
{ "hexcode": "#0f766e", "number": 700, "name": "Genoa" },
{ "hexcode": "#115e59", "number": 800, "name": "Eden" },
{ "hexcode": "#134e4a", "number": 900, "name": "Eden" },
{ "hexcode": "#042f2e", "number": 950, "name": "Tiber" }
]
},
{
"key": "cyan",
"palettes": [
{ "hexcode": "#ecfeff", "number": 50, "name": "Bubbles" },
{ "hexcode": "#cffafe", "number": 100, "name": "Oyster Bay" },
{ "hexcode": "#a5f3fc", "number": 200, "name": "Anakiwa" },
{ "hexcode": "#67e8f9", "number": 300, "name": "Spray" },
{ "hexcode": "#22d3ee", "number": 400, "name": "Bright Turquoise" },
{ "hexcode": "#06b6d4", "number": 500, "name": "Cerulean" },
{ "hexcode": "#0891b2", "number": 600, "name": "Bondi Blue" },
{ "hexcode": "#0e7490", "number": 700, "name": "Blue Chill" },
{ "hexcode": "#155e75", "number": 800, "name": "Blumine" },
{ "hexcode": "#164e63", "number": 900, "name": "Chathams Blue" },
{ "hexcode": "#083344", "number": 950, "name": "Tarawera" }
]
},
{
"key": "sky",
"palettes": [
{ "hexcode": "#f0f9ff", "number": 50, "name": "Alice Blue" },
{ "hexcode": "#e0f2fe", "number": 100, "name": "Pattens Blue" },
{ "hexcode": "#bae6fd", "number": 200, "name": "French Pass" },
{ "hexcode": "#7dd3fc", "number": 300, "name": "Malibu" },
{ "hexcode": "#38bdf8", "number": 400, "name": "Picton Blue" },
{ "hexcode": "#0ea5e9", "number": 500, "name": "Cerulean" },
{ "hexcode": "#0284c7", "number": 600, "name": "Lochmara" },
{ "hexcode": "#0369a1", "number": 700, "name": "Bahama Blue" },
{ "hexcode": "#075985", "number": 800, "name": "Venice Blue" },
{ "hexcode": "#0c4a6e", "number": 900, "name": "Chathams Blue" },
{ "hexcode": "#082f49", "number": 950, "name": "Blue Whale" }
]
},
{
"key": "blue",
"palettes": [
{ "hexcode": "#eff6ff", "number": 50, "name": "Zumthor" },
{ "hexcode": "#dbeafe", "number": 100, "name": "Hawkes Blue" },
{ "hexcode": "#bfdbfe", "number": 200, "name": "Tropical Blue" },
{ "hexcode": "#93c5fd", "number": 300, "name": "Malibu" },
{ "hexcode": "#60a5fa", "number": 400, "name": "Cornflower Blue" },
{ "hexcode": "#3b82f6", "number": 500, "name": "Dodger Blue" },
{ "hexcode": "#2563eb", "number": 600, "name": "Royal Blue" },
{ "hexcode": "#1d4ed8", "number": 700, "name": "Cerulean Blue" },
{ "hexcode": "#1e40af", "number": 800, "name": "Persian Blue" },
{ "hexcode": "#1e3a8a", "number": 900, "name": "Bay of Many" },
{ "hexcode": "#172554", "number": 950, "name": "Bunting" }
]
},
{
"key": "indigo",
"palettes": [
{ "hexcode": "#eef2ff", "number": 50, "name": "Zircon" },
{ "hexcode": "#e0e7ff", "number": 100, "name": "Hawkes Blue" },
{ "hexcode": "#c7d2fe", "number": 200, "name": "Periwinkle" },
{ "hexcode": "#a5b4fc", "number": 300, "name": "Perano" },
{ "hexcode": "#818cf8", "number": 400, "name": "Portage" },
{ "hexcode": "#6366f1", "number": 500, "name": "Royal Blue" },
{ "hexcode": "#4f46e5", "number": 600, "name": "Royal Blue" },
{ "hexcode": "#4338ca", "number": 700, "name": "Governor Bay" },
{ "hexcode": "#3730a3", "number": 800, "name": "Governor Bay" },
{ "hexcode": "#312e81", "number": 900, "name": "Minsk" },
{ "hexcode": "#1e1b4b", "number": 950, "name": "Port Gore" }
]
},
{
"key": "violet",
"palettes": [
{ "hexcode": "#f5f3ff", "number": 50, "name": "Titan White" },
{ "hexcode": "#ede9fe", "number": 100, "name": "Titan White" },
{ "hexcode": "#ddd6fe", "number": 200, "name": "Fog" },
{ "hexcode": "#c4b5fd", "number": 300, "name": "Melrose" },
{ "hexcode": "#a78bfa", "number": 400, "name": "Dull Lavender" },
{ "hexcode": "#8b5cf6", "number": 500, "name": "Medium Purple" },
{ "hexcode": "#7c3aed", "number": 600, "name": "Purple Heart" },
{ "hexcode": "#6d28d9", "number": 700, "name": "Purple Heart" },
{ "hexcode": "#5b21b6", "number": 800, "name": "Purple Heart" },
{ "hexcode": "#4c1d95", "number": 900, "name": "Daisy Bush" },
{ "hexcode": "#2e1065", "number": 950, "name": "Violent Violet" }
]
},
{
"key": "purple",
"palettes": [
{ "hexcode": "#faf5ff", "number": 50, "name": "Magnolia" },
{ "hexcode": "#f3e8ff", "number": 100, "name": "Blue Chalk" },
{ "hexcode": "#e9d5ff", "number": 200, "name": "Blue Chalk" },
{ "hexcode": "#d8b4fe", "number": 300, "name": "Mauve" },
{ "hexcode": "#c084fc", "number": 400, "name": "Heliotrope" },
{ "hexcode": "#a855f7", "number": 500, "name": "Medium Purple" },
{ "hexcode": "#9333ea", "number": 600, "name": "Electric Violet" },
{ "hexcode": "#7e22ce", "number": 700, "name": "Purple Heart" },
{ "hexcode": "#6b21a8", "number": 800, "name": "Seance" },
{ "hexcode": "#581c87", "number": 900, "name": "Daisy Bush" },
{ "hexcode": "#3b0764", "number": 950, "name": "Christalle" }
]
},
{
"key": "fuchsia",
"palettes": [
{ "hexcode": "#fdf4ff", "number": 50, "name": "White Pointer" },
{ "hexcode": "#fae8ff", "number": 100, "name": "White Pointer" },
{ "hexcode": "#f5d0fe", "number": 200, "name": "Mauve" },
{ "hexcode": "#f0abfc", "number": 300, "name": "Mauve" },
{ "hexcode": "#e879f9", "number": 400, "name": "Heliotrope" },
{ "hexcode": "#d946ef", "number": 500, "name": "Heliotrope" },
{ "hexcode": "#c026d3", "number": 600, "name": "Fuchsia Pink" },
{ "hexcode": "#a21caf", "number": 700, "name": "Violet Eggplant" },
{ "hexcode": "#86198f", "number": 800, "name": "Seance" },
{ "hexcode": "#701a75", "number": 900, "name": "Seance" },
{ "hexcode": "#4a044e", "number": 950, "name": "Clairvoyant" }
]
},
{
"key": "pink",
"palettes": [
{ "hexcode": "#fdf2f8", "number": 50, "name": "Wisp Pink" },
{ "hexcode": "#fce7f3", "number": 100, "name": "Carousel Pink" },
{ "hexcode": "#fbcfe8", "number": 200, "name": "Classic Rose" },
{ "hexcode": "#f9a8d4", "number": 300, "name": "Lavender Pink" },
{ "hexcode": "#f472b6", "number": 400, "name": "Persian Pink" },
{ "hexcode": "#ec4899", "number": 500, "name": "Brilliant Rose" },
{ "hexcode": "#db2777", "number": 600, "name": "Cerise" },
{ "hexcode": "#be185d", "number": 700, "name": "Maroon Flush" },
{ "hexcode": "#9d174d", "number": 800, "name": "Disco" },
{ "hexcode": "#831843", "number": 900, "name": "Disco" },
{ "hexcode": "#500724", "number": 950, "name": "Cab Sav" }
]
},
{
"key": "rose",
"palettes": [
{ "hexcode": "#fff1f2", "number": 50, "name": "Lavender blush" },
{ "hexcode": "#ffe4e6", "number": 100, "name": "Cosmos" },
{ "hexcode": "#fecdd3", "number": 200, "name": "Pastel Pink" },
{ "hexcode": "#fda4af", "number": 300, "name": "Sweet Pink" },
{ "hexcode": "#fb7185", "number": 400, "name": "Froly" },
{ "hexcode": "#f43f5e", "number": 500, "name": "Radical Red" },
{ "hexcode": "#e11d48", "number": 600, "name": "Amaranth" },
{ "hexcode": "#be123c", "number": 700, "name": "Cardinal" },
{ "hexcode": "#9f1239", "number": 800, "name": "Shiraz" },
{ "hexcode": "#881337", "number": 900, "name": "Claret" },
{ "hexcode": "#4c0519", "number": 950, "name": "Cab Sav" }
]
}
]

View File

@@ -1,11 +1,6 @@
import { colorNames } from '../constant';
import { getHex, getHsl, getRgb } from './colord';
import { getHex, getHsl, getRgb } from './color';
import colorNames from './json/color-name.json';
/**
* Get color name
*
* @param color
*/
export function getColorName(color: string) {
const hex = getHex(color);
const rgb = getRgb(color);
@@ -22,13 +17,15 @@ export function getColorName(color: string) {
colorNames.some((item, index) => {
const [hexValue, colorName] = item;
const match = hex === hexValue;
const hexcode = `#${hexValue}`;
const match = hex === hexcode;
if (match) {
name = colorName;
} else {
const { r, g, b } = getRgb(hexValue);
const { h, s, l } = getHsl(hexValue);
const { r, g, b } = getRgb(hexcode);
const { h, s, l } = getHsl(hexcode);
ndf1 = (rgb.r - r) ** 2 + (rgb.g - g) ** 2 + (rgb.b - b) ** 2;
ndf2 = (hsl.h - h) ** 2 + (hsl.s - s) ** 2 + (hsl.l - l) ** 2;
@@ -43,7 +40,7 @@ export function getColorName(color: string) {
return match;
});
name = colorNames[cl][1];
name = cl < 0 ? 'Invalid Color' : colorNames[cl][1];
return name;
}

View File

@@ -0,0 +1,95 @@
import { getDeltaE, getHsl, isValidColor, transformHslToHex } from './color';
import { getColorName } from './name';
import type { ColorPaletteFamily, ColorPaletteFamilyWithNearestPalette } from './type';
import defaultPalettes from './json/palette.json';
export function getNearestColorPaletteFamily(color: string, families: ColorPaletteFamily[]) {
const familyWithConfig = families.map(family => {
const palettes = family.palettes.map(palette => {
return {
...palette,
delta: getDeltaE(color, palette.hexcode)
};
});
const nearestPalette = palettes.reduce((prev, curr) => (prev.delta < curr.delta ? prev : curr));
return {
...family,
palettes,
nearestPalette
};
});
const nearestPaletteFamily = familyWithConfig.reduce((prev, curr) =>
prev.nearestPalette.delta < curr.nearestPalette.delta ? prev : curr
);
const { l } = getHsl(color);
const paletteFamily: ColorPaletteFamilyWithNearestPalette = {
...nearestPaletteFamily,
nearestLightnessPalette: nearestPaletteFamily.palettes.reduce((prev, curr) => {
const { l: prevLightness } = getHsl(prev.hexcode);
const { l: currLightness } = getHsl(curr.hexcode);
const deltaPrev = Math.abs(prevLightness - l);
const deltaCurr = Math.abs(currLightness - l);
return deltaPrev < deltaCurr ? prev : curr;
})
};
return paletteFamily;
}
export function getColorPaletteFamily(color: string, colorName: string) {
if (!isValidColor(color)) {
throw new Error('Invalid color, please check color value!');
}
const { h: h1, s: s1 } = getHsl(color);
const { nearestLightnessPalette, palettes } = getNearestColorPaletteFamily(
color,
defaultPalettes as ColorPaletteFamily[]
);
const { number, hexcode } = nearestLightnessPalette;
const { h: h2, s: s2 } = getHsl(hexcode);
const deltaH = h1 - h2 || h2;
const sRatio = s1 / s2;
const colorPaletteFamily: ColorPaletteFamily = {
key: colorName,
palettes: palettes.map(palette => {
let hexValue = color;
const isSame = number === palette.number;
if (!isSame) {
const { h: h3, s: s3, l } = getHsl(palette.hexcode);
const newH = deltaH < 0 ? h3 + deltaH : deltaH;
const newS = s3 * sRatio;
hexValue = transformHslToHex({
h: newH,
s: newS,
l
});
}
return {
hexcode: hexValue,
number: palette.number,
name: getColorName(hexValue)
};
})
};
return colorPaletteFamily;
}

View File

@@ -0,0 +1,49 @@
/** The color palette number */
export type ColorPaletteNumber = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950;
/** The color palette item */
export type ColorPaletteItem = {
/** The color hexcode */
hexcode: string;
/**
* The color number
*
* @link {@link ColorPaletteNumber}
*/
number: ColorPaletteNumber;
/** The color name */
name: string;
};
export type ColorPaletteFamily = {
/** The color palette family key */
key: string;
/** The color palette family's palettes */
palettes: ColorPaletteItem[];
};
export type ColorPaletteWithDelta = ColorPaletteItem & {
delta: number;
};
export type ColorPaletteItemWithName = ColorPaletteItem & {
name: string;
};
export type ColorPaletteFamilyWithNearestPalette = ColorPaletteFamily & {
nearestPalette: ColorPaletteWithDelta;
nearestLightnessPalette: ColorPaletteWithDelta;
};
export type ColorPalette = ColorPaletteFamily & {
/** The color map of the palette */
colorMap: Map<ColorPaletteNumber, ColorPaletteItem>;
/**
* The main color of the palette
*
* Which number is 500
*/
main: ColorPaletteItemWithName;
/** The match color of the palette */
match: ColorPaletteItemWithName;
};

View File

@@ -1,2 +0,0 @@
export * from './name';
export * from './palette';

File diff suppressed because it is too large Load Diff

View File

@@ -1,356 +0,0 @@
import type { ColorPaletteFamily } from '../types';
export const colorPalettes: ColorPaletteFamily[] = [
{
name: 'Slate',
palettes: [
{ hex: '#f8fafc', number: 50 },
{ hex: '#f1f5f9', number: 100 },
{ hex: '#e2e8f0', number: 200 },
{ hex: '#cbd5e1', number: 300 },
{ hex: '#94a3b8', number: 400 },
{ hex: '#64748b', number: 500 },
{ hex: '#475569', number: 600 },
{ hex: '#334155', number: 700 },
{ hex: '#1e293b', number: 800 },
{ hex: '#0f172a', number: 900 },
{ hex: '#020617', number: 950 }
]
},
{
name: 'Gray',
palettes: [
{ hex: '#f9fafb', number: 50 },
{ hex: '#f3f4f6', number: 100 },
{ hex: '#e5e7eb', number: 200 },
{ hex: '#d1d5db', number: 300 },
{ hex: '#9ca3af', number: 400 },
{ hex: '#6b7280', number: 500 },
{ hex: '#4b5563', number: 600 },
{ hex: '#374151', number: 700 },
{ hex: '#1f2937', number: 800 },
{ hex: '#111827', number: 900 },
{ hex: '#030712', number: 950 }
]
},
{
name: 'Zinc',
palettes: [
{ hex: '#fafafa', number: 50 },
{ hex: '#f4f4f5', number: 100 },
{ hex: '#e4e4e7', number: 200 },
{ hex: '#d4d4d8', number: 300 },
{ hex: '#a1a1aa', number: 400 },
{ hex: '#71717a', number: 500 },
{ hex: '#52525b', number: 600 },
{ hex: '#3f3f46', number: 700 },
{ hex: '#27272a', number: 800 },
{ hex: '#18181b', number: 900 },
{ hex: '#09090b', number: 950 }
]
},
{
name: 'Neutral',
palettes: [
{ hex: '#fafafa', number: 50 },
{ hex: '#f5f5f5', number: 100 },
{ hex: '#e5e5e5', number: 200 },
{ hex: '#d4d4d4', number: 300 },
{ hex: '#a3a3a3', number: 400 },
{ hex: '#737373', number: 500 },
{ hex: '#525252', number: 600 },
{ hex: '#404040', number: 700 },
{ hex: '#262626', number: 800 },
{ hex: '#171717', number: 900 },
{ hex: '#0a0a0a', number: 950 }
]
},
{
name: 'Stone',
palettes: [
{ hex: '#fafaf9', number: 50 },
{ hex: '#f5f5f4', number: 100 },
{ hex: '#e7e5e4', number: 200 },
{ hex: '#d6d3d1', number: 300 },
{ hex: '#a8a29e', number: 400 },
{ hex: '#78716c', number: 500 },
{ hex: '#57534e', number: 600 },
{ hex: '#44403c', number: 700 },
{ hex: '#292524', number: 800 },
{ hex: '#1c1917', number: 900 },
{ hex: '#0c0a09', number: 950 }
]
},
{
name: 'Red',
palettes: [
{ hex: '#fef2f2', number: 50 },
{ hex: '#fee2e2', number: 100 },
{ hex: '#fecaca', number: 200 },
{ hex: '#fca5a5', number: 300 },
{ hex: '#f87171', number: 400 },
{ hex: '#ef4444', number: 500 },
{ hex: '#dc2626', number: 600 },
{ hex: '#b91c1c', number: 700 },
{ hex: '#991b1b', number: 800 },
{ hex: '#7f1d1d', number: 900 },
{ hex: '#450a0a', number: 950 }
]
},
{
name: 'Orange',
palettes: [
{ hex: '#fff7ed', number: 50 },
{ hex: '#ffedd5', number: 100 },
{ hex: '#fed7aa', number: 200 },
{ hex: '#fdba74', number: 300 },
{ hex: '#fb923c', number: 400 },
{ hex: '#f97316', number: 500 },
{ hex: '#ea580c', number: 600 },
{ hex: '#c2410c', number: 700 },
{ hex: '#9a3412', number: 800 },
{ hex: '#7c2d12', number: 900 },
{ hex: '#431407', number: 950 }
]
},
{
name: 'Amber',
palettes: [
{ hex: '#fffbeb', number: 50 },
{ hex: '#fef3c7', number: 100 },
{ hex: '#fde68a', number: 200 },
{ hex: '#fcd34d', number: 300 },
{ hex: '#fbbf24', number: 400 },
{ hex: '#f59e0b', number: 500 },
{ hex: '#d97706', number: 600 },
{ hex: '#b45309', number: 700 },
{ hex: '#92400e', number: 800 },
{ hex: '#78350f', number: 900 },
{ hex: '#451a03', number: 950 }
]
},
{
name: 'Yellow',
palettes: [
{ hex: '#fefce8', number: 50 },
{ hex: '#fef9c3', number: 100 },
{ hex: '#fef08a', number: 200 },
{ hex: '#fde047', number: 300 },
{ hex: '#facc15', number: 400 },
{ hex: '#eab308', number: 500 },
{ hex: '#ca8a04', number: 600 },
{ hex: '#a16207', number: 700 },
{ hex: '#854d0e', number: 800 },
{ hex: '#713f12', number: 900 },
{ hex: '#422006', number: 950 }
]
},
{
name: 'Lime',
palettes: [
{ hex: '#f7fee7', number: 50 },
{ hex: '#ecfccb', number: 100 },
{ hex: '#d9f99d', number: 200 },
{ hex: '#bef264', number: 300 },
{ hex: '#a3e635', number: 400 },
{ hex: '#84cc16', number: 500 },
{ hex: '#65a30d', number: 600 },
{ hex: '#4d7c0f', number: 700 },
{ hex: '#3f6212', number: 800 },
{ hex: '#365314', number: 900 },
{ hex: '#1a2e05', number: 950 }
]
},
{
name: 'Green',
palettes: [
{ hex: '#f0fdf4', number: 50 },
{ hex: '#dcfce7', number: 100 },
{ hex: '#bbf7d0', number: 200 },
{ hex: '#86efac', number: 300 },
{ hex: '#4ade80', number: 400 },
{ hex: '#22c55e', number: 500 },
{ hex: '#16a34a', number: 600 },
{ hex: '#15803d', number: 700 },
{ hex: '#166534', number: 800 },
{ hex: '#14532d', number: 900 },
{ hex: '#052e16', number: 950 }
]
},
{
name: 'Emerald',
palettes: [
{ hex: '#ecfdf5', number: 50 },
{ hex: '#d1fae5', number: 100 },
{ hex: '#a7f3d0', number: 200 },
{ hex: '#6ee7b7', number: 300 },
{ hex: '#34d399', number: 400 },
{ hex: '#10b981', number: 500 },
{ hex: '#059669', number: 600 },
{ hex: '#047857', number: 700 },
{ hex: '#065f46', number: 800 },
{ hex: '#064e3b', number: 900 },
{ hex: '#022c22', number: 950 }
]
},
{
name: 'Teal',
palettes: [
{ hex: '#f0fdfa', number: 50 },
{ hex: '#ccfbf1', number: 100 },
{ hex: '#99f6e4', number: 200 },
{ hex: '#5eead4', number: 300 },
{ hex: '#2dd4bf', number: 400 },
{ hex: '#14b8a6', number: 500 },
{ hex: '#0d9488', number: 600 },
{ hex: '#0f766e', number: 700 },
{ hex: '#115e59', number: 800 },
{ hex: '#134e4a', number: 900 },
{ hex: '#042f2e', number: 950 }
]
},
{
name: 'Cyan',
palettes: [
{ hex: '#ecfeff', number: 50 },
{ hex: '#cffafe', number: 100 },
{ hex: '#a5f3fc', number: 200 },
{ hex: '#67e8f9', number: 300 },
{ hex: '#22d3ee', number: 400 },
{ hex: '#06b6d4', number: 500 },
{ hex: '#0891b2', number: 600 },
{ hex: '#0e7490', number: 700 },
{ hex: '#155e75', number: 800 },
{ hex: '#164e63', number: 900 },
{ hex: '#083344', number: 950 }
]
},
{
name: 'Sky',
palettes: [
{ hex: '#f0f9ff', number: 50 },
{ hex: '#e0f2fe', number: 100 },
{ hex: '#bae6fd', number: 200 },
{ hex: '#7dd3fc', number: 300 },
{ hex: '#38bdf8', number: 400 },
{ hex: '#0ea5e9', number: 500 },
{ hex: '#0284c7', number: 600 },
{ hex: '#0369a1', number: 700 },
{ hex: '#075985', number: 800 },
{ hex: '#0c4a6e', number: 900 },
{ hex: '#082f49', number: 950 }
]
},
{
name: 'Blue',
palettes: [
{ hex: '#eff6ff', number: 50 },
{ hex: '#dbeafe', number: 100 },
{ hex: '#bfdbfe', number: 200 },
{ hex: '#93c5fd', number: 300 },
{ hex: '#60a5fa', number: 400 },
{ hex: '#3b82f6', number: 500 },
{ hex: '#2563eb', number: 600 },
{ hex: '#1d4ed8', number: 700 },
{ hex: '#1e40af', number: 800 },
{ hex: '#1e3a8a', number: 900 },
{ hex: '#172554', number: 950 }
]
},
{
name: 'Indigo',
palettes: [
{ hex: '#eef2ff', number: 50 },
{ hex: '#e0e7ff', number: 100 },
{ hex: '#c7d2fe', number: 200 },
{ hex: '#a5b4fc', number: 300 },
{ hex: '#818cf8', number: 400 },
{ hex: '#6366f1', number: 500 },
{ hex: '#4f46e5', number: 600 },
{ hex: '#4338ca', number: 700 },
{ hex: '#3730a3', number: 800 },
{ hex: '#312e81', number: 900 },
{ hex: '#1e1b4b', number: 950 }
]
},
{
name: 'Violet',
palettes: [
{ hex: '#f5f3ff', number: 50 },
{ hex: '#ede9fe', number: 100 },
{ hex: '#ddd6fe', number: 200 },
{ hex: '#c4b5fd', number: 300 },
{ hex: '#a78bfa', number: 400 },
{ hex: '#8b5cf6', number: 500 },
{ hex: '#7c3aed', number: 600 },
{ hex: '#6d28d9', number: 700 },
{ hex: '#5b21b6', number: 800 },
{ hex: '#4c1d95', number: 900 },
{ hex: '#2e1065', number: 950 }
]
},
{
name: 'Purple',
palettes: [
{ hex: '#faf5ff', number: 50 },
{ hex: '#f3e8ff', number: 100 },
{ hex: '#e9d5ff', number: 200 },
{ hex: '#d8b4fe', number: 300 },
{ hex: '#c084fc', number: 400 },
{ hex: '#a855f7', number: 500 },
{ hex: '#9333ea', number: 600 },
{ hex: '#7e22ce', number: 700 },
{ hex: '#6b21a8', number: 800 },
{ hex: '#581c87', number: 900 },
{ hex: '#3b0764', number: 950 }
]
},
{
name: 'Fuchsia',
palettes: [
{ hex: '#fdf4ff', number: 50 },
{ hex: '#fae8ff', number: 100 },
{ hex: '#f5d0fe', number: 200 },
{ hex: '#f0abfc', number: 300 },
{ hex: '#e879f9', number: 400 },
{ hex: '#d946ef', number: 500 },
{ hex: '#c026d3', number: 600 },
{ hex: '#a21caf', number: 700 },
{ hex: '#86198f', number: 800 },
{ hex: '#701a75', number: 900 },
{ hex: '#4a044e', number: 950 }
]
},
{
name: 'Pink',
palettes: [
{ hex: '#fdf2f8', number: 50 },
{ hex: '#fce7f3', number: 100 },
{ hex: '#fbcfe8', number: 200 },
{ hex: '#f9a8d4', number: 300 },
{ hex: '#f472b6', number: 400 },
{ hex: '#ec4899', number: 500 },
{ hex: '#db2777', number: 600 },
{ hex: '#be185d', number: 700 },
{ hex: '#9d174d', number: 800 },
{ hex: '#831843', number: 900 },
{ hex: '#500724', number: 950 }
]
},
{
name: 'Rose',
palettes: [
{ hex: '#fff1f2', number: 50 },
{ hex: '#ffe4e6', number: 100 },
{ hex: '#fecdd3', number: 200 },
{ hex: '#fda4af', number: 300 },
{ hex: '#fb7185', number: 400 },
{ hex: '#f43f5e', number: 500 },
{ hex: '#e11d48', number: 600 },
{ hex: '#be123c', number: 700 },
{ hex: '#9f1239', number: 800 },
{ hex: '#881337', number: 900 },
{ hex: '#4c0519', number: 950 }
]
}
];

View File

@@ -1,7 +0,0 @@
import { colorPalettes } from './constant';
export * from './palette';
export * from './shared';
export { colorPalettes };
export * from './types';

View File

@@ -1,45 +0,0 @@
import type { AnyColor } from 'colord';
import { getHex } from '../shared';
import type { ColorPaletteNumber } from '../types';
import { getRecommendedColorPalette } from './recommend';
import { getAntDColorPalette } from './antd';
/**
* get color palette by provided color
*
* @param color
* @param recommended whether to get recommended color palette (the provided color may not be the main color)
*/
export function getColorPalette(color: AnyColor, recommended = false) {
const colorMap = new Map<ColorPaletteNumber, string>();
if (recommended) {
const colorPalette = getRecommendedColorPalette(getHex(color));
colorPalette.palettes.forEach(palette => {
colorMap.set(palette.number, palette.hex);
});
} else {
const colors = getAntDColorPalette(color);
const colorNumbers: ColorPaletteNumber[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
colorNumbers.forEach((number, index) => {
colorMap.set(number, colors[index]);
});
}
return colorMap;
}
/**
* get color palette color by number
*
* @param color the provided color
* @param number the color palette number
* @param recommended whether to get recommended color palette (the provided color may not be the main color)
*/
export function getPaletteColorByNumber(color: AnyColor, number: ColorPaletteNumber, recommended = false) {
const colorMap = getColorPalette(color, recommended);
return colorMap.get(number as ColorPaletteNumber)!;
}

View File

@@ -1,152 +0,0 @@
import { getColorName, getDeltaE, getHsl, isValidColor, transformHslToHex } from '../shared';
import { colorPalettes } from '../constant';
import type {
ColorPalette,
ColorPaletteFamily,
ColorPaletteFamilyWithNearestPalette,
ColorPaletteMatch,
ColorPaletteNumber
} from '../types';
/**
* get recommended color palette by provided color
*
* @param color the provided color
*/
export function getRecommendedColorPalette(color: string) {
const colorPaletteFamily = getRecommendedColorPaletteFamily(color);
const colorMap = new Map<ColorPaletteNumber, ColorPalette>();
colorPaletteFamily.palettes.forEach(palette => {
colorMap.set(palette.number, palette);
});
const mainColor = colorMap.get(500)!;
const matchColor = colorPaletteFamily.palettes.find(palette => palette.hex === color)!;
const colorPalette: ColorPaletteMatch = {
...colorPaletteFamily,
colorMap,
main: mainColor,
match: matchColor
};
return colorPalette;
}
/**
* get recommended palette color by provided color
*
* @param color the provided color
* @param number the color palette number
*/
export function getRecommendedPaletteColorByNumber(color: string, number: ColorPaletteNumber) {
const colorPalette = getRecommendedColorPalette(color);
const { hex } = colorPalette.colorMap.get(number)!;
return hex;
}
/**
* get color palette family by provided color and color name
*
* @param color the provided color
*/
export function getRecommendedColorPaletteFamily(color: string) {
if (!isValidColor(color)) {
throw new Error('Invalid color, please check color value!');
}
let colorName = getColorName(color);
colorName = colorName.toLowerCase().replace(/\s/g, '-');
const { h: h1, s: s1 } = getHsl(color);
const { nearestLightnessPalette, palettes } = getNearestColorPaletteFamily(color, colorPalettes);
const { number, hex } = nearestLightnessPalette;
const { h: h2, s: s2 } = getHsl(hex);
const deltaH = h1 - h2;
const sRatio = s1 / s2;
const colorPaletteFamily: ColorPaletteFamily = {
name: colorName,
palettes: palettes.map(palette => {
let hexValue = color;
const isSame = number === palette.number;
if (!isSame) {
const { h: h3, s: s3, l } = getHsl(palette.hex);
const newH = deltaH < 0 ? h3 + deltaH : h3 - deltaH;
const newS = s3 * sRatio;
hexValue = transformHslToHex({
h: newH,
s: newS,
l
});
}
return {
hex: hexValue,
number: palette.number
};
})
};
return colorPaletteFamily;
}
/**
* get nearest color palette family
*
* @param color color
* @param families color palette families
*/
function getNearestColorPaletteFamily(color: string, families: ColorPaletteFamily[]) {
const familyWithConfig = families.map(family => {
const palettes = family.palettes.map(palette => {
return {
...palette,
delta: getDeltaE(color, palette.hex)
};
});
const nearestPalette = palettes.reduce((prev, curr) => (prev.delta < curr.delta ? prev : curr));
return {
...family,
palettes,
nearestPalette
};
});
const nearestPaletteFamily = familyWithConfig.reduce((prev, curr) =>
prev.nearestPalette.delta < curr.nearestPalette.delta ? prev : curr
);
const { l } = getHsl(color);
const paletteFamily: ColorPaletteFamilyWithNearestPalette = {
...nearestPaletteFamily,
nearestLightnessPalette: nearestPaletteFamily.palettes.reduce((prev, curr) => {
const { l: prevLightness } = getHsl(prev.hex);
const { l: currLightness } = getHsl(curr.hex);
const deltaPrev = Math.abs(prevLightness - l);
const deltaCurr = Math.abs(currLightness - l);
return deltaPrev < deltaCurr ? prev : curr;
})
};
return paletteFamily;
}

View File

@@ -1,93 +0,0 @@
import { colord, extend } from 'colord';
import namesPlugin from 'colord/plugins/names';
import mixPlugin from 'colord/plugins/mix';
import labPlugin from 'colord/plugins/lab';
import type { AnyColor, HslColor, RgbColor } from 'colord';
extend([namesPlugin, mixPlugin, labPlugin]);
export function isValidColor(color: AnyColor) {
return colord(color).isValid();
}
export function getHex(color: AnyColor) {
return colord(color).toHex();
}
export function getRgb(color: AnyColor) {
return colord(color).toRgb();
}
export function getHsl(color: AnyColor) {
return colord(color).toHsl();
}
export function getHsv(color: AnyColor) {
return colord(color).toHsv();
}
export function getDeltaE(color1: AnyColor, color2: AnyColor) {
return colord(color1).delta(color2);
}
export function transformHslToHex(color: HslColor) {
return colord(color).toHex();
}
/**
* Add color alpha
*
* @param color - Color
* @param alpha - Alpha (0 - 1)
*/
export function addColorAlpha(color: AnyColor, alpha: number) {
return colord(color).alpha(alpha).toHex();
}
/**
* Mix color
*
* @param firstColor - First color
* @param secondColor - Second color
* @param ratio - The ratio of the second color (0 - 1)
*/
export function mixColor(firstColor: AnyColor, secondColor: AnyColor, ratio: number) {
return colord(firstColor).mix(secondColor, ratio).toHex();
}
/**
* Transform color with opacity to similar color without opacity
*
* @param color - Color
* @param alpha - Alpha (0 - 1)
* @param bgColor Background color (usually white or black)
*/
export function transformColorWithOpacity(color: AnyColor, alpha: number, bgColor = '#ffffff') {
const originColor = addColorAlpha(color, alpha);
const { r: oR, g: oG, b: oB } = colord(originColor).toRgb();
const { r: bgR, g: bgG, b: bgB } = colord(bgColor).toRgb();
function calRgb(or: number, bg: number, al: number) {
return bg + (or - bg) * al;
}
const resultRgb: RgbColor = {
r: calRgb(oR, bgR, alpha),
g: calRgb(oG, bgG, alpha),
b: calRgb(oB, bgB, alpha)
};
return colord(resultRgb).toHex();
}
/**
* Is white color
*
* @param color - Color
*/
export function isWhiteColor(color: AnyColor) {
return colord(color).isEqual('#ffffff');
}
export { colord };

View File

@@ -1,2 +0,0 @@
export * from './colord';
export * from './name';

View File

@@ -1,58 +0,0 @@
/**
* the color palette number
*
* the main color number is 500
*/
export type ColorPaletteNumber = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950;
/** the color palette */
export type ColorPalette = {
/** the color hex value */
hex: string;
/**
* the color number
*
* - 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950
*/
number: ColorPaletteNumber;
};
/** the color palette family */
export type ColorPaletteFamily = {
/** the color palette family name */
name: string;
/** the color palettes */
palettes: ColorPalette[];
};
/** the color palette with delta */
export type ColorPaletteWithDelta = ColorPalette & {
delta: number;
};
/** the color palette family with nearest palette */
export type ColorPaletteFamilyWithNearestPalette = ColorPaletteFamily & {
nearestPalette: ColorPaletteWithDelta;
nearestLightnessPalette: ColorPaletteWithDelta;
};
/** the color palette match */
export type ColorPaletteMatch = ColorPaletteFamily & {
/** the color map of the palette */
colorMap: Map<ColorPaletteNumber, ColorPalette>;
/**
* the main color of the palette
*
* which number is 500
*/
main: ColorPalette;
/** the match color of the palette */
match: ColorPalette;
};
/**
* The color index of color palette
*
* From left to right, the color is from light to dark, 6 is main color
*/
export type ColorIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11;

View File

@@ -1,20 +0,0 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

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

View File

@@ -7,5 +7,4 @@ import useHookTable from './use-table';
export { useBoolean, useLoading, useCountDown, useContext, useSvgIconRender, useHookTable };
export * from './use-signal';
export * from './use-table';

View File

@@ -1,144 +0,0 @@
import { computed, ref, shallowRef, triggerRef } from 'vue';
import type {
ComputedGetter,
DebuggerOptions,
Ref,
ShallowRef,
WritableComputedOptions,
WritableComputedRef
} from 'vue';
type Updater<T> = (value: T) => T;
type Mutator<T> = (value: T) => void;
/**
* Signal is a reactive value that can be set, updated or mutated
*
* @example
* ```ts
* const count = useSignal(0);
*
* // `watchEffect`
* watchEffect(() => {
* console.log(count());
* });
*
* // watch
* watch(count, value => {
* console.log(value);
* });
*
* // useComputed
* const double = useComputed(() => count() * 2);
* const writeableDouble = useComputed({
* get: () => count() * 2,
* set: value => count.set(value / 2)
* });
* ```
*/
export interface Signal<T> {
(): Readonly<T>;
/**
* Set the value of the signal
*
* It recommend use `set` for primitive values
*
* @param value
*/
set(value: T): void;
/**
* Update the value of the signal using an updater function
*
* It recommend use `update` for non-primitive values, only the first level of the object will be reactive.
*
* @param updater
*/
update(updater: Updater<T>): void;
/**
* Mutate the value of the signal using a mutator function
*
* this action will call `triggerRef`, so the value will be tracked on `watchEffect`.
*
* It recommend use `mutate` for non-primitive values, all levels of the object will be reactive.
*
* @param mutator
*/
mutate(mutator: Mutator<T>): void;
/**
* Get the reference of the signal
*
* Sometimes it can be useful to make `v-model` work with the signal
*
* ```vue
* <template>
* <input v-model="model.count" />
* </template>;
*
* <script setup lang="ts">
* const state = useSignal({ count: 0 }, { useRef: true });
*
* const model = state.getRef();
* </script>
* ```
*/
getRef(): Readonly<ShallowRef<Readonly<T>>>;
}
export interface ReadonlySignal<T> {
(): Readonly<T>;
}
export interface SignalOptions {
/**
* Whether to use `ref` to store the value
*
* @default false use `sharedRef` to store the value
*/
useRef?: boolean;
}
export function useSignal<T>(initialValue: T, options?: SignalOptions): Signal<T> {
const { useRef } = options || {};
const state = useRef ? (ref(initialValue) as Ref<T>) : shallowRef(initialValue);
return createSignal(state);
}
export function useComputed<T>(getter: ComputedGetter<T>, debugOptions?: DebuggerOptions): ReadonlySignal<T>;
export function useComputed<T>(options: WritableComputedOptions<T>, debugOptions?: DebuggerOptions): Signal<T>;
export function useComputed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
debugOptions?: DebuggerOptions
) {
const isGetter = typeof getterOrOptions === 'function';
const computedValue = computed(getterOrOptions as any, debugOptions);
if (isGetter) {
return () => computedValue.value as ReadonlySignal<T>;
}
return createSignal(computedValue);
}
function createSignal<T>(state: ShallowRef<T> | WritableComputedRef<T>): Signal<T> {
const signal = () => state.value;
signal.set = (value: T) => {
state.value = value;
};
signal.update = (updater: Updater<T>) => {
state.value = updater(state.value);
};
signal.mutate = (mutator: Mutator<T>) => {
mutator(state.value);
triggerRef(state);
};
signal.getRef = () => state as Readonly<ShallowRef<Readonly<T>>>;
return signal;
}

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/materials",
"version": "1.2.5",
"version": "1.0.2",
"exports": {
".": "./src/index.ts"
},
@@ -11,7 +11,7 @@
},
"dependencies": {
"@sa/utils": "workspace:*",
"simplebar-vue": "2.3.5"
"simplebar-vue": "2.3.3"
},
"devDependencies": {
"typed-css-modules": "0.9.1"

View File

@@ -36,7 +36,7 @@ defineSlots<Slots>();
<template>
<div
class=":soy: relative inline-flex cursor-pointer items-center justify-center gap-12px whitespace-nowrap border-(1px solid) rounded-4px px-12px py-4px"
class=":soy: relative inline-flex cursor-pointer items-center justify-center gap-12px whitespace-nowrap border-1px rounded-4px border-solid px-12px py-4px"
:class="[
style['button-tab'],
{ [style['button-tab_dark']]: darkMode },

View File

@@ -25,6 +25,31 @@ interface Emits {
const emit = defineEmits<Emits>();
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>();
const activeTabComponent = computed(() => {
const { mode, chromeClass, buttonClass } = props;
@@ -53,30 +78,17 @@ const bindProps = computed(() => {
function handleClose() {
emit('close');
}
function handleMouseup(e: MouseEvent) {
// close tab by mouse wheel button click
if (e.button === 1) {
handleClose();
}
}
</script>
<template>
<component
:is="activeTabComponent.component"
:class="activeTabComponent.class"
:style="cssVars"
v-bind="bindProps"
@mouseup="handleMouseup"
>
<component :is="activeTabComponent.component" :class="activeTabComponent.class" :style="cssVars" v-bind="bindProps">
<template #prefix>
<slot name="prefix"></slot>
</template>
<slot></slot>
<template #suffix>
<slot name="suffix">
<SvgClose v-if="closable" :class="[style['svg-close']]" @click.stop="handleClose" />
<SvgClose v-if="closable" :class="[style['svg-close']]" @click="handleClose" />
</slot>
</template>
</component>

View File

@@ -1,4 +1,4 @@
import { addColorAlpha, transformColorWithOpacity } from '@sa/color';
import { addColorAlpha, transformColorWithOpacity } from '@sa/utils';
import type { PageTabCssVars, PageTabCssVarsProps } from '../../types';
/** The active color of the tab */

View File

@@ -2,10 +2,23 @@
defineOptions({
name: 'SvgClose'
});
const emit = defineEmits<Emits>();
interface Emits {
(e: 'click'): void;
}
function handleClick() {
emit('click');
}
</script>
<template>
<div class=":soy: relative h-16px w-16px inline-flex items-center justify-center rd-50% text-14px">
<div
class=":soy: relative h-16px w-16px inline-flex items-center justify-center rd-50% text-14px"
@click.stop="handleClick"
>
<svg width="1em" height="1em" viewBox="0 0 1024 1024">
<path
fill="currentColor"

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/fetch",
"version": "1.2.5",
"version": "1.0.2",
"exports": {
".": "./src/index.ts"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/scripts",
"version": "1.2.5",
"version": "1.0.2",
"bin": {
"sa": "./bin.ts"
},
@@ -13,15 +13,15 @@
}
},
"devDependencies": {
"@soybeanjs/changelog": "0.3.24",
"bumpp": "9.4.1",
"@soybeanjs/changelog": "0.3.15",
"bumpp": "9.4.0",
"c12": "1.10.0",
"cac": "6.7.14",
"consola": "3.2.3",
"enquirer": "2.4.1",
"execa": "9.2.0",
"execa": "8.0.1",
"kolorist": "1.8.0",
"npm-check-updates": "16.14.20",
"rimraf": "5.0.7"
"npm-check-updates": "16.14.18",
"rimraf": "5.0.5"
}
}

View File

@@ -19,7 +19,6 @@ const defaultOptions: CliOption = {
['style', 'Changes that do not affect the meaning of the code'],
['refactor', 'A code change that neither fixes a bug nor adds a feature'],
['perf', 'A code change that improves performance'],
['optimize', 'A code change that optimizes code quality'],
['test', 'Adding missing tests or correcting existing tests'],
['build', 'Changes that affect the build system or external dependencies'],
['ci', 'Changes to our CI configuration files and scripts'],
@@ -28,7 +27,6 @@ const defaultOptions: CliOption = {
],
gitCommitScopes: [
['projects', 'project'],
['packages', 'packages'],
['components', 'components'],
['hooks', 'hook functions'],
['utils', 'utils functions'],

View File

@@ -3,5 +3,5 @@ import type { Options } from 'execa';
export async function execCommand(cmd: string, args: string[], options?: Options) {
const { execa } = await import('execa');
const res = await execa(cmd, args, options);
return (res?.stdout as string)?.trim() || '';
return res?.stdout?.trim() || '';
}

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@sa/utils",
"version": "1.2.5",
"version": "1.0.2",
"exports": {
".": "./src/index.ts"
},

View File

@@ -1,6 +1,74 @@
import type { AnyColor, HsvColor } from 'colord';
import { getHex, getHsv, isValidColor, mixColor } from '../shared';
import type { ColorIndex } from '../types';
import { colord, extend } from 'colord';
import namesPlugin from 'colord/plugins/names';
import mixPlugin from 'colord/plugins/mix';
import type { AnyColor, HsvColor, RgbColor } from 'colord';
extend([namesPlugin, mixPlugin]);
/**
* Add color alpha
*
* @param color - Color
* @param alpha - Alpha (0 - 1)
*/
export function addColorAlpha(color: string, alpha: number) {
return colord(color).alpha(alpha).toHex();
}
/**
* Mix color
*
* @param firstColor - First color
* @param secondColor - Second color
* @param ratio - The ratio of the second color (0 - 1)
*/
export function mixColor(firstColor: string, secondColor: string, ratio: number) {
return colord(firstColor).mix(secondColor, ratio).toHex();
}
/**
* Transform color with opacity to similar color without opacity
*
* @param color - Color
* @param alpha - Alpha (0 - 1)
* @param bgColor Background color (usually white or black)
*/
export function transformColorWithOpacity(color: string, alpha: number, bgColor = '#ffffff') {
const originColor = addColorAlpha(color, alpha);
const { r: oR, g: oG, b: oB } = colord(originColor).toRgb();
const { r: bgR, g: bgG, b: bgB } = colord(bgColor).toRgb();
function calRgb(or: number, bg: number, al: number) {
return bg + (or - bg) * al;
}
const resultRgb: RgbColor = {
r: calRgb(oR, bgR, alpha),
g: calRgb(oG, bgG, alpha),
b: calRgb(oB, bgB, alpha)
};
return colord(resultRgb).toHex();
}
/**
* Is white color
*
* @param color - Color
*/
export function isWhiteColor(color: string) {
return colord(color).isEqual('#ffffff');
}
/**
* Get rgb of color
*
* @param color Color
*/
export function getRgbOfColor(color: string) {
return colord(color).toRgb();
}
/** Hue step */
const hueStep = 2;
@@ -18,23 +86,32 @@ const lightColorCount = 5;
const darkColorCount = 4;
/**
* Get AntD palette color by index
* The color index of color palette
*
* From left to right, the color is from light to dark, 6 is main color
*/
type ColorIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
/**
* Get color palette (from left to right, the color is from light to dark, 6 is main color)
*
* @param color - Color
* @param index - The color index of color palette (the main color index is 6)
* @returns Hex color
*/
export function getAntDPaletteColorByIndex(color: AnyColor, index: ColorIndex): string {
if (!isValidColor(color)) {
export function getColorPalette(color: AnyColor, index: ColorIndex): string {
const transformColor = colord(color);
if (!transformColor.isValid()) {
throw new Error('invalid input color value');
}
if (index === 6) {
return getHex(color);
return colord(transformColor).toHex();
}
const isLight = index < 6;
const hsv = getHsv(color);
const hsv = transformColor.toHsv();
const i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
const newHsv: HsvColor = {
@@ -43,7 +120,7 @@ export function getAntDPaletteColorByIndex(color: AnyColor, index: ColorIndex):
v: getValue(hsv, i, isLight)
};
return getHex(newHsv);
return colord(newHsv).toHex();
}
/** Map of dark color index and opacity */
@@ -54,33 +131,32 @@ const darkColorMap = [
{ index: 5, opacity: 0.45 },
{ index: 5, opacity: 0.65 },
{ index: 5, opacity: 0.85 },
{ index: 5, opacity: 0.9 },
{ index: 4, opacity: 0.93 },
{ index: 4, opacity: 0.9 },
{ index: 3, opacity: 0.95 },
{ index: 2, opacity: 0.97 },
{ index: 1, opacity: 0.98 }
];
/**
* Get AntD color palette
* Get color palettes
*
* @param color - Color
* @param darkTheme - Dark theme
* @param darkThemeMixColor - Dark theme mix color (default: #141414)
*/
export function getAntDColorPalette(color: AnyColor, darkTheme = false, darkThemeMixColor = '#141414'): string[] {
const indexes: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
export function getColorPalettes(color: AnyColor, darkTheme = false, darkThemeMixColor = '#141414'): string[] {
const indexes: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const patterns = indexes.map(index => getAntDPaletteColorByIndex(color, index));
const patterns = indexes.map(index => getColorPalette(color, index));
if (darkTheme) {
const darkPatterns = darkColorMap.map(({ index, opacity }) => {
const darkColor = mixColor(darkThemeMixColor, patterns[index], opacity);
const darkColor = colord(darkThemeMixColor).mix(patterns[index], opacity);
return darkColor;
});
return darkPatterns.map(item => getHex(item));
return darkPatterns.map(item => colord(item).toHex());
}
return patterns;

View File

@@ -1,3 +1,4 @@
export * from './color';
export * from './crypto';
export * from './storage';
export * from './nanoid';

View File

@@ -3,7 +3,7 @@ import localforage from 'localforage';
/** The storage type */
export type StorageType = 'local' | 'session';
export function createStorage<T extends object>(type: StorageType, storagePrefix: string) {
export function createStorage<T extends object>(type: StorageType) {
const stg = type === 'session' ? window.sessionStorage : window.localStorage;
const storage = {
@@ -16,7 +16,7 @@ export function createStorage<T extends object>(type: StorageType, storagePrefix
set<K extends keyof T>(key: K, value: T[K]) {
const json = JSON.stringify(value);
stg.setItem(`${storagePrefix}${key as string}`, json);
stg.setItem(key as string, json);
},
/**
* Get session
@@ -24,7 +24,7 @@ export function createStorage<T extends object>(type: StorageType, storagePrefix
* @param key Session key
*/
get<K extends keyof T>(key: K): T[K] | null {
const json = stg.getItem(`${storagePrefix}${key as string}`);
const json = stg.getItem(key as string);
if (json) {
let storageData: T[K] | null = null;
@@ -37,12 +37,12 @@ export function createStorage<T extends object>(type: StorageType, storagePrefix
}
}
stg.removeItem(`${storagePrefix}${key as string}`);
stg.removeItem(key as string);
return null;
},
remove(key: keyof T) {
stg.removeItem(`${storagePrefix}${key as string}`);
stg.removeItem(key as string);
},
clear() {
stg.clear();

View File

@@ -1,20 +0,0 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

12152
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

3
src-tauri/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# Generated by Cargo
# will have compiled files and executables
/target/

3664
src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

26
src-tauri/Cargo.toml Normal file
View File

@@ -0,0 +1,26 @@
[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
default-run = "app"
edition = "2021"
rust-version = "1.60"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.5.1", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.6.1", features = [] }
[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
# DO NOT REMOVE!!
custom-protocol = [ "tauri/custom-protocol" ]

3
src-tauri/build.rs Normal file
View File

@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

BIN
src-tauri/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src-tauri/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src-tauri/icons/icon.icns Normal file

Binary file not shown.

BIN
src-tauri/icons/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
src-tauri/icons/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

8
src-tauri/src/main.rs Normal file
View File

@@ -0,0 +1,8 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

60
src-tauri/tauri.conf.json Normal file
View File

@@ -0,0 +1,60 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"devPath": "http://localhost:9527",
"distDir": "../dist"
},
"package": {
"productName": "soybean-admin",
"version": "1.0.0"
},
"tauri": {
"allowlist": {
"all": false
},
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "",
"deb": {
"depends": []
},
"externalBin": [],
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
"identifier": "cn.soybeanjs.admin",
"longDescription": "",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null
},
"resources": [],
"shortDescription": "",
"targets": "all",
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"security": {
"csp": null
},
"updater": {
"active": false
},
"windows": [
{
"fullscreen": false,
"height": 768,
"resizable": true,
"title": "SoybeanAdmin",
"width": 1366
}
]
}
}

View File

@@ -21,10 +21,10 @@ const columns = defineModel<NaiveUI.TableColumnCheck[]>('columns', {
{{ $t('common.columnSetting') }}
</NButton>
</template>
<VueDraggable v-model="columns" :animation="150" filter=".none_draggable">
<VueDraggable v-model="columns">
<div v-for="item in columns" :key="item.key" class="h-36px flex-y-center rd-4px hover:(bg-primary bg-opacity-20)">
<icon-mdi-drag class="mr-8px h-full cursor-move text-icon" />
<NCheckbox v-model:checked="item.checked" class="none_draggable flex-1">
<icon-mdi-drag class="mr-8px cursor-move text-icon" />
<NCheckbox v-model:checked="item.checked">
{{ item.title }}
</NCheckbox>
</div>

View File

@@ -1,6 +1,4 @@
<script setup lang="ts">
import { $t } from '@/locales';
defineOptions({
name: 'TableHeaderOperation'
});

View File

@@ -9,13 +9,9 @@ interface Props {
collapsed?: boolean;
/** Arrow style icon */
arrowIcon?: boolean;
zIndex?: number;
}
const props = withDefaults(defineProps<Props>(), {
arrowIcon: false,
zIndex: 98
});
const props = defineProps<Props>();
type NumberBool = 0 | 1;
@@ -40,12 +36,7 @@ const icon = computed(() => {
</script>
<template>
<ButtonIcon
:key="String(collapsed)"
:tooltip-content="collapsed ? $t('icon.expand') : $t('icon.collapse')"
tooltip-placement="bottom-start"
:z-index="zIndex"
>
<ButtonIcon :tooltip-content="collapsed ? $t('icon.expand') : $t('icon.collapse')" tooltip-placement="bottom-start">
<SvgIcon :icon="icon" />
</ButtonIcon>
</template>

View File

@@ -15,7 +15,7 @@ const icon = computed(() => (props.pin ? 'mdi-pin-off' : 'mdi-pin'));
<template>
<ButtonIcon
:tooltip-content="pin ? $t('icon.unpin') : $t('icon.pin')"
:tooltip-content="pin ? $t('icon.pin') : $t('icon.unpin')"
tooltip-placement="bottom-start"
:z-index="100"
>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import { createReusableTemplate } from '@vueuse/core';
import type { PopoverPlacement } from 'naive-ui';
import { twMerge } from 'tailwind-merge';
defineOptions({
name: 'ButtonIcon',
@@ -20,29 +21,60 @@ interface Props {
}
const props = withDefaults(defineProps<Props>(), {
class: '',
class: 'h-36px text-icon',
icon: '',
tooltipContent: '',
tooltipPlacement: 'bottom',
zIndex: 98
});
const DEFAULT_CLASS = 'h-[36px] text-icon';
interface ButtonProps {
className: string;
}
const [DefineButton, Button] = createReusableTemplate<ButtonProps>();
const cls = computed(() => {
let clsStr = props.class;
if (!clsStr.includes('h-')) {
clsStr += ' h-36px';
}
if (!clsStr.includes('text-')) {
clsStr += ' text-icon';
}
return clsStr;
});
</script>
<template>
<NTooltip :placement="tooltipPlacement" :z-index="zIndex" :disabled="!tooltipContent">
<!-- define component start: Button -->
<DefineButton v-slot="{ $slots, className }">
<NButton quaternary :class="className">
<div class="flex-center gap-8px">
<component :is="$slots.default" />
</div>
</NButton>
</DefineButton>
<!-- define component end: Button -->
<NTooltip v-if="tooltipContent" :placement="tooltipPlacement" :z-index="zIndex">
<template #trigger>
<NButton quaternary :class="twMerge(DEFAULT_CLASS, props.class)" v-bind="$attrs">
<div class="flex-center gap-8px">
<slot>
<SvgIcon :icon="icon" />
</slot>
</div>
</NButton>
<Button :class-name="cls" v-bind="$attrs">
<slot>
<SvgIcon :icon="icon" />
</slot>
</Button>
</template>
{{ tooltipContent }}
</NTooltip>
<Button v-else :class-name="cls" v-bind="$attrs">
<slot>
<SvgIcon :icon="icon" />
</slot>
</Button>
</template>
<style scoped></style>

View File

@@ -2,7 +2,7 @@
import { computed, useAttrs } from 'vue';
import { Icon } from '@iconify/vue';
defineOptions({ name: 'SvgIcon', inheritAttrs: false });
defineOptions({ name: 'SvgIcon' });
/**
* Props

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { getPaletteColorByNumber } from '@sa/color';
import { getColorPalette } from '@sa/utils';
defineOptions({ name: 'WaveBg' });
@@ -11,8 +11,8 @@ interface Props {
const props = defineProps<Props>();
const lightColor = computed(() => getPaletteColorByNumber(props.themeColor, 200));
const darkColor = computed(() => getPaletteColorByNumber(props.themeColor, 500));
const lightColor = computed(() => getColorPalette(props.themeColor, 3));
const darkColor = computed(() => getColorPalette(props.themeColor, 6));
</script>
<template>

View File

@@ -146,10 +146,6 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
await onUpdated?.(chart!);
}
function setOptions(options: T) {
chart?.setOption(options);
}
/** render chart */
async function render() {
if (!isRendered()) {
@@ -229,7 +225,6 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
return {
domRef,
updateOptions,
setOptions
updateOptions
};
}

View File

@@ -92,6 +92,7 @@ export function useRouterPush(inSetup = true) {
}
return {
route,
routerPush,
routerBack,
routerPushByKey,

View File

@@ -1,7 +1,6 @@
import { computed, effectScope, onScopeDispose, reactive, ref, watch } from 'vue';
import type { Ref } from 'vue';
import type { PaginationProps } from 'naive-ui';
import { cloneDeep } from 'lodash-es';
import { useBoolean, useHookTable } from '@sa/hooks';
import { useAppStore } from '@/store/modules/app';
import { $t } from '@/locales';
@@ -14,14 +13,10 @@ export function useTable<A extends NaiveUI.TableApiFn>(config: NaiveUI.NaiveTabl
const scope = effectScope();
const appStore = useAppStore();
const isMobile = computed(() => appStore.isMobile);
const { apiFn, apiParams, immediate, showTotal } = config;
const { apiFn, apiParams, immediate } = config;
const SELECTION_KEY = '__selection__';
const EXPAND_KEY = '__expand__';
const {
loading,
empty,
@@ -70,12 +65,6 @@ export function useTable<A extends NaiveUI.TableApiFn>(config: NaiveUI.NaiveTabl
title: $t('common.check'),
checked: true
});
} else if (column.type === 'expand') {
checks.push({
key: EXPAND_KEY,
title: $t('common.expandColumn'),
checked: true
});
}
});
@@ -89,8 +78,6 @@ export function useTable<A extends NaiveUI.TableApiFn>(config: NaiveUI.NaiveTabl
columnMap.set(column.key as string, column);
} else if (column.type === 'selection') {
columnMap.set(SELECTION_KEY, column);
} else if (column.type === 'expand') {
columnMap.set(EXPAND_KEY, column);
}
});
@@ -137,20 +124,14 @@ export function useTable<A extends NaiveUI.TableApiFn>(config: NaiveUI.NaiveTabl
});
getData();
},
...(showTotal
? {
prefix: page => $t('datatable.itemCount', { total: page.itemCount })
}
: {})
}
});
// this is for mobile, if the system does not support mobile, you can use `pagination` directly
const mobilePagination = computed(() => {
const p: PaginationProps = {
...pagination,
pageSlot: isMobile.value ? 3 : 9,
prefix: !isMobile.value && showTotal ? pagination.prefix : undefined
pageSlot: appStore.isMobile ? 3 : 9
};
return p;
@@ -205,8 +186,7 @@ export function useTableOperate<T extends TableData = TableData>(data: Ref<T[]>,
function handleEdit(id: T['id']) {
operateType.value = 'edit';
const findItem = data.value.find(item => item.id === id) || null;
editingData.value = cloneDeep(findItem);
editingData.value = data.value.find(item => item.id === id) || null;
openDrawer();
}

View File

@@ -18,7 +18,6 @@ defineOptions({
const appStore = useAppStore();
const themeStore = useThemeStore();
const { menus } = setupMixMenuContext();
const layoutMode = computed(() => {
const vertical: LayoutMode = 'vertical';
@@ -66,7 +65,7 @@ function getSiderWidth() {
let w = isVerticalMix.value || isHorizontalMix.value ? mixWidth : width;
if (isVerticalMix.value && appStore.mixSiderFixed && menus.value.length) {
if (isVerticalMix.value && appStore.mixSiderFixed) {
w += mixChildMenuWidth;
}
@@ -78,12 +77,14 @@ function getSiderCollapsedWidth() {
let w = isVerticalMix.value || isHorizontalMix.value ? mixCollapsedWidth : collapsedWidth;
if (isVerticalMix.value && appStore.mixSiderFixed && menus.value.length) {
if (isVerticalMix.value && appStore.mixSiderFixed) {
w += mixChildMenuWidth;
}
return w;
}
setupMixMenuContext();
</script>
<template>

View File

@@ -1,47 +1,4 @@
import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useContext } from '@sa/hooks';
import { useRouteStore } from '@/store/modules/route';
import { useMixMenu } from '../hooks';
export const { setupStore: setupMixMenuContext, useStore: useMixMenuContext } = useContext('mix-menu', useMixMenu);
function useMixMenu() {
const route = useRoute();
const routeStore = useRouteStore();
const activeFirstLevelMenuKey = ref('');
function setActiveFirstLevelMenuKey(key: string) {
activeFirstLevelMenuKey.value = key;
}
function getActiveFirstLevelMenuKey() {
const { hideInMenu, activeMenu } = route.meta;
const name = route.name as string;
const routeName = (hideInMenu ? activeMenu : name) || name;
const [firstLevelRouteName] = routeName.split('_');
setActiveFirstLevelMenuKey(firstLevelRouteName);
}
const menus = computed(
() => routeStore.menus.find(menu => menu.key === activeFirstLevelMenuKey.value)?.children || []
);
watch(
() => route.name,
() => {
getActiveFirstLevelMenuKey();
},
{ immediate: true }
);
return {
activeFirstLevelMenuKey,
setActiveFirstLevelMenuKey,
getActiveFirstLevelMenuKey,
menus
};
}

View File

@@ -0,0 +1,44 @@
import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useRouteStore } from '@/store/modules/route';
export function useMixMenu() {
const route = useRoute();
const routeStore = useRouteStore();
const activeFirstLevelMenuKey = ref('');
function setActiveFirstLevelMenuKey(key: string) {
activeFirstLevelMenuKey.value = key;
}
function getActiveFirstLevelMenuKey() {
const { hideInMenu, activeMenu } = route.meta;
const name = route.name as string;
const routeName = (hideInMenu ? activeMenu : name) || name;
const [firstLevelRouteName] = routeName.split('_');
setActiveFirstLevelMenuKey(firstLevelRouteName);
}
const menus = computed(
() => routeStore.menus.find(menu => menu.key === activeFirstLevelMenuKey.value)?.children || []
);
watch(
() => route.name,
() => {
getActiveFirstLevelMenuKey();
},
{ immediate: true }
);
return {
activeFirstLevelMenuKey,
setActiveFirstLevelMenuKey,
getActiveFirstLevelMenuKey,
menus
};
}

View File

@@ -1,10 +1,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import { LAYOUT_SCROLL_EL_ID } from '@sa/materials';
import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
import { useRouteStore } from '@/store/modules/route';
import { useTabStore } from '@/store/modules/tab';
defineOptions({
name: 'GlobalContent'
@@ -22,31 +19,21 @@ withDefaults(defineProps<Props>(), {
const appStore = useAppStore();
const themeStore = useThemeStore();
const routeStore = useRouteStore();
const tabStore = useTabStore();
const transitionName = computed(() => (themeStore.page.animate ? themeStore.page.animateMode : ''));
function resetScroll() {
const el = document.querySelector(`#${LAYOUT_SCROLL_EL_ID}`);
el?.scrollTo({ left: 0, top: 0 });
}
</script>
<template>
<RouterView v-slot="{ Component, route }">
<Transition
:name="transitionName"
:name="themeStore.page.animateMode"
mode="out-in"
@before-leave="appStore.setContentXScrollable(true)"
@after-leave="resetScroll"
@after-enter="appStore.setContentXScrollable(false)"
>
<KeepAlive :include="routeStore.cacheRoutes">
<component
:is="Component"
v-if="appStore.reloadFlag"
:key="tabStore.getTabIdByRoute(route)"
:key="route.path"
:class="{ 'p-16px': showPadding }"
class="flex-grow bg-layout transition-300"
/>

View File

@@ -47,7 +47,7 @@ const headerMenus = computed(() => {
</script>
<template>
<DarkModeContainer class="h-full flex-y-center px-12px shadow-header">
<DarkModeContainer class="h-full flex-y-center shadow-header">
<GlobalLogo v-if="showLogo" class="h-full" :style="{ width: themeStore.sider.width + 'px' }" />
<HorizontalMenu v-if="showMenu" mode="horizontal" :menus="headerMenus" class="px-12px" />
<div v-else class="h-full flex-y-center flex-1-hidden">

View File

@@ -57,7 +57,7 @@ function updateExpandedKeys() {
}
function handleClickMenu(key: RouteKey) {
const query = routeStore.getRouteQueryOfMetaByKey(key);
const { query } = routeStore.getSelectedMenuMetaByKey(key) || {};
routerPushByKey(key, { query });
}

View File

@@ -2,7 +2,7 @@
import { computed } from 'vue';
import { createReusableTemplate } from '@vueuse/core';
import { SimpleScrollbar } from '@sa/materials';
import { transformColorWithOpacity } from '@sa/color';
import { transformColorWithOpacity } from '@sa/utils';
import { useAppStore } from '@/store/modules/app';
import { useRouteStore } from '@/store/modules/route';
import { useThemeStore } from '@/store/modules/theme';
@@ -68,7 +68,7 @@ function handleClickMixMenu(menu: App.Global.Menu) {
<component :is="icon" :class="[isMini ? 'text-icon-small' : 'text-icon-large']" />
<p
class="w-full ellipsis-text text-center text-12px transition-height-300"
:class="[isMini ? 'h-0 pt-0' : 'h-20px pt-4px']"
:class="[isMini ? 'h-0 pt-0' : 'h-24px pt-4px']"
>
{{ label }}
</p>
@@ -92,7 +92,6 @@ function handleClickMixMenu(menu: App.Global.Menu) {
<MenuToggler
arrow-icon
:collapsed="appStore.siderCollapse"
:z-index="99"
:class="{ 'text-white:88 !hover:text-white': inverted }"
@click="appStore.toggleSiderCollapse"
/>

View File

@@ -2,10 +2,10 @@
import { computed } from 'vue';
import { useBoolean } from '@sa/hooks';
import { useAppStore } from '@/store/modules/app';
import { useRouteStore } from '@/store/modules/route';
import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router';
import { $t } from '@/locales';
import { useMixMenuContext } from '../../context';
import { useMixMenu } from '../../hooks';
import FirstLevelMenu from './first-level-menu.vue';
import BaseMenu from './base-menu.vue';
@@ -15,15 +15,16 @@ defineOptions({
const appStore = useAppStore();
const themeStore = useThemeStore();
const routeStore = useRouteStore();
const { routerPushByKey } = useRouterPush();
const { bool: drawerVisible, setBool: setDrawerVisible } = useBoolean();
const { menus, activeFirstLevelMenuKey, setActiveFirstLevelMenuKey, getActiveFirstLevelMenuKey } = useMixMenuContext();
const { activeFirstLevelMenuKey, setActiveFirstLevelMenuKey, getActiveFirstLevelMenuKey } = useMixMenu();
const siderInverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
const hasMenus = computed(() => menus.value.length > 0);
const menus = computed(() => routeStore.menus.find(menu => menu.key === activeFirstLevelMenuKey.value)?.children || []);
const showDrawer = computed(() => hasMenus.value && (drawerVisible.value || appStore.mixSiderFixed));
const showDrawer = computed(() => (drawerVisible.value && menus.value.length) || appStore.mixSiderFixed);
function handleSelectMixMenu(menu: App.Global.Menu) {
setActiveFirstLevelMenuKey(menu.key);
@@ -48,15 +49,15 @@ function handleResetActiveMenu() {
</FirstLevelMenu>
<div
class="relative h-full transition-width-300"
:style="{ width: appStore.mixSiderFixed && hasMenus ? themeStore.sider.mixChildMenuWidth + 'px' : '0px' }"
:style="{ width: appStore.mixSiderFixed ? themeStore.sider.mixChildMenuWidth + 'px' : '0px' }"
>
<DarkModeContainer
class="absolute-lt h-full flex-col-stretch nowrap-hidden shadow-sm transition-all-300"
:inverted="siderInverted"
:style="{ width: showDrawer ? themeStore.sider.mixChildMenuWidth + 'px' : '0px' }"
>
<header class="flex-y-center justify-between px-12px" :style="{ height: themeStore.header.height + 'px' }">
<h2 class="text-16px text-primary font-bold">{{ $t('system.title') }}</h2>
<header class="flex-y-center justify-between" :style="{ height: themeStore.header.height + 'px' }">
<h2 class="pl-8px text-16px text-primary font-bold">{{ $t('system.title') }}</h2>
<PinToggler
:pin="appStore.mixSiderFixed"
:class="{ 'text-white:88 !hover:text-white': siderInverted }"

View File

@@ -34,9 +34,9 @@ function search() {
}
function handleClose() {
visible.value = false;
// handle with setTimeout to prevent user from seeing some operations
setTimeout(() => {
visible.value = false;
resultOptions.value = [];
keyword.value = '';
}, 200);
@@ -73,8 +73,10 @@ function getActivePathIndex() {
}
/** key enter */
function handleEnter() {
function handleEnter(e: Event | undefined) {
if (resultOptions.value?.length === 0 || activePath.value === '') return;
e?.preventDefault();
handleClose();
router.push(activePath.value);
}

View File

@@ -141,10 +141,6 @@ function init() {
tabStore.initTabStore(route);
}
function removeFocus() {
(document.activeElement as HTMLElement)?.blur();
}
// watch
watch(
() => route.fullPath,
@@ -166,11 +162,7 @@ init();
<template>
<DarkModeContainer class="size-full flex-y-center px-16px shadow-tab">
<div ref="bsWrapper" class="h-full flex-1-hidden">
<BetterScroll
ref="bsScroll"
:options="{ scrollX: true, scrollY: false, click: appStore.isMobile }"
@click="removeFocus"
>
<BetterScroll ref="bsScroll" :options="{ scrollX: true, scrollY: false, click: appStore.isMobile }">
<div
ref="tabRef"
class="h-full flex pr-18px"

Some files were not shown because too many files have changed in this diff Show More