mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-17 17:26:38 +08:00
Compare commits
381 Commits
tauri-v1.0
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
fd087f59fa | ||
|
9fa951aa06 | ||
|
12b25e0d58 | ||
|
e33f944a74 | ||
|
8d7f91dccf | ||
|
33ade53904 | ||
|
923eb98a5c | ||
|
e89b86ce56 | ||
|
03dd64c543 | ||
|
aeb6369005 | ||
|
133196f337 | ||
|
41191d54fb | ||
|
5cb1cebd88 | ||
|
a5c4b4e3b7 | ||
|
87a675bf62 | ||
|
4d42dcbea8 | ||
|
276d836c87 | ||
|
7d84062e2c | ||
|
afd604212b | ||
|
fcb89883fa | ||
|
dc674ce870 | ||
|
dbd995c12c | ||
|
7b2e510a2f | ||
|
da149e5bbd | ||
|
7c3dac4212 | ||
|
39b89a1234 | ||
|
c57f88aad2 | ||
|
3e4e17abd8 | ||
|
e6044d0fc7 | ||
|
2ed0b6484c | ||
|
222187d3b0 | ||
|
75455b006c | ||
|
dfb647a82c | ||
|
7fb5c72f7e | ||
|
41b5f49341 | ||
|
f35c250a89 | ||
|
1ff4d82d19 | ||
|
c3abc3df09 | ||
|
a013ea2c46 | ||
|
61244f0f2a | ||
|
85e40b1943 | ||
|
123d2c9074 | ||
|
05dc11e258 | ||
|
8527aa80b8 | ||
|
804860994e | ||
|
29698bef69 | ||
|
4e1b65b6c4 | ||
|
3cbaf4f4bf | ||
|
fa305146bc | ||
|
2e8cb35cfe | ||
|
a7c59adabc | ||
|
a6ecd3e083 | ||
|
3febb65d70 | ||
|
5d8b782d37 | ||
|
8b12ef9fd8 | ||
|
a6a47247ff | ||
|
4cc1487f46 | ||
|
b8112613ea | ||
|
a1a5c74c74 | ||
|
be6080ba0f | ||
|
3e0076d466 | ||
|
52c336d7e0 | ||
|
a03becdaed | ||
|
15163d7011 | ||
|
132e101243 | ||
|
9b9455d945 | ||
|
86da767e24 | ||
|
54e7d6d00a | ||
|
5cf3236475 | ||
|
6489ec46ae | ||
|
ac86247876 | ||
|
c9433e1710 | ||
|
60dd22624b | ||
|
56760245d4 | ||
|
d7aebb7dfa | ||
|
214341ee0b | ||
|
2849463bd6 | ||
|
101b6f9000 | ||
|
3e0eb72025 | ||
|
9f4fb8c3fb | ||
|
0dfcf18b64 | ||
|
ba38119efd | ||
|
ab9858c7d0 | ||
|
5c6ab0b595 | ||
|
b93c2036ac | ||
|
af4177e70b | ||
|
4eca4e3a7e | ||
|
0c809de644 | ||
|
a53eb10cdf | ||
|
d088f81345 | ||
|
17d7e52216 | ||
|
91de4a8ef7 | ||
|
09144dfe93 | ||
|
9ad5d7170a | ||
|
9d28b31c24 | ||
|
0c1741096b | ||
|
71e6307db2 | ||
|
b667eab76a | ||
|
4b3ac11bd5 | ||
|
24bb6d95cb | ||
|
4726498faa | ||
|
75cbfbbfe6 | ||
|
8dcda38560 | ||
|
2072f5850e | ||
|
cfaab8527a | ||
|
83ba798781 | ||
|
9669ca2041 | ||
|
baefdfd8c4 | ||
|
24e9e57a31 | ||
|
4da588c6ba | ||
|
3e72c3b45a | ||
|
ce66bca1a1 | ||
|
f35627e169 | ||
|
0ac95bdcf5 | ||
|
04d056479f | ||
|
60bbd2d145 | ||
|
38eeb678f3 | ||
|
207d6eb6ec | ||
|
6561f0b5f9 | ||
|
20f8ed311c | ||
|
5baf19d204 | ||
|
75997c1655 | ||
|
c4e16102cb | ||
|
be8556cd2b | ||
|
27b5222cfb | ||
|
79b2a28b5a | ||
|
531bfaf1b9 | ||
|
c3f1f69833 | ||
|
a44ea624f1 | ||
|
7cb43fc3c2 | ||
|
4c9f4e09b1 | ||
|
9c012c7d13 | ||
|
878d9c395c | ||
|
3830ec7a69 | ||
|
ef6cf93d96 | ||
|
7fa55905fc | ||
|
2fa400b113 | ||
|
73e917ad59 | ||
|
e9a2ee4a23 | ||
|
a1c14a1570 | ||
|
b16a96323a | ||
|
26c93dff9a | ||
|
5094f0eea8 | ||
|
a8f923eb38 | ||
|
e6aa25e9f8 | ||
|
968370b1aa | ||
|
cb1d44525d | ||
|
939c512f02 | ||
|
3f23993a83 | ||
|
ebc838c3e0 | ||
|
5646a50ddf | ||
|
217a3bb259 | ||
|
d37591dd7a | ||
|
cf67d55cbf | ||
|
98b75c22c3 | ||
|
993e9caf79 | ||
|
f26d0a61eb | ||
|
eed617f9eb | ||
|
ea8aa6c4e6 | ||
|
4dde4c22b1 | ||
|
59f07d8ac4 | ||
|
e8378318c6 | ||
|
840273474f | ||
|
4e55b0e9bd | ||
|
5377002932 | ||
|
c469512bd4 | ||
|
613c836e0f | ||
|
f73f70c51b | ||
|
a0b76daeff | ||
|
f6bd6b8603 | ||
|
087e532613 | ||
|
00f41dd25e | ||
|
1d1b148c8f | ||
|
958d0baf3f | ||
|
0206969e98 | ||
|
527fd79df2 | ||
|
06971f3935 | ||
|
f4eeb2ed63 | ||
|
793b16e9ba | ||
|
cf9fc7d31f | ||
|
58fc09623c | ||
|
276ea7fa2f | ||
|
96c1044ffb | ||
|
a91335d74b | ||
|
3e2a993db8 | ||
|
cf0192ad71 | ||
|
a454b620f1 | ||
|
752ec1e9e7 | ||
|
e035eab223 | ||
|
be13ca274b | ||
|
72ede8bf9c | ||
|
0d20e4c915 | ||
|
c791adc6bc | ||
|
693f704690 | ||
|
49f60b2d50 | ||
|
425c69acc2 | ||
|
f7de3fd0f1 | ||
|
c7f6f2a537 | ||
|
531432d5ff | ||
|
ff1d50461f | ||
|
b5027c8f3e | ||
|
1e67ae8c49 | ||
|
5c67d0650e | ||
|
af735e87b8 | ||
|
b336841567 | ||
|
b094d6882b | ||
|
b94baa18c1 | ||
|
3eeace94dd | ||
|
ad2f24707a | ||
|
cd9d58d4de | ||
|
2198b982f0 | ||
|
376e2f7d84 | ||
|
069fa8a817 | ||
|
b5a723cb5f | ||
|
79e85bc6b5 | ||
|
bf7183747b | ||
|
aabb2a49c4 | ||
|
7026126a57 | ||
|
c212565248 | ||
|
813d8ce4c7 | ||
|
20a8127434 | ||
|
ec8cadd183 | ||
|
4470cb4e8c | ||
|
8f9a70505c | ||
|
9094b21cbb | ||
|
6259287240 | ||
|
22004ff4dc | ||
|
08827a42a3 | ||
|
6a6eb9afd6 | ||
|
ff51b72dac | ||
|
fe06b8c499 | ||
|
4b63bbcf67 | ||
|
5531a68641 | ||
|
584cd54d6d | ||
|
2bec899031 | ||
|
83862ba76e | ||
|
060c0a91c7 | ||
|
76649e2a08 | ||
|
ddf3823a55 | ||
|
e75fd73f34 | ||
|
1f4647b995 | ||
|
910dfca8bb | ||
|
ffb48b15bc | ||
|
e6086f0f35 | ||
|
9b05d73e6d | ||
|
bd69c00e74 | ||
|
d08a3817d1 | ||
|
d0380ce5cb | ||
|
756f84ab1d | ||
|
5a4f844d24 | ||
|
59faf15229 | ||
|
5bd96b8dd3 | ||
|
8efdb10d86 | ||
|
2aa85c6f93 | ||
|
3ad438984e | ||
|
f1850416c8 | ||
|
086bad474e | ||
|
42b121a117 | ||
|
db64b0e2e9 | ||
|
40d0f8ac1c | ||
|
87b18386f5 | ||
|
4c1c7e6599 | ||
|
ac78b9a394 | ||
|
e379d6ce67 | ||
|
64fc0996e3 | ||
|
bc8dc47d7f | ||
|
6a83f869f9 | ||
|
419ea42343 | ||
|
24ff852180 | ||
|
57b4a9d532 | ||
|
0774a51525 | ||
|
68ea974617 | ||
|
4ea9c85757 | ||
|
fa56e9c9e7 | ||
|
071241f8cb | ||
|
1978397c90 | ||
|
e4d53aa7b5 | ||
|
e7f0a30bd8 | ||
|
4d17cfdcd3 | ||
|
46e90a7208 | ||
|
3b47b5a56f | ||
|
073fbcfc36 | ||
|
0a90dd3764 | ||
|
60f3b14085 | ||
|
18b3f05382 | ||
|
72ccb6bf6d | ||
|
21df57b71c | ||
|
7bd43df5f7 | ||
|
599b4e1947 | ||
|
003e145e77 | ||
|
c4b5c65625 | ||
|
ebe55af7d5 | ||
|
a6b92103a6 | ||
|
f4513e1e38 | ||
|
734ef9852c | ||
|
c137b97089 | ||
|
b8f9e60211 | ||
|
ce2a75b5a8 | ||
|
d0f17a440c | ||
|
1ed2eb5fdd | ||
|
d3849baaff | ||
|
413a8b29c7 | ||
|
52188d8853 | ||
|
fbc2e61f49 | ||
|
55d7cc09d7 | ||
|
dae2aa5513 | ||
|
e3bd397248 | ||
|
42f950f819 | ||
|
b3e9bbaeaa | ||
|
c89e00d194 | ||
|
4708eded4c | ||
|
3d10ef1a90 | ||
|
145300e95c | ||
|
3ddb17a0f5 | ||
|
6b465c0ebd | ||
|
dbe31eb1dc | ||
|
a8dbc03e23 | ||
|
c0ed1f26f7 | ||
|
a1920fcad9 | ||
|
34999971fd | ||
|
1cb3816e48 | ||
|
de9567420a | ||
|
4749b411bd | ||
|
fbd80c289a | ||
|
da12d4a5cd | ||
|
3e61eab498 | ||
|
1c3b4734fa | ||
|
93c7ff7122 | ||
|
1e14293d67 | ||
|
19e65c1a9f | ||
|
e57bf0b076 | ||
|
5d45cef1f0 | ||
|
d460e5cc6d | ||
|
3b5e4b3405 | ||
|
ec5b1c3f84 | ||
|
1fc34cc5f8 | ||
|
dcd51f4cda | ||
|
09f6464678 | ||
|
fdde679c70 | ||
|
b266035800 | ||
|
a1e432f81e | ||
|
82eabab753 | ||
|
c347528bbb | ||
|
d335df6f46 | ||
|
0d45b86cde | ||
|
516f46a47e | ||
|
b52432a792 | ||
|
cf5bc88a75 | ||
|
03b1fbacc4 | ||
|
9a66979f09 | ||
|
c695208f62 | ||
|
3ceeb6f942 | ||
|
04aa0972f9 | ||
|
cb83d6d90d | ||
|
79d9c5143a | ||
|
5a5232bdf4 | ||
|
7392bebff9 | ||
|
23f283aa31 | ||
|
42e16a0165 | ||
|
ac92817343 | ||
|
93191737dd | ||
|
d9af5aa2d3 | ||
|
d984f75b80 | ||
|
27c53cd688 | ||
|
b7f0749170 | ||
|
cb8ea2531d | ||
|
c6648b6c8b | ||
|
bb74d9949b | ||
|
60beff7e63 | ||
|
d6eda8f9ed | ||
|
f4a9cf8339 | ||
|
efc0e25c7f | ||
|
35310ed73c | ||
|
001059cca0 | ||
|
4af884c08b | ||
|
41e470ed6e | ||
|
2f15a2ac6a | ||
|
1c72dc76ee | ||
|
a1b484a8eb | ||
|
d9410e416e | ||
|
769d84a7f4 |
20
.env
20
.env
@ -1,3 +1,5 @@
|
||||
# the base url of the application, the default is "/"
|
||||
# if use a sub directory, it must be end with "/", like "/admin/" but not "/admin"
|
||||
VITE_BASE_URL=/
|
||||
|
||||
VITE_APP_TITLE=SoybeanAdmin
|
||||
@ -36,7 +38,23 @@ VITE_SERVICE_LOGOUT_CODES=8888,8889
|
||||
VITE_SERVICE_MODAL_LOGOUT_CODES=7777,7778
|
||||
|
||||
# token expired codes of backend service, when the code is received, it will refresh the token and resend the request
|
||||
VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998
|
||||
VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998,3333
|
||||
|
||||
# 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_
|
||||
|
||||
# used to control whether the program automatically detects updates
|
||||
VITE_AUTOMATICALLY_DETECT_UPDATE=Y
|
||||
|
||||
# show proxy url log in terminal
|
||||
VITE_PROXY_LOG=Y
|
||||
|
||||
# used to control whether to launch editor
|
||||
# by the way, this plugin is only available in dev mode, not in build mode
|
||||
VITE_DEVTOOLS_LAUNCH_EDITOR=code
|
||||
|
@ -1,5 +1,5 @@
|
||||
# backend service base url, prod environment
|
||||
VITE_SERVICE_BASE_URL=https://mock.apifox.com/m1/3109515-0-default
|
||||
VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
|
||||
|
||||
# other backend service base url, prod environment
|
||||
VITE_OTHER_SERVICE_BASE_URL= `{
|
||||
|
@ -1,5 +1,5 @@
|
||||
# backend service base url, test environment
|
||||
VITE_SERVICE_BASE_URL=https://mock.apifox.com/m1/3109515-0-default
|
||||
VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
|
||||
|
||||
# other backend service base url, test environment
|
||||
VITE_OTHER_SERVICE_BASE_URL= `{
|
||||
|
2
.github/ISSUE_TEMPLATE/bug-report_cn.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug-report_cn.yaml
vendored
@ -1,4 +1,4 @@
|
||||
name: Bug提交
|
||||
name: 🐞 Bug提交
|
||||
description: 在使用软件或功能的过程中遇到了错误
|
||||
title: '[Bug]: '
|
||||
labels: [ "bug?" ]
|
||||
|
2
.github/ISSUE_TEMPLATE/bug-report_en.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug-report_en.yaml
vendored
@ -1,4 +1,4 @@
|
||||
name: Bug Report
|
||||
name: 🐞 Bug Report
|
||||
description: Encountered an error while using the software or feature
|
||||
title: '[Bug]: '
|
||||
labels: [ "bug?" ]
|
||||
|
48
.github/ISSUE_TEMPLATE/feature_request_cn.yaml
vendored
Normal file
48
.github/ISSUE_TEMPLATE/feature_request_cn.yaml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
name: 🚀 功能请求
|
||||
description: 提出一个想法以帮助我们改进W&B
|
||||
title: "[功能]: "
|
||||
labels:
|
||||
- "功能请求"
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**感谢 :heart: 您花时间填写此功能请求报告!**
|
||||
我们恳请您搜索看看您的功能是否[已经存在](https://github.com/soybeanjs/soybean-admin/issues?q=is%3Aissue+sort%3Acreated-desc+)。
|
||||
|
||||
我们也很乐意接受用户的贡献。有关更多详细信息,请参阅[此处](https://github.com/soybeanjs/soybean-admin/blob/main/README.zh_CN.md#%E5%A6%82%E4%BD%95%E8%B4%A1%E7%8C%AE)。
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 描述
|
||||
description: |
|
||||
对您感兴趣的功能的清晰简洁的描述。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 建议的解决方案
|
||||
description: |
|
||||
描述您想要的解决方案。对您希望发生的事情的清晰简洁的描述。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 替代方案
|
||||
description: |
|
||||
描述您考虑过的替代方案。
|
||||
对您考虑过的任何替代解决方案或功能的清晰简洁的描述。
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 额外的上下文
|
||||
description: |
|
||||
在此处添加有关问题的其他上下文。
|
||||
validations:
|
||||
required: false
|
48
.github/ISSUE_TEMPLATE/feature_request_en.yaml
vendored
Normal file
48
.github/ISSUE_TEMPLATE/feature_request_en.yaml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
name: 🚀 Feature Request
|
||||
description: Suggest an idea to help us improve W&B
|
||||
title: "[Feature]: "
|
||||
labels:
|
||||
- "feature_request"
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Thanks :heart: for taking the time to fill out this feature request report!**
|
||||
We kindly ask that you search to see if an issue [already exists](https://github.com/soybeanjs/soybean-admin/issues?q=is%3Aissue+sort%3Acreated-desc+) for your feature.
|
||||
|
||||
We are also happy to accept contributions from our users. For more details see [here](https://github.com/soybeanjs/soybean-admin/blob/main/README.md#how-to-contribute).
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
A clear and concise description of the feature you're interested in.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Suggested Solution
|
||||
description: |
|
||||
Describe the solution you'd like. A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Alternatives
|
||||
description: |
|
||||
Describe alternatives you've considered.
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: |
|
||||
Add any other context about the problem here.
|
||||
validations:
|
||||
required: false
|
1
.npmrc
1
.npmrc
@ -1,3 +1,4 @@
|
||||
registry=https://registry.npmmirror.com/
|
||||
shamefully-hoist=true
|
||||
ignore-workspace-root-check=true
|
||||
link-workspace-packages=true
|
||||
|
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
@ -6,9 +6,6 @@
|
||||
"dbaeumer.vscode-eslint",
|
||||
"editorconfig.editorconfig",
|
||||
"esbenp.prettier-vscode",
|
||||
"formulahendry.auto-close-tag",
|
||||
"formulahendry.auto-complete-tag",
|
||||
"formulahendry.auto-rename-tag",
|
||||
"lokalise.i18n-ally",
|
||||
"mhutchie.git-graph",
|
||||
"mikestead.dotenv",
|
||||
@ -17,6 +14,7 @@
|
||||
"sdras.vue-vscode-snippets",
|
||||
"vue.volar",
|
||||
"whtouche.vscode-js-console-utils",
|
||||
"zhuangtongfa.material-theme"
|
||||
"zhuangtongfa.material-theme",
|
||||
"tu6ge.naive-ui-intelligence"
|
||||
]
|
||||
}
|
||||
|
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@ -12,9 +12,11 @@
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "TS Debugger",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/tsx",
|
||||
"runtimeExecutable": "tsx",
|
||||
"skipFiles": ["<node_internals>/**", "${workspaceFolder}/node_modules/**"],
|
||||
"program": "${file}"
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
20
.vscode/settings.json
vendored
20
.vscode/settings.json
vendored
@ -3,17 +3,29 @@
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
"editor.formatOnSave": false,
|
||||
"eslint.validate": ["html", "css", "scss", "json", "jsonc"],
|
||||
"eslint.validate": [
|
||||
"html",
|
||||
"css",
|
||||
"scss",
|
||||
"json",
|
||||
"jsonc",
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue"
|
||||
],
|
||||
"i18n-ally.displayLanguage": "zh-cn",
|
||||
"i18n-ally.enabledParsers": ["ts"],
|
||||
"i18n-ally.enabledFrameworks": ["vue"],
|
||||
"i18n-ally.editor.preferEditor": true,
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.localesPaths": ["src/locales/langs"],
|
||||
"i18n-ally.parsers.typescript.compilerOptions": {
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"prettier.enable": false,
|
||||
"unocss.root": ["./"],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"vue.server.hybridMode": true
|
||||
"unocss.root": ["./"]
|
||||
}
|
||||
|
51
.vscode/vue3.code-snippets
vendored
Normal file
51
.vscode/vue3.code-snippets
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"Print soy Vue3 SFC page": {
|
||||
"scope": "vue",
|
||||
"prefix": ["v3","page","view"],
|
||||
"body": [
|
||||
"<script lang=\"ts\" setup>",
|
||||
"//$1",
|
||||
"</script>\n",
|
||||
"<template>",
|
||||
" <div class=\"\">",
|
||||
" <!-- page only one root element -->",
|
||||
" $2",
|
||||
" </div>",
|
||||
"</template>\n",
|
||||
],
|
||||
},
|
||||
|
||||
"Print soy Vue3 SFC Component": {
|
||||
"scope": "vue",
|
||||
"prefix": ["component","comp"],
|
||||
"body": [
|
||||
"<script lang=\"ts\" setup>",
|
||||
"//$1",
|
||||
"</script>\n",
|
||||
"<template>",
|
||||
" <div class=\"\">",
|
||||
" $2",
|
||||
" </div>",
|
||||
"</template>\n",
|
||||
],
|
||||
},
|
||||
"Print soy style": {
|
||||
"scope": "vue",
|
||||
"prefix": "st",
|
||||
"body": ["<style scoped>", "//", "</style>\n"],
|
||||
},
|
||||
"Print soy script": {
|
||||
"scope": "vue",
|
||||
"prefix": "sc",
|
||||
"body": ["<script lang=\"ts\" setup>", "//$3", "</script>\n"],
|
||||
},
|
||||
"Print soy template": {
|
||||
"scope": "vue",
|
||||
"prefix": "te",
|
||||
"body": [
|
||||
"<template>",
|
||||
" <div class=\"\">$1</div>",
|
||||
"</template>\n",
|
||||
],
|
||||
},
|
||||
}
|
1541
CHANGELOG.md
1541
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
634
CHANGELOG.zh_CN.md
Normal file
634
CHANGELOG.zh_CN.md
Normal file
@ -0,0 +1,634 @@
|
||||
# 更新日志
|
||||
|
||||
## [v1.3.5](https://github.com/soybeanjs/soybean-admin/compare/v1.3.4...v1.3.5) (2024-09-07)
|
||||
|
||||
### 🚀 新功能
|
||||
|
||||
- **packages**:
|
||||
- @sa/axios: 成功时将响应添加到 flatRequest - by @soybeanjs [<samp>(c4e16)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c4e1610)
|
||||
- **projects**:
|
||||
- README.zh_CN.md 添加合作推广 - by @PZ-18664918826 in https://github.com/soybeanjs/soybean-admin/issues/601 [<samp>(2fa40)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2fa400b)
|
||||
- 根据 Apache 规范添加更多提交类型 - by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/610 [<samp>(878d9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/878d9c3)
|
||||
- 配置是否支持自动更新。关闭#612 - by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/617 和 https://github.com/soybeanjs/soybean-admin/issues/612 [<samp>(4c9f4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4c9f4e0)
|
||||
- 添加应用错误处理程序。关闭 #587 - by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/587 [<samp>(be855)</samp>](https://github.com/soybeanjs/soybean-admin/commit/be8556c)
|
||||
|
||||
### 🐞 Bug 修复
|
||||
|
||||
- **deps**:
|
||||
- 将 json5 从 devDependencies 移到 dependencies 以支持生产环境使用 - by @mufeng889 in https://github.com/soybeanjs/soybean-admin/issues/618 [<samp>(7cb43)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7cb43fc)
|
||||
- **projects**:
|
||||
- 避免检索缓存的 HTML - by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/605 [<samp>(ef6cf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ef6cf93)
|
||||
- 修复登录重定向 - by @soybeanjs [<samp>(3830e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3830ec7)
|
||||
- 修复侧边栏折叠时的 vertical-mix-menu。修复 #608 - by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/608 [<samp>(c3f1f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c3f1f69)
|
||||
- 修复 activeMenu 为父菜单时的面包屑。修复 #589 - by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/589 [<samp>(79b2a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/79b2a28)
|
||||
- 修复遇到多请求时的刷新令牌。修复 #581 - by @soybeanjs in https://github.com/soybeanjs/soybean-admin/issues/581 [<samp>(27b52)</samp>](https://github.com/soybeanjs/soybean-admin/commit/27b5222)
|
||||
- **types**:
|
||||
- 修复 TableApiFn 的类型 - by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/599 [<samp>(26c93)</samp>](https://github.com/soybeanjs/soybean-admin/commit/26c93df)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **projects**: 优化菜单 selectedKey - by @soybeanjs [<samp>(531bf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/531bfaf)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **projects**:
|
||||
- 更新 README - by @mufeng889 in https://github.com/soybeanjs/soybean-admin/issues/594 [<samp>(a8f92)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a8f923e)
|
||||
- 更新 README - by @soybeanjs [<samp>(e9a2e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e9a2ee4)
|
||||
- 更新 README - by @soybeanjs [<samp>(73e91)</samp>](https://github.com/soybeanjs/soybean-admin/commit/73e917a)
|
||||
- 更新文档中重要信息的位置 - by **Azir** [<samp>(9c012)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9c012c7)
|
||||
|
||||
### 🏡 杂项
|
||||
|
||||
- **deps**:
|
||||
- 更新依赖 - by @soybeanjs [<samp>(a1c14)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a1c14a1)
|
||||
- 更新依赖 - by @soybeanjs [<samp>(7fa55)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7fa5590)
|
||||
- 更新依赖 - by @soybeanjs [<samp>(a44ea)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a44ea62)
|
||||
- **projects**:
|
||||
- 使用 json5 解析环境变量 `VITE_OTHER_SERVICE_BASE_URL` 并修复代理启用 - by @soybeanjs [<samp>(b16a9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b16a963)
|
||||
|
||||
### 🎨 样式
|
||||
|
||||
- **projects**: 将脚本 czh 重命名为 commit:zh - by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/597 [<samp>(5094f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5094f0e)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs) [](https://github.com/mufeng889) [](https://github.com/Azir-11) [](https://github.com/PZ-18664918826)
|
||||
[Azir](mailto:2075125282@qq.com),
|
||||
|
||||
## [v1.3.4](https://github.com/honghuangdc/soybean-admin/compare/v1.3.3...v1.3.4) (2024-08-01)
|
||||
|
||||
### 🚨 重大变更
|
||||
|
||||
- **projects**: 当 vertical-mix 布局为 mixSiderFixed 时,不重置活动菜单 - by @honghuangdc [<samp>(939c5)</samp>](https://github.com/honghuangdc/soybean-admin/commit/939c512)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **projects**: 优化代码 - by @honghuangdc [<samp>(cb1d4)</samp>](https://github.com/honghuangdc/soybean-admin/commit/cb1d445)
|
||||
|
||||
### 🏡 杂项
|
||||
|
||||
- **projects**: 更新依赖并修复 vue-router 类型 - by @honghuangdc [<samp>(96837)</samp>](https://github.com/honghuangdc/soybean-admin/commit/968370b)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc)
|
||||
|
||||
## [v1.3.3](https://github.com/honghuangdc/soybean-admin/compare/v1.3.2...v1.3.3) (2024-07-30)
|
||||
|
||||
### 🐞 Bug 修复
|
||||
|
||||
- **projects**: 修复水印设置 - by @honghuangdc [<samp>(5646a)</samp>](https://github.com/honghuangdc/soybean-admin/commit/5646a50)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **projects**: 更新 CHANGELOG - by @honghuangdc [<samp>(ebc83)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ebc838c)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc)
|
||||
|
||||
## [v1.3.2](https://github.com/honghuangdc/soybean-admin/compare/v1.3.1...v1.3.2) (2024-07-30)
|
||||
|
||||
### 🚀 新功能
|
||||
|
||||
- **项目**:
|
||||
- 添加颜色渐变模式。关闭 #567 - 由 @Azir-11 在 https://github.com/honghuangdc/soybean-admin/issues/569 和 https://github.com/honghuangdc/soybean-admin/issues/567 [<samp>(4dde4)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4dde4c2)
|
||||
- 添加全屏水印。关闭 #571 - 由 @paynezhuang 在 https://github.com/honghuangdc/soybean-admin/issues/573 和 https://github.com/honghuangdc/soybean-admin/issues/571 [<samp>(ea8aa)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ea8aa6c)
|
||||
|
||||
### 🐞 Bug 修复
|
||||
|
||||
- **项目**: 修复 vertical-mix 菜单选中问题 - 由 @honghuangdc [<samp>(59f07)</samp>](https://github.com/honghuangdc/soybean-admin/commit/59f07d8)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **项目**:
|
||||
- 添加类型 WatermarkProps - 由 @honghuangdc [<samp>(f26d0)</samp>](https://github.com/honghuangdc/soybean-admin/commit/f26d0a6)
|
||||
- 移除 home NAlert 可关闭 - 由 @honghuangdc [<samp>(98b75)</samp>](https://github.com/honghuangdc/soybean-admin/commit/98b75c2)
|
||||
- 优化响应代码比较 - 由 @honghuangdc [<samp>(cf67d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/cf67d55)
|
||||
- **类型**:
|
||||
- 移除无用类型。 - 由 **Azir** [<samp>(eed61)</samp>](https://github.com/honghuangdc/soybean-admin/commit/eed617f)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 更新更新日志 - 由 @honghuangdc [<samp>(d3759)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d37591d)
|
||||
|
||||
### 🏡 杂项
|
||||
|
||||
- **依赖**: 更新依赖 - 由 @honghuangdc [<samp>(993e9)</samp>](https://github.com/honghuangdc/soybean-admin/commit/993e9ca)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc) [](https://github.com/paynezhuang) [](https://github.com/Azir-11)
|
||||
[Azir](mailto:2075125282@qq.com),
|
||||
|
||||
## [v1.3.1](https://github.com/honghuangdc/soybean-admin/compare/v1.3.0...v1.3.1) (2024-07-22)
|
||||
|
||||
### 🐞 Bug 修复
|
||||
|
||||
- **项目**:
|
||||
- 修复顶部菜单混合和反向模式下侧边栏宽度异常的问题 - 由 @Azir-11 在 https://github.com/honghuangdc/soybean-admin/issues/562 修复 [<samp>(c4695)</samp>](https://github.com/honghuangdc/soybean-admin/commit/c469512)
|
||||
- 修复水平混合菜单反转问题。修复 #563 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/563 修复 [<samp>(4e55b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4e55b0e)
|
||||
- 修复当布局来自移动端时垂直菜单不渲染的问题 - 由 @honghuangdc 修复 [<samp>(84027)</samp>](https://github.com/honghuangdc/soybean-admin/commit/8402734)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 更新更新日志 - 由 @honghuangdc 更新 [<samp>(613c8)</samp>](https://github.com/honghuangdc/soybean-admin/commit/613c836)
|
||||
|
||||
### 🎨 样式
|
||||
|
||||
- **其他**: 修改灰度模式的中文名称 - 由 **Azir** 修改 [<samp>(53770)</samp>](https://github.com/honghuangdc/soybean-admin/commit/5377002)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc) [](https://github.com/Azir-11)
|
||||
[Azir](mailto:2075125282@qq.com),
|
||||
|
||||
|
||||
## [v1.3.0](https://github.com/soybeanjs/soybean-admin/compare/v1.2.8...v1.3.0) (2024-07-22)
|
||||
|
||||
### 🚨 破坏性变更
|
||||
|
||||
- **项目**: 重构全局菜单 & 支持 `reversed-horizontal-mix-menu`。关闭 #365 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/365 提出 [<samp>(087e5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/087e532)
|
||||
|
||||
### 🚀 功能
|
||||
|
||||
- **包**:
|
||||
- `@sa/scripts`: 命令 `gitCommit` 支持中文 - 由 @mmdapl 在 https://github.com/soybeanjs/soybean-admin/issues/548 提出 [<samp>(06971)</samp>](https://github.com/soybeanjs/soybean-admin/commit/06971f3)
|
||||
- @sa/axios: 用 AbortController 替换 CancelTokenSource。关闭 #530, 关闭 #532 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/530 和 https://github.com/soybeanjs/soybean-admin/issues/532 提出 [<samp>(527fd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/527fd79)
|
||||
- @sa/scripts: 为命令 `gitCommitVerify` 添加忽略模式列表。关闭 #504 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/504 提出 [<samp>(958d0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/958d0ba)
|
||||
- **项目**:
|
||||
- 使分支 `main` 更精简 & 修改请求重试次数为 0 - 由 @Azir-11 提出 [<samp>(793b1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/793b16e)
|
||||
|
||||
### 🐞 修复
|
||||
|
||||
- **钩子**: 当 pagesize 返回 0 时防止程序冻结 - 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/545 提出 [<samp>(f4eeb)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f4eeb2e)
|
||||
|
||||
### 💅 重构
|
||||
|
||||
- **项目**:
|
||||
- 合并 `theme tokens` 和 `theme settings`。关闭 #379 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/379 提出 [<samp>(1d1b1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1d1b148)
|
||||
- 将 css 变量挂载到 root - 由 @honghuangdc 提出 [<samp>(00f41)</samp>](https://github.com/soybeanjs/soybean-admin/commit/00f41dd)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 更新更新日志 - 由 @honghuangdc 提出 [<samp>(a0b76)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a0b76da)
|
||||
|
||||
### 🏡 杂务
|
||||
|
||||
- **依赖**: 更新依赖 - 由 @honghuangdc 提出 [<samp>(f6bd6)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f6bd6b8)
|
||||
- **项目**: 添加脚本 `czh` - 由 @honghuangdc 提出 [<samp>(02069)</samp>](https://github.com/soybeanjs/soybean-admin/commit/0206969)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc) [](https://github.com/mmdapl) [](https://github.com/Azir-11)
|
||||
|
||||
|
||||
## [v1.2.8](https://github.com/soybeanjs/soybean-admin/compare/v1.2.7...v1.2.8) (2024-07-20)
|
||||
|
||||
### 🐞 修复
|
||||
|
||||
- **包**:
|
||||
- @sa/hooks: 修复 useHookTable 的 searchParams。修复了 #552 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/552 提出 [<samp>(96c10)</samp>](https://github.com/soybeanjs/soybean-admin/commit/96c1044)
|
||||
- **类型**:
|
||||
- 修复了引用类型错误 - 由 **dodu2014** 在 https://github.com/soybeanjs/soybean-admin/issues/551 提出 [<samp>(3e2a9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3e2a993)
|
||||
- 修复 useHookTable 的数据类型 - 由 @honghuangdc 提出 [<samp>(276ea)</samp>](https://github.com/soybeanjs/soybean-admin/commit/276ea7f)
|
||||
|
||||
### 💅 重构
|
||||
|
||||
- **项目**: 用 `klona` 替换 `lodash-es` 的 `cloneDeep` - 由 @honghuangdc 提出 [<samp>(a9133)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a91335d)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 更新更新日志 - 由 @honghuangdc 提出 [<samp>(58fc0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/58fc096)
|
||||
|
||||
### 🏡 杂务
|
||||
|
||||
- **依赖**: 更新依赖 - 由 @honghuangdc 提出 [<samp>(cf019)</samp>](https://github.com/soybeanjs/soybean-admin/commit/cf0192a)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc)
|
||||
[dodu2014](mailto:dodu@live.cn)
|
||||
|
||||
|
||||
## [v1.2.7](https://github.com/honghuangdc/soybean-admin/compare/v1.2.6...v1.2.7) (2024-07-12)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **项目**: 支持自定义菜单图标大小 - 由 @wynn-w 在 https://github.com/honghuangdc/soybean-admin/issues/534 提出 [<samp>(e035e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/e035eab)
|
||||
|
||||
### 🏡 杂务
|
||||
|
||||
- **依赖**:
|
||||
- 更新依赖 - 由 @honghuangdc [<samp>(72ede)</samp>](https://github.com/honghuangdc/soybean-admin/commit/72ede8b)
|
||||
- 更新依赖 - 由 @honghuangdc [<samp>(be13c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/be13ca2)
|
||||
- 更新依赖 - 由 @honghuangdc [<samp>(752ec)</samp>](https://github.com/honghuangdc/soybean-admin/commit/752ec1e)
|
||||
- **项目**:
|
||||
- 修复已弃用的配置配置 - 由 @paynezhuang 在 https://github.com/honghuangdc/soybean-admin/issues/524 提出 [<samp>(0d20e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/0d20e4c)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc) [](https://github.com/wynn-w) [](https://github.com/paynezhuang)
|
||||
|
||||
## [v1.2.6](https://github.com/honghuangdc/soybean-admin/compare/v1.2.5...v1.2.6) (2024-06-21)
|
||||
|
||||
### 🐞 修复
|
||||
|
||||
- **项目**:
|
||||
- 请求模态标题使用 i18n。修复 #507 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/507 提出 [<samp>(f7de3)</samp>](https://github.com/honghuangdc/soybean-admin/commit/f7de3fd)
|
||||
- 为 `useTable` 添加 `getDataByPage`。修复 #499 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/499 提出 [<samp>(425c6)</samp>](https://github.com/honghuangdc/soybean-admin/commit/425c69a)
|
||||
- 修复登录重定向到 routeHome 时,动态路由的 routeHome 与静态路由不同的问题。修复 #511 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/511 提出 [<samp>(49f60)</samp>](https://github.com/honghuangdc/soybean-admin/commit/49f60b2)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **项目**: 优化 `getRouteQueryOfLoginRoute` - 由 @honghuangdc [<samp>(693f7)</samp>](https://github.com/honghuangdc/soybean-admin/commit/693f704)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**:
|
||||
- 更新更新日志 - 由 @honghuangdc [<samp>(5c67d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/5c67d06)
|
||||
- 更新自述文件 - 由 @honghuangdc [<samp>(1e67a)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1e67ae8)
|
||||
|
||||
### 🏡 杂务
|
||||
|
||||
- **依赖**:
|
||||
- 更新依赖。关闭 #510 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/510 提出 [<samp>(53143)</samp>](https://github.com/honghuangdc/soybean-admin/commit/531432d)
|
||||
- 更新依赖 - 由 @honghuangdc [<samp>(c7f6f)</samp>](https://github.com/honghuangdc/soybean-admin/commit/c7f6f2a)
|
||||
|
||||
### 🤖 CI
|
||||
|
||||
- **项目**:
|
||||
- 添加 github 问题模板 - 由 @honghuangdc [<samp>(b5027)</samp>](https://github.com/honghuangdc/soybean-admin/commit/b5027c8)
|
||||
- 更新 github 问题模板 - 由 @honghuangdc [<samp>(ff1d5)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ff1d504)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc)
|
||||
|
||||
## [v1.2.5](https://github.com/soybeanjs/soybean-admin/compare/v1.2.4...v1.2.5) (2024-06-15)
|
||||
|
||||
### 🐞 错误修复
|
||||
|
||||
- **项目**: 修复登出后标签页异常缓存的问题。已修复 #495 - 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/495 [<samp>(3eeac)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3eeace9)
|
||||
|
||||
### 🔥 性能
|
||||
|
||||
- **项目**: 初始化静态路由功能不需要异步 - 由 **CHENZL** 在 https://github.com/soybeanjs/soybean-admin/issues/493 [<samp>(2198b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2198b98)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **项目**: 优化代码 - 由 @soybeanjs [<samp>(b94ba)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b94baa1)
|
||||
- **类型**: 提高全局类型的兼容性 - 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/494 [<samp>(cd9d5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/cd9d58d)
|
||||
- **工具**: 减少代码缩进,提高可读性 - 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/496 [<samp>(ad2f2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ad2f247)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 更新CHANGELOG - 由 @soybeanjs [<samp>(f70d2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f70d29b)
|
||||
|
||||
### 🏡 杂项
|
||||
|
||||
- **依赖**: 更新依赖 - 由 @soybeanjs [<samp>(b094d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b094d68)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs) [](https://github.com/Azir-11)
|
||||
[CHENZL](mailto:zlong5568863@qq.com)
|
||||
|
||||
|
||||
## [v1.2.4](https://github.com/soybeanjs/soybean-admin/compare/v1.2.3...v1.2.4) (2024-06-14)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **项目**:
|
||||
- 优化 `setupAppVersionNotification` - 由 @soybeanjs 提交 [<samp>(b5a72)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b5a723c)
|
||||
- 获取 'Asia/Shanghai' 时区的构建时间 - 由 @soybeanjs 提交 [<samp>(069fa)</samp>](https://github.com/soybeanjs/soybean-admin/commit/069fa8a)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs)
|
||||
|
||||
## [v1.2.3](https://github.com/soybeanjs/soybean-admin/compare/v1.2.2...v1.2.3) (2024-06-13)
|
||||
|
||||
### 🐞 修复错误
|
||||
|
||||
- **项目**:
|
||||
- 通过在index.html中添加color-scheme元标签修复移动浏览器主题问题 - 由 @KickCashew 在 https://github.com/soybeanjs/soybean-admin/issues/488 中提交 [<samp>(c2125)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c212565)
|
||||
- 修复二级目录组件为空 - 由 @paynezhuang 在 https://github.com/soybeanjs/soybean-admin/issues/491 中提交 [<samp>(aabb2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/aabb2a4)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**:
|
||||
- 修复超链接指向错误 - 由 **Azir** 提交 [<samp>(20a81)</samp>](https://github.com/soybeanjs/soybean-admin/commit/20a8127)
|
||||
- 更新 README - 由 @soybeanjs 提交 [<samp>(70261)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7026126)
|
||||
|
||||
### 🏡 杂项
|
||||
|
||||
- **依赖**:
|
||||
- 更新依赖 - 由 @soybeanjs 提交 [<samp>(813d8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/813d8ce)
|
||||
- 更新依赖 - 由 @soybeanjs 提交 [<samp>(bf718)</samp>](https://github.com/soybeanjs/soybean-admin/commit/bf71837)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs) [](https://github.com/paynezhuang) [](https://github.com/KickCashew)
|
||||
[Azir](mailto:2075125282@qq.com)
|
||||
|
||||
## [v1.2.2](https://github.com/honghuangdc/soybean-admin/compare/v1.2.1...v1.2.2) (2024-06-12)
|
||||
|
||||
### 🚀 特性
|
||||
|
||||
- **项目**: 切换标签时重置滚动位置 - 由 @soybeanjs 提交 [<samp>(9094b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/9094b21)
|
||||
|
||||
### 🐞 修复错误
|
||||
|
||||
- **项目**:
|
||||
- 在DEV模式下隐藏AppVersionNotification - 由 @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 - 由 @soybeanjs 在 https://github.com/honghuangdc/soybean-admin/issues/483 中提交 [<samp>(4470c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4470cb4)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 更新 README - 由 @soybeanjs 提交 [<samp>(8f9a7)</samp>](https://github.com/honghuangdc/soybean-admin/commit/8f9a705)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs) [](https://github.com/sigma-plus)
|
||||
|
||||
## [v1.2.1](https://github.com/honghuangdc/soybean-admin/compare/v1.2.0...v1.2.1) (2024-06-07)
|
||||
|
||||
### 🐞 修复错误
|
||||
|
||||
- **项目**:
|
||||
- 修复页面重新加载时获取用户信息 - 由 @soybeanjs 提交 [<samp>(ff51b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ff51b72)
|
||||
- 修复setupAppVersionNotification渲染 - 由 @soybeanjs 提交 [<samp>(6a6eb)</samp>](https://github.com/honghuangdc/soybean-admin/commit/6a6eb9a)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 更新CHANGELOG - 由 @soybeanjs [<samp>(fe06b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/fe06b8c)完成
|
||||
|
||||
### 🏡 日常任务
|
||||
|
||||
- **依赖**: 更新依赖 - 由 @soybeanjs [<samp>(08827)</samp>](https://github.com/honghuangdc/soybean-admin/commit/08827a4)完成
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs)
|
||||
|
||||
|
||||
## [v1.2.0](https://github.com/soybeanjs/soybean-admin/compare/v1.1.5...v1.2.0) (2024-06-06)
|
||||
|
||||
### 🚀 功能
|
||||
|
||||
- **项目**:
|
||||
- 支持系统新版本更新通知。关闭 #420 - 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/420 [<samp>(584cd)</samp>](https://github.com/soybeanjs/soybean-admin/commit/584cd54)
|
||||
- 在路由守卫中获取用户信息并从localStorage中移除。关闭 #459 - 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/459 [<samp>(5531a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5531a68)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 更新CHANGELOG - 由 @soybeanjs [<samp>(2bec8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2bec899)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs)
|
||||
|
||||
## [v1.1.5](https://github.com/soybeanjs/soybean-admin/compare/v1.1.4...v1.1.5) (2024-06-06)
|
||||
|
||||
### 🐞 错误修复
|
||||
|
||||
- **项目**: 修复注册组件名,CodeLogin => Register - 由 @m-xlsea 在 https://github.com/soybeanjs/soybean-admin/issues/478 [<samp>(ddf38)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ddf3823)
|
||||
|
||||
### 🏡 杂务
|
||||
|
||||
- **依赖**: 更新依赖 - 由 @soybeanjs [<samp>(060c0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/060c0a9)
|
||||
- **项目**: 更新 vscode 设置: vue 官方 - 由 @soybeanjs [<samp>(76649)</samp>](https://github.com/soybeanjs/soybean-admin/commit/76649e2)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs) [](https://github.com/m-xlsea)
|
||||
|
||||
## [v1.1.4](https://github.com/honghuangdc/soybean-admin/compare/v1.1.3...v1.1.4) (2024-06-06)
|
||||
|
||||
### 🐞 错误修复
|
||||
|
||||
- **utils**: 修复了按esc键时modalLogout的错误 - 由 @sigma-plus 在 https://github.com/honghuangdc/soybean-admin/issues/470 中提出 [<samp>(bd69c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/bd69c00)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **projects**: 优化了RouteMeta的备注 - 由 @soybeanjs 提出 [<samp>(ffb48)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ffb48b1)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **projects**:
|
||||
- 更新了CHANGELOG - 由 @soybeanjs 提出 [<samp>(756f8)</samp>](https://github.com/honghuangdc/soybean-admin/commit/756f84a)
|
||||
- 更新了Node&pnpm版本 - 由 @Azir-11 在 https://github.com/honghuangdc/soybean-admin/issues/472 中提出 [<samp>(9b05d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/9b05d73)
|
||||
|
||||
### 🏡 杂项
|
||||
|
||||
- **deps**:
|
||||
- 更新了依赖 - 由 @soybeanjs 提出 [<samp>(d0380)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d0380ce)
|
||||
- 更新了依赖 - 由 @soybeanjs 提出 [<samp>(1f464)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1f4647b)
|
||||
- **projects**:
|
||||
- 关闭了http代理 - 由 @soybeanjs 提出 [<samp>(d08a3)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d08a381)
|
||||
- 更新了mock url - 由 @soybeanjs 提出 [<samp>(e6086)</samp>](https://github.com/honghuangdc/soybean-admin/commit/e6086f0)
|
||||
- 更新了vscode设置 - 由 @soybeanjs 提出 [<samp>(910df)</samp>](https://github.com/honghuangdc/soybean-admin/commit/910dfca)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs) [](https://github.com/Azir-11) [](https://github.com/sigma-plus)
|
||||
|
||||
## [v1.1.3](https://github.com/soybeanjs/soybean-admin/compare/v1.1.2...v1.1.3) (2024-06-02)
|
||||
|
||||
### 🐞 错误修复
|
||||
|
||||
- **组件**:
|
||||
- 修复了由于回车导致搜索框反复弹出的问题 - 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/468 中修复 [<samp>(5bd96)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5bd96b8)
|
||||
- **项目**:
|
||||
- 修复点击菜单搜索。修复了 #466,关闭 #467 - 由 @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 - 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/464 中修复 [<samp>(59faf)</samp>](https://github.com/soybeanjs/soybean-admin/commit/59faf15)
|
||||
- **样式**:
|
||||
- 修复 FirstLevelMenu 样式。修复了 #450 - 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/450 中修复 [<samp>(db64b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/db64b0e)
|
||||
- 修复 PinToggler 样式。修复了 #451 - 由 @soybeanjs 在 https://github.com/soybeanjs/soybean-admin/issues/451 中修复 [<samp>(42b12)</samp>](https://github.com/soybeanjs/soybean-admin/commit/42b121a)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **组件**: 提高 TableColumnSetting 的拖动区域精度,带有动画 - 由 @orangelckc 在 https://github.com/soybeanjs/soybean-admin/issues/465 中优化 [<samp>(2aa85)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2aa85c6)
|
||||
- **项目**: unocss 边框快捷方式 - 由 @soybeanjs 优化 [<samp>(40d0f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/40d0f8a)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 更新 CHANGELOG - 由 @soybeanjs 更新 [<samp>(87b18)</samp>](https://github.com/soybeanjs/soybean-admin/commit/87b1838)
|
||||
|
||||
### 🏡 杂项
|
||||
|
||||
- **其他**:
|
||||
- 纠正拼写错误 - 由 @orangelckc 在 https://github.com/soybeanjs/soybean-admin/issues/460 中纠正 [<samp>(086ba)</samp>](https://github.com/soybeanjs/soybean-admin/commit/086bad4)
|
||||
- 纠正拼写错误 - 由 @Azir-11 在 https://github.com/soybeanjs/soybean-admin/issues/462 中纠正 [<samp>(f1850)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f185041)
|
||||
- **项目**:
|
||||
- 更新 vscode launch.json - 由 @soybeanjs 更新 [<samp>(4c1c7)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4c1c7e6)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/soybeanjs) [](https://github.com/Azir-11) [](https://github.com/orangelckc)
|
||||
|
||||
## [v1.1.2](https://github.com/soybeanjs/soybean-admin/compare/v1.1.1...v1.1.2) (2024-05-24)
|
||||
|
||||
### 🐞 错误修复
|
||||
|
||||
- **项目**:
|
||||
- 修复头部样式 & 修复点击全局标签时按钮高亮。修复了 #446 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/446 [<samp>(64fc0)</samp>](https://github.com/soybeanjs/soybean-admin/commit/64fc099)
|
||||
- 修复多标签页只渲染一次。修复了 #441 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/441 [<samp>(e379d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e379d6c)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **项目**: 优化代码 - 由 @honghuangdc [<samp>(bc8dc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/bc8dc47)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc)
|
||||
|
||||
## [v1.1.1](https://github.com/soybeanjs/soybean-admin/compare/v1.1.0...v1.1.1) (2024-05-20)
|
||||
|
||||
### 🚀 功能
|
||||
|
||||
- **hooks**: 为 useEcharts 添加 setOptions - 由 @honghuangdc 提交 [<samp>(e4d53)</samp>](https://github.com/soybeanjs/soybean-admin/commit/e4d53aa)
|
||||
|
||||
### 🐞 修复错误
|
||||
|
||||
- **projects**:
|
||||
- 修复 useRouter。修复了 #436 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/436 提交 [<samp>(0774a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/0774a51)
|
||||
- 在动态路由模式下获取路由时添加错误处理。修复了 440 - 由 @honghuangdc 提交 [<samp>(57b4a)</samp>](https://github.com/soybeanjs/soybean-admin/commit/57b4a9d)
|
||||
- **styles**:
|
||||
- 修复 useTable 类型 - 由 @honghuangdc 提交 [<samp>(07124)</samp>](https://github.com/soybeanjs/soybean-admin/commit/071241f)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **projects**:
|
||||
- 更新 CHANGELOG - 由 @honghuangdc 提交 [<samp>(19783)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1978397)
|
||||
- 更新 README.md - 由 @honghuangdc 提交 [<samp>(fa56e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/fa56e9c)
|
||||
- 更新 README.md - 由 @honghuangdc 提交 [<samp>(419ea)</samp>](https://github.com/soybeanjs/soybean-admin/commit/419ea42)
|
||||
|
||||
### 🏡 杂项
|
||||
|
||||
- **projects**:
|
||||
- 更新依赖并修复 TS 错误 - 由 @honghuangdc 提交 [<samp>(4ea9c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/4ea9c85)
|
||||
- 更新 eslint-config 并修复代码 - 由 @honghuangdc 提交 [<samp>(68ea9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/68ea974)
|
||||
- 更新 @elegant-router/vue 并为 resolve route 添加错误处理。修复了 #442 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/442 提交 [<samp>(24ff8)</samp>](https://github.com/soybeanjs/soybean-admin/commit/24ff852)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc)
|
||||
|
||||
## [v1.1.0](https://github.com/honghuangdc/soybean-admin/compare/v1.0.9...v1.1.0) (2024-05-07)
|
||||
|
||||
### 🚀 功能
|
||||
|
||||
- **项目**:
|
||||
- 支持灰度。修复了 #385 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/385 [<samp>(d335d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d335df6)
|
||||
- 添加前缀到本地存储 - 由 **Azir** [<samp>(1fc34)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1fc34cc)
|
||||
- 添加表格显示总数选项 - 由 **paynezhuang** [<samp>(3e61e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3e61eab)
|
||||
- 添加推荐颜色切换。关闭了 #388 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/388 [<samp>(a1920)</samp>](https://github.com/honghuangdc/soybean-admin/commit/a1920fc)
|
||||
- 添加菜单路由字段 - 由 **paynezhuang** [<samp>(dbe31)</samp>](https://github.com/honghuangdc/soybean-admin/commit/dbe31eb)
|
||||
- 支持短时间内重复请求错误只出现一次。关闭了 #368, 关闭了 #369 - 由 @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)
|
||||
- 通过鼠标滚轮按钮点击关闭标签 - 由 **JianJroh** [<samp>(d3849)</samp>](https://github.com/honghuangdc/soybean-admin/commit/d3849ba)
|
||||
- 页面:支持管理菜单更多选项。关闭了 #366 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/366 [<samp>(c4b5c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/c4b5c65)
|
||||
- useTable 添加展开显示 - 由 **paynezhuang** [<samp>(0a90d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/0a90dd3)
|
||||
|
||||
### 🐞 错误修复
|
||||
|
||||
- **项目**:
|
||||
- 菜单 fixedIndexInTab 默认为 null - 由 **paynezhuang** [<samp>(3d10e)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3d10ef1)
|
||||
- 修复菜单切换器 zIndex - 由 @honghuangdc [<samp>(7bd43)</samp>](https://github.com/honghuangdc/soybean-admin/commit/7bd43df)
|
||||
- 修复管理菜单模态样式 - 由 @honghuangdc [<samp>(60f3b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/60f3b14)
|
||||
- 当角色改变时修复菜单数据。修复了 #391 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/391 [<samp>(3b47b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3b47b5a)
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **项目**: 移除废弃的代码 - 由 @honghuangdc [<samp>(72ccb)</samp>](https://github.com/honghuangdc/soybean-admin/commit/72ccb6b)
|
||||
|
||||
### 💅 重构
|
||||
|
||||
- **项目**:
|
||||
- 重构 @sa/color-palette => @sa/color & 优化 @sa/utils 代码 - 由 @honghuangdc [<samp>(34999)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3499997)
|
||||
- menu-operate-drawer => menu-operate-modal - 由 @honghuangdc [<samp>(003e1)</samp>](https://github.com/honghuangdc/soybean-admin/commit/003e145)
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**:
|
||||
- 添加 CHANGELOG.zh_CN.md - 由 @honghuangdc [<samp>(18b3f)</samp>](https://github.com/honghuangdc/soybean-admin/commit/18b3f05)
|
||||
- 更新 CHANGELOG - 由 @honghuangdc [<samp>(4d17c)</samp>](https://github.com/honghuangdc/soybean-admin/commit/4d17cfd)
|
||||
|
||||
### 🏡 杂项
|
||||
|
||||
- **依赖**:
|
||||
- 更新依赖 - 由 @honghuangdc [<samp>(1cb38)</samp>](https://github.com/honghuangdc/soybean-admin/commit/1cb3816)
|
||||
- 更新依赖 - 由 @honghuangdc [<samp>(599b4)</samp>](https://github.com/honghuangdc/soybean-admin/commit/599b4e1)
|
||||
- **项目**:
|
||||
- 合并 main 到 v1.1.0 - 由 @honghuangdc [<samp>(ebe55)</samp>](https://github.com/honghuangdc/soybean-admin/commit/ebe55af)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc)
|
||||
[paynezhuang](mailto:paynezhuang@gmail.com), [JianJroh](mailto:rhjian@foxmail.com), [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)
|
||||
|
||||
### 🚀 特性
|
||||
|
||||
- **项目**: useTable添加展开以显示 - 由 **paynezhuang** [<samp>(0a90d)</samp>](https://github.com/honghuangdc/soybean-admin/commit/0a90dd3) 提供
|
||||
|
||||
### 🐞 修复的错误
|
||||
|
||||
- **项目**:
|
||||
- 修复 manage_menu 模态样式 - 由 @honghuangdc [<samp>(60f3b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/60f3b14) 提供
|
||||
- 当角色改变时修复菜单数据。修复了 #391 - 由 @honghuangdc 在 https://github.com/honghuangdc/soybean-admin/issues/391 [<samp>(3b47b)</samp>](https://github.com/honghuangdc/soybean-admin/commit/3b47b5a) 提供
|
||||
|
||||
### 🛠 优化
|
||||
|
||||
- **项目**: 删除废弃的代码 - 由 @honghuangdc [<samp>(72ccb)</samp>](https://github.com/honghuangdc/soybean-admin/commit/72ccb6b) 提供
|
||||
|
||||
### 📖 文档
|
||||
|
||||
- **项目**: 添加 CHANGELOG.zh_CN.md - 由 @honghuangdc [<samp>(18b3f)</samp>](https://github.com/honghuangdc/soybean-admin/commit/18b3f05) 提供
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc)
|
||||
[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)
|
||||
|
||||
### 🚀 功能
|
||||
|
||||
- **项目**:
|
||||
- 支持灰度。修复了 #385 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/385 [<samp>(d335d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d335df6)
|
||||
- 添加前缀到本地存储 - 由 **Azir** [<samp>(1fc34)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1fc34cc)
|
||||
- 添加表格 showTotal 选项 - 由 **paynezhuang** [<samp>(3e61e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3e61eab)
|
||||
- 添加推荐颜色切换。关闭了 #388 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/388 [<samp>(a1920)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a1920fc)
|
||||
- 添加菜单路由字段 - 由 **paynezhuang** [<samp>(dbe31)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dbe31eb)
|
||||
- 支持短时间内重复请求错误只发生一次。关闭了 #368, 关闭了 #369 - 由 @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)
|
||||
- 通过鼠标滚轮按钮点击关闭标签 - 由 **JianJroh** [<samp>(d3849)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d3849ba)
|
||||
- 页面:支持更多的 manage_menu 选项。关闭了 #366 - 由 @honghuangdc 在 https://github.com/soybeanjs/soybean-admin/issues/366 [<samp>(c4b5c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c4b5c65)
|
||||
|
||||
### 🐞 错误修复
|
||||
|
||||
- **项目**:
|
||||
- 修复菜单 fixedIndexInTab 默认为 null - 由 **paynezhuang** [<samp>(3d10e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3d10ef1)完成
|
||||
- 修复菜单切换器 zIndex - 由 @honghuangdc [<samp>(7bd43)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7bd43df)完成
|
||||
|
||||
### 💅 重构
|
||||
|
||||
- **项目**:
|
||||
- 重构 @sa/color-palette => @sa/color & 性能优化 @sa/utils - 由 @honghuangdc [<samp>(34999)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3499997)完成
|
||||
- menu-operate-drawer => menu-operate-modal - 由 @honghuangdc [<samp>(003e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/003e145)完成
|
||||
|
||||
### 🏡 杂务
|
||||
|
||||
- **依赖**:
|
||||
- 更新依赖 - 由 @honghuangdc [<samp>(1cb38)</samp>](https://github.com/soybeanjs/soybean-admin/commit/1cb3816)完成
|
||||
- 更新依赖 - 由 @honghuangdc [<samp>(599b4)</samp>](https://github.com/soybeanjs/soybean-admin/commit/599b4e1)完成
|
||||
- **项目**:
|
||||
- 合并主分支到 v1.1.0 - 由 @honghuangdc [<samp>(ebe55)</samp>](https://github.com/soybeanjs/soybean-admin/commit/ebe55af)完成
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/honghuangdc)
|
||||
[JianJroh](mailto:rhjian@foxmail.com), [paynezhuang](mailto:paynezhuang@gmail.com), [Azir](mailto:2075125282@qq.com)
|
204
README.en_US.md
Normal file
204
README.en_US.md
Normal file
@ -0,0 +1,204 @@
|
||||
<div align="center">
|
||||
<img src="./public/favicon.svg" width="160" />
|
||||
<h1>SoybeanAdmin</h1>
|
||||
<span><a href="./README.md">中文</a> | English</span>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
[](./LICENSE)
|
||||
[](https://github.com/soybeanjs/soybean-admin)
|
||||
[](https://github.com/soybeanjs/soybean-admin)
|
||||
[](https://gitee.com/honghuangdc/soybean-admin)
|
||||
[](https://gitcode.com/soybeanjs/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="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
|
||||
|
||||
> [!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!
|
||||
|
||||
> [!NOTE]
|
||||
> The `SoybeanAdmin` quick start series videos have been uploaded to [Bilibili](https://www.bilibili.com/video/BV1YKdRYXELC) Go online [click here](https://www.bilibili.com/video/BV1YKdRYXELC) Go check it out
|
||||
|
||||
> [!WARNING]
|
||||
> `SoybeanAdmin` is planning to develop a `V2` version, see [plan list](https://github.com/soybeanjs/soybean-admin/issues/767)
|
||||
|
||||
## 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, Vite7, 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.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **Cutting-edge technology application**: using the latest popular technology stack such as Vue3, Vite7, TypeScript, Pinia and UnoCSS.
|
||||
- **Clear project architecture**: using pnpm monorepo architecture, clear structure, elegant and easy to understand.
|
||||
- **Strict code specifications**: follow the [SoybeanJS specification](https://docs.soybeanjs.cn/standard), integrate eslint, prettier and simple-git-hooks to ensure the code is standardized.
|
||||
- **TypeScript**: support strict type checking to improve code maintainability.
|
||||
- **Rich theme configuration**: built-in a variety of theme configurations, perfectly integrated with UnoCSS.
|
||||
- **Built-in internationalization solution**: easily realize multi-language support.
|
||||
- **Automated file routing system**: automatically generate route import, declaration and type. For more details, please refer to [Elegant Router](https://github.com/soybeanjs/elegant-router).
|
||||
- **Flexible permission routing**: support both front-end static routing and back-end dynamic routing.
|
||||
- **Rich page components**: built-in a variety of pages and components, including 403, 404, 500 pages, as well as layout components, tag components, theme configuration components, etc.
|
||||
- **Command line tool**: built-in efficient command line tool, git commit, delete file, release, etc.
|
||||
- **Mobile adaptation**: perfectly support mobile terminal to realize adaptive layout.
|
||||
|
||||
|
||||
## Version
|
||||
|
||||
- **NaiveUI Version:**
|
||||
- [Preview Link](https://naive.soybeanjs.cn/)
|
||||
- [Github Repository](https://github.com/soybeanjs/soybean-admin)
|
||||
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin)
|
||||
- [Gitcode Repository](https://gitcode.com/soybeanjs/soybean-admin)
|
||||
|
||||
- **AntDesignVue Version:**
|
||||
- [Preview Link](https://antd.soybeanjs.cn/)
|
||||
- [Github Repository](https://github.com/soybeanjs/soybean-admin-antd)
|
||||
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin-antd)
|
||||
- [Gitcode Repository](https://gitcode.com/soybeanjs/soybean-admin-antd)
|
||||
|
||||
- **ElementPlus Version:**
|
||||
- [Preview Link](https://elp.soybeanjs.cn/)
|
||||
- [Github Repository](https://github.com/soybeanjs/soybean-admin-element-plus)
|
||||
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin-element-plus)
|
||||
- [Gitcode Repository](https://gitcode.com/soybeanjs/soybean-admin-element-plus)
|
||||
|
||||
- **Legacy Version:**
|
||||
- [Preview Link](https://legacy.soybeanjs.cn/)
|
||||
- [Github Repository](https://github.com/soybeanjs/soybean-admin/tree/legacy)
|
||||
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin/tree/legacy)
|
||||
- [Gitcode Repository](https://gitcode.com/soybeanjs/soybean-admin/tree/legacy)
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Link](https://docs.soybeanjs.cn)
|
||||
- [Legacy Docs](https://legacy-docs.soybeanjs.cn)
|
||||
|
||||
## Example Images
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
**Environment Preparation**
|
||||
|
||||
Make sure your environment meets the following requirements:
|
||||
|
||||
- **git**: you need git to clone and manage project versions.
|
||||
- **NodeJS**: >=20.19.0, recommended 20.19.0 or higher.
|
||||
- **pnpm**: >= 10.5.0, recommended 10.5.0 or higher.
|
||||
|
||||
**Clone Project**
|
||||
|
||||
```bash
|
||||
# github
|
||||
git clone https://github.com/soybeanjs/soybean-admin.git
|
||||
# gitee
|
||||
git clone https://gitee.com/honghuangdc/soybean-admin.git
|
||||
# gitcode
|
||||
git clone https://gitcode.com/soybeanjs/soybean-admin.git
|
||||
```
|
||||
|
||||
**Install Dependencies**
|
||||
|
||||
```bash
|
||||
pnpm i
|
||||
```
|
||||
> Since this project uses the pnpm monorepo management method, please do not use npm or yarn to install dependencies.
|
||||
|
||||
**Start Project**
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
**Build Project**
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
**Code Synchronization**
|
||||
|
||||
Refer to the [Code Synchronization](https://docs.soybeanjs.cn/guide/sync) document.
|
||||
|
||||
## Ecosystem
|
||||
|
||||
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): SoybeanAdmin based version of React.
|
||||
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): A Mock Api management system that helps front-end developers quickly implement interface mocks.
|
||||
- [T-Shell](https://github.com/TheBlindM/T-Shell): A terminal emulator and SSH client with configurable command prompts.
|
||||
- [pea](https://github.com/haitang1894/pea) : Adopting SpringBoot3.2 + JDK21, MyBatis-Plus, SpringSecurity security framework, etc., suitable for the simple permission system developed by [soybean-admin](https://gitee.com/honghuangdc/soybean-admin).
|
||||
- [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.
|
||||
- [ba](https://github.com/xiatianYa/Ba-Server): Backend service docking with soybean admin based on goFrame framework, adapted to dynamic routing, and interface authentication permissions.
|
||||
- [soybean-admin-go](https://github.com/WgoW/soybean-admin-go):A Go backend service developed based on the Gin and GORM frameworks, integrated with the example branch of Soybean Admin. It supports dynamic routing and API permission authentication.
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
## Browser Support
|
||||
|
||||
It is recommended to use the latest version of Chrome in development for a better experience.
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## OpenSource Author
|
||||
|
||||
[Soybean](https://github.com/honghuangdc)
|
||||
|
||||
## Contributors
|
||||
|
||||
Thanks the following people for their contributions. If you want to contribute to this project, please refer to [How to Contribute](#how-to-contribute).
|
||||
|
||||
<a href="https://github.com/soybeanjs/soybean-admin/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" />
|
||||
</a>
|
||||
|
||||
## Communication
|
||||
|
||||
`SoybeanAdmin` is a completely open source and free project, helping developers to develop medium and large-scale management systems more conveniently. It also provides WeChat and QQ communication groups. If you have any questions, please feel free to ask in the group.
|
||||
|
||||
<div>
|
||||
<p>QQ Group</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-4.jpg" style="width:200px" />
|
||||
</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" />
|
||||
</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>
|
||||
|
||||
## Star Trend
|
||||
|
||||
[](https://star-history.com/#soybeanjs/soybean-admin&Date)
|
||||
|
||||
## License
|
||||
|
||||
This project is based on the [MIT © 2021 Soybean](./LICENSE) protocol, for learning purposes only, please retain the author's copyright information for commercial use, the author does not guarantee and is not responsible for the software.
|
220
README.md
220
README.md
@ -1,62 +1,107 @@
|
||||
<div align="center">
|
||||
<img src="./public/favicon.svg" width="160" />
|
||||
<h1>Soybean Admin</h1>
|
||||
<span>English | <a href="./README.zh_CN.md">中文</a></span>
|
||||
<h1>SoybeanAdmin</h1>
|
||||
<span>中文 | <a href="./README.en_US.md">English</a></span>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
[](./LICENSE)  
|
||||
---
|
||||
|
||||
[](./LICENSE)
|
||||
[](https://github.com/soybeanjs/soybean-admin)
|
||||
[](https://github.com/soybeanjs/soybean-admin)
|
||||
[](https://gitee.com/honghuangdc/soybean-admin)
|
||||
[](https://gitcode.com/soybeanjs/soybean-admin)
|
||||
|
||||
>[!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`.
|
||||
<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="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
|
||||
> [!NOTE]
|
||||
> 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!
|
||||
> 如果您觉得 `SoybeanAdmin`对您有所帮助,或者您喜欢我们的项目,请在 GitHub 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持!
|
||||
|
||||
## Introduction
|
||||
> [!NOTE]
|
||||
> `SoybeanAdmin` 快速上手系列视频已在 [Bilibili](https://www.bilibili.com/video/BV1YKdRYXELC) 上线 [点击这里](https://www.bilibili.com/video/BV1YKdRYXELC) 前往查看
|
||||
|
||||
[`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.
|
||||
> [!WARNING]
|
||||
> `SoybeanAdmin` 正在计划开发 `V2` 版本,详情见[计划清单](https://github.com/soybeanjs/soybean-admin/issues/767)
|
||||
|
||||
## 简介
|
||||
|
||||
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite7, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件,代码规范严谨,实现了自动化的文件路由系统。此外,它还采用了基于 ApiFox 的在线Mock数据方案。`SoybeanAdmin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
|
||||
|
||||
## 特性
|
||||
|
||||
- **前沿技术应用**:采用 Vue3, Vite7, TypeScript, Pinia 和 UnoCSS 等最新流行的技术栈。
|
||||
- **清晰的项目架构**:采用 pnpm monorepo 架构,结构清晰,优雅易懂。
|
||||
- **严格的代码规范**:遵循 [SoybeanJS 规范](https://docs.soybeanjs.cn/zh/standard),集成了eslint, prettier 和 simple-git-hooks,保证代码的规范性。
|
||||
- **TypeScript**: 支持严格的类型检查,提高代码的可维护性。
|
||||
- **丰富的主题配置**:内置多样的主题配置,与 UnoCSS 完美结合。
|
||||
- **内置国际化方案**:轻松实现多语言支持。
|
||||
- **自动化文件路由系统**:自动生成路由导入、声明和类型。更多细节请查看 [Elegant Router](https://github.com/soybeanjs/elegant-router)。
|
||||
- **灵活的权限路由**:同时支持前端静态路由和后端动态路由。
|
||||
- **丰富的页面组件**:内置多样页面和组件,包括403、404、500页面,以及布局组件、标签组件、主题配置组件等。
|
||||
- **命令行工具**:内置高效的命令行工具,git提交、删除文件、发布等。
|
||||
- **移动端适配**:完美支持移动端,实现自适应布局。
|
||||
|
||||
|
||||
## Features
|
||||
## 版本
|
||||
|
||||
- **Cutting-edge technology application**: using the latest popular technology stack such as Vue3, Vite5, TypeScript, Pinia and UnoCSS.
|
||||
- **Clear project architecture**: using pnpm monorepo architecture, clear structure, elegant and easy to understand.
|
||||
- **Strict code specifications**: follow the [SoybeanJS specification](https://docs.soybeanjs.cn/standard), integrate eslint, prettier and simple-git-hooks to ensure the code is standardized.
|
||||
- **TypeScript**: support strict type checking to improve code maintainability.
|
||||
- **Rich theme configuration**: built-in a variety of theme configurations, perfectly integrated with UnoCSS.
|
||||
- **Built-in internationalization solution**: easily realize multi-language support.
|
||||
- **Automated file routing system**: automatically generate route import, declaration and type. For more details, please refer to [Elegant Router](https://github.com/soybeanjs/elegant-router).
|
||||
- **Flexible permission routing**: support both front-end static routing and back-end dynamic routing.
|
||||
- **Rich page components**: built-in a variety of pages and components, including 403, 404, 500 pages, as well as layout components, tag components, theme configuration components, etc.
|
||||
- **Command line tool**: built-in efficient command line tool, git commit, delete file, release, etc.
|
||||
- **Mobile adaptation**: perfectly support mobile terminal to realize adaptive layout.
|
||||
- **NaiveUI 版本:**
|
||||
- [预览地址](https://naive.soybeanjs.cn/)
|
||||
- [Github 仓库](https://github.com/soybeanjs/soybean-admin)
|
||||
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin)
|
||||
- [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin)
|
||||
- **AntDesignVue 版本:**
|
||||
- [预览地址](https://antd.soybeanjs.cn/)
|
||||
- [Github 仓库](https://github.com/soybeanjs/soybean-admin-antd)
|
||||
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin-antd)
|
||||
- [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin-antd)
|
||||
- **ElementPlus 版本:**
|
||||
- [预览地址](https://elp.soybeanjs.cn/)
|
||||
- [Github 仓库](https://github.com/soybeanjs/soybean-admin-element-plus)
|
||||
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin-element-plus)
|
||||
- [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin-element-plus)
|
||||
- **旧版:**
|
||||
- [预览地址](https://legacy.soybeanjs.cn/)
|
||||
- [Github 仓库](https://github.com/soybeanjs/soybean-admin/tree/legacy)
|
||||
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin/tree/legacy)
|
||||
- [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin/tree/legacy)
|
||||
|
||||
|
||||
## Version
|
||||
## 文档
|
||||
|
||||
- **NaiveUI Version:**
|
||||
- [Preview Link](https://naive.soybeanjs.cn/)
|
||||
- [Github Repository](https://github.com/soybeanjs/soybean-admin)
|
||||
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin)
|
||||
|
||||
- **AntDesignVue Version:**
|
||||
- [Preview Link](https://antd.soybeanjs.cn/)
|
||||
- [Github Repository](https://github.com/soybeanjs/soybean-admin-antd)
|
||||
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin-antd)
|
||||
|
||||
- **Legacy Version:**
|
||||
- [Preview Link](https://legacy.soybeanjs.cn/)
|
||||
- [Github Repository](https://github.com/soybeanjs/soybean-admin/tree/legacy)
|
||||
- [地址](https://docs.soybeanjs.cn)
|
||||
- [旧版文档](https://legacy-docs.soybeanjs.cn)
|
||||
|
||||
|
||||
## Documentation
|
||||
## 合作事项
|
||||
|
||||
- [Link](https://docs.soybeanjs.cn)
|
||||
- [Legacy Docs](https://legacy-docs.soybeanjs.cn)
|
||||
我们非常感谢大家对 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 的支持!为了进一步回馈社区,并助力企业和开发者实现个性化需求,我们现提供多种合作服务,期待与您携手共赢。
|
||||
|
||||
## Example Images
|
||||
##### 1、定制化管理后台开发
|
||||
|
||||
针对企业和开发者的特定业务需求,我们提供基于 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 的定制化管理后台开发服务。我们的团队具备丰富的行业经验,能够迅速理解并实现您的需求,打造高效、灵活且安全的定制化解决方案。
|
||||
|
||||
- **定制开发**:我们将根据您的具体需求,提供从需求分析、UI设计到功能实现的全方位服务,确保项目高效交付。
|
||||
- **功能扩展**:在 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 基础上,扩展您所需的特定功能模块,提升管理后台的功能和用户体验。
|
||||
|
||||
##### 2、企业外包服务
|
||||
|
||||
我们承接各类企业级外包项目,特别是在管理后台系统的开发、集成与运维方面。我们以精益求精的态度,确保项目的质量和进度,为您的业务提供强有力的技术支持。
|
||||
|
||||
- **项目开发**:无论是全新的项目,还是现有系统的优化与集成,我们都将为您量身打造高效可靠的解决方案。
|
||||
- **系统集成与维护**:我们也提供基于 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 的系统集成与长期维护服务,确保您的系统稳定、安全地运行。
|
||||
|
||||
##### 3、联系方式
|
||||
|
||||
如有合作意向或项目咨询,请通过以下方式与我们联系:
|
||||
|
||||
- **Email**: [soybeanjs@outlook.com](mailto:soybeanjs@outlook.com)
|
||||
- **GitHub Issues**: 欢迎通过 [GitHub Issues](https://github.com/soybeanjs/soybean-admin/issues/new) 联系我们,进行初步的合作洽谈。
|
||||
- **商务合作微信**: honghuangdc
|
||||
|
||||
期待与您开展深入合作,共同推动 SoybeanAdmin 项目及其在更多领域的成功应用!
|
||||
|
||||
|
||||
## 示例图片
|
||||
|
||||

|
||||

|
||||
@ -71,95 +116,116 @@
|
||||

|
||||
|
||||
|
||||
## Usage
|
||||
## 使用
|
||||
|
||||
**Environment Preparation**
|
||||
**环境准备**
|
||||
|
||||
Make sure your environment meets the following requirements:
|
||||
确保你的环境满足以下要求:
|
||||
|
||||
- **git**: you need git to clone and manage project versions.
|
||||
- **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.
|
||||
- **git**: 你需要git来克隆和管理项目版本。
|
||||
- **NodeJS**: >=20.19.0,推荐 20.19.0 或更高。
|
||||
- **pnpm**: >= 10.5.0,推荐 10.5.0 或更高。
|
||||
|
||||
**Clone Project**
|
||||
**克隆项目**
|
||||
|
||||
```bash
|
||||
# github
|
||||
git clone https://github.com/soybeanjs/soybean-admin.git
|
||||
# gitee
|
||||
git clone https://gitee.com/honghuangdc/soybean-admin.git
|
||||
# gitcode
|
||||
git clone https://gitcode.com/soybeanjs/soybean-admin.git
|
||||
```
|
||||
|
||||
**Install Dependencies**
|
||||
**安装依赖**
|
||||
|
||||
```bash
|
||||
pnpm i
|
||||
```
|
||||
> Since this project uses the pnpm monorepo management method, please do not use npm or yarn to install dependencies.
|
||||
> 由于本项目采用了 pnpm monorepo 的管理方式,因此请不要使用 npm 或 yarn 来安装依赖。
|
||||
|
||||
**Start Project**
|
||||
**启动项目**
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
**Build Project**
|
||||
**构建项目**
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## 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).
|
||||
参考 [代码同步](https://docs.soybeanjs.cn/zh/guide/sync) 文档。
|
||||
|
||||
## Git Commit Guidelines
|
||||
## 周边生态
|
||||
|
||||
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.
|
||||
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): 基于SoybeanAdmin的React版本.
|
||||
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
|
||||
- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。
|
||||
- [pea](https://github.com/haitang1894/pea) : 采用SpringBoot3.2 + JDK21、MyBatis-Plus、SpringSecurity安全框架等,适配 [soybean-admin](https://gitee.com/honghuangdc/soybean-admin) 开发的简单权限系统。
|
||||
- [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 的现代化轻量管理平台.
|
||||
- [ba](https://github.com/xiatianYa/Ba-Server): 基于goFrame框架开发的后端服务对接soybean-admin,适配动态路由,接口鉴权限。
|
||||
- [soybean-admin-go](https://github.com/WgoW/soybean-admin-go):基于gin+gorm框架开发的go语言后端服务对接soybean-admin的example分支,适配动态路由,接口鉴权限。
|
||||
|
||||
## Browser Support
|
||||
|
||||
It is recommended to use the latest version of Chrome in development for a better experience.
|
||||
## 如何贡献
|
||||
|
||||
我们热烈欢迎并感谢所有形式的贡献。如果您有任何想法或建议,欢迎通过提交 [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` 命令来创建提交信息,以确保信息的规范性。
|
||||
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
推荐使用最新版的 Chrome 浏览器进行开发,以获得更好的体验。
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## OpenSource Author
|
||||
## 开源作者
|
||||
|
||||
[Soybean](https://github.com/honghuangdc)
|
||||
|
||||
## Contributors
|
||||
|
||||
Thanks the following people for their contributions. If you want to contribute to this project, please refer to [How to Contribute](#how-to-contribute).
|
||||
## 贡献者
|
||||
|
||||
感谢以下贡献者的贡献。如果您想为本项目做出贡献,请参考 [如何贡献](#如何贡献)。
|
||||
|
||||
<a href="https://github.com/soybeanjs/soybean-admin/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" />
|
||||
</a>
|
||||
|
||||
## Communication
|
||||
## 交流
|
||||
|
||||
`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.
|
||||
`SoybeanAdmin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。
|
||||
|
||||
<div>
|
||||
<p>QQ Group</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-2.jpg" style="width:200px" />
|
||||
</div>
|
||||
<div>
|
||||
<p>WeChat Group</p>
|
||||
<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" />
|
||||
<p>QQ交流群</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-4.jpg" style="width:200px" />
|
||||
</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" /> -->
|
||||
<p>微信群</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/picgo/soybean-admin-wechat-0620.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" />
|
||||
</div>
|
||||
|
||||
## Star Trend
|
||||
## Star 趋势
|
||||
|
||||
[](https://star-history.com/#soybeanjs/soybean-admin&Date)
|
||||
|
||||
## License
|
||||
## 开源协议
|
||||
|
||||
This project is based on the [MIT © 2021 Soybean](./LICENSE) protocol, for learning purposes only, please retain the author's copyright information for commercial use, the author does not guarantee and is not responsible for the software.
|
||||
项目基于 [MIT © 2021 Soybean](./LICENSE) 协议,仅供学习参考,商业使用请保留作者版权信息,作者不保证也不承担任何软件的使用风险。
|
||||
|
169
README.zh_CN.md
169
README.zh_CN.md
@ -1,169 +0,0 @@
|
||||
<div align="center">
|
||||
<img src="./public/favicon.svg" width="160" />
|
||||
<h1>Soybean Admin</h1>
|
||||
<span><a href="./README.zh_CN.md">English</a> | 中文</span>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
[](./LICENSE)  
|
||||
|
||||
>[!CAUTION]
|
||||
> 旧版本的 `Soybean Admin` 已经移动到分支 [legacy](https://github.com/soybeanjs/soybean-admin/tree/legacy)。建议使用最新版本的 `Soybean Admin`。
|
||||
|
||||
> [!NOTE]
|
||||
> 如果您觉得 `Soybean Admin`对您有所帮助,或者您喜欢我们的项目,请在 GitHub 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持!
|
||||
|
||||
<br />
|
||||
|
||||
[](https://star-history.com/#soybeanjs/soybean-admin&Date)
|
||||
|
||||
|
||||
## 简介
|
||||
|
||||
[`Soybean Admin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件,代码规范严谨,实现了自动化的文件路由系统。此外,它还采用了基于 ApiFox 的在线Mock数据方案。`Soybean Admin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
|
||||
|
||||
## 特性
|
||||
|
||||
- **前沿技术应用**:采用 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS 等最新流行的技术栈。
|
||||
- **清晰的项目架构**:采用 pnpm monorepo 架构,结构清晰,优雅易懂。
|
||||
- **严格的代码规范**:遵循 [SoybeanJS 规范](https://docs.soybeanjs.cn/zh/standard),集成了eslint, prettier 和 simple-git-hooks,保证代码的规范性。
|
||||
- **TypeScript**: 支持严格的类型检查,提高代码的可维护性。
|
||||
- **丰富的主题配置**:内置多样的主题配置,与 UnoCSS 完美结合。
|
||||
- **内置国际化方案**:轻松实现多语言支持。
|
||||
- **自动化文件路由系统**:自动生成路由导入、声明和类型。更多细节请查看 [Elegant Router](https://github.com/soybeanjs/elegant-router)。
|
||||
- **灵活的权限路由**:同时支持前端静态路由和后端动态路由。
|
||||
- **丰富的页面组件**:内置多样页面和组件,包括403、404、500页面,以及布局组件、标签组件、主题配置组件等。
|
||||
- **命令行工具**:内置高效的命令行工具,git提交、删除文件、发布等。
|
||||
- **移动端适配**:完美支持移动端,实现自适应布局。
|
||||
|
||||
|
||||
## 版本
|
||||
|
||||
- **NaiveUI 版本:**
|
||||
- [预览地址](https://naive.soybeanjs.cn/)
|
||||
- [Github 仓库](https://github.com/soybeanjs/soybean-admin)
|
||||
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin)
|
||||
|
||||
- **AntDesignVue 版本:**
|
||||
- [预览地址](https://antd.soybeanjs.cn/)
|
||||
- [Github 仓库](https://github.com/soybeanjs/soybean-admin-antd)
|
||||
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin-antd)
|
||||
|
||||
- **旧版:**
|
||||
- [预览地址](https://legacy.soybeanjs.cn/)
|
||||
- [Github 仓库](https://github.com/soybeanjs/soybean-admin/tree/legacy)
|
||||
|
||||
|
||||
## 文档
|
||||
|
||||
- [地址](https://docs.soybeanjs.cn)
|
||||
- [旧版文档](https://legacy-docs.soybeanjs.cn)
|
||||
|
||||
## 示例图片
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
## 使用
|
||||
|
||||
**环境准备**
|
||||
|
||||
确保你的环境满足以下要求:
|
||||
|
||||
- **git**: 你需要git来克隆和管理项目版本。
|
||||
- **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 或更高。
|
||||
|
||||
**克隆项目**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/soybeanjs/soybean-admin.git
|
||||
```
|
||||
|
||||
**安装依赖**
|
||||
|
||||
```bash
|
||||
pnpm i
|
||||
```
|
||||
> 由于本项目采用了 pnpm monorepo 的管理方式,因此请不要使用 npm 或 yarn 来安装依赖。
|
||||
|
||||
**启动项目**
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
**构建项目**
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## 如何贡献
|
||||
|
||||
我们热烈欢迎并感谢所有形式的贡献。如果您有任何想法或建议,欢迎通过提交 [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) 规范的提交信息。在提交PR时,请务必使用 `commit` 命令来创建提交信息,以确保信息的规范性。
|
||||
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
推荐使用最新版的 Chrome 浏览器进行开发,以获得更好的体验。
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## 开源作者
|
||||
|
||||
[Soybean](https://github.com/honghuangdc)
|
||||
|
||||
## 贡献者
|
||||
|
||||
感谢以下贡献者的贡献。如果您想为本项目做出贡献,请参考 [如何贡献](#如何贡献)。
|
||||
|
||||
<a href="https://github.com/soybeanjs/soybean-admin/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" />
|
||||
</a>
|
||||
|
||||
## 交流
|
||||
|
||||
`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>
|
||||
<p>微信群</p>
|
||||
<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" />
|
||||
</div>
|
||||
<div>
|
||||
<p>添加 Soybean 的微信,业务咨询、合作、项目架构、一对一指导等</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybean.jpg" style="width:200px" />
|
||||
</div>
|
||||
|
||||
## Star 趋势
|
||||
|
||||
[](https://star-history.com/#soybeanjs/soybean-admin&Date)
|
||||
|
||||
## 开源协议
|
||||
|
||||
项目基于 [MIT © 2021 Soybean](./LICENSE) 协议,仅供学习参考,商业使用请保留作者版权信息,作者不保证也不承担任何软件的使用风险。
|
@ -1 +1,2 @@
|
||||
export * from './proxy';
|
||||
export * from './time';
|
||||
|
@ -1,34 +1,53 @@
|
||||
import type { ProxyOptions } from 'vite';
|
||||
import { bgRed, bgYellow, green, lightBlue } from 'kolorist';
|
||||
import { consola } from 'consola';
|
||||
import { createServiceConfig } from '../../src/utils/service';
|
||||
|
||||
/**
|
||||
* Set http proxy
|
||||
*
|
||||
* @param env - The current env
|
||||
* @param isDev - Is development environment
|
||||
* @param enable - If enable http proxy
|
||||
*/
|
||||
export function createViteProxy(env: Env.ImportMeta, isDev: boolean) {
|
||||
const isEnableHttpProxy = isDev && env.VITE_HTTP_PROXY === 'Y';
|
||||
export function createViteProxy(env: Env.ImportMeta, enable: boolean) {
|
||||
const isEnableHttpProxy = enable && env.VITE_HTTP_PROXY === 'Y';
|
||||
|
||||
if (!isEnableHttpProxy) return undefined;
|
||||
|
||||
const isEnableProxyLog = env.VITE_PROXY_LOG === 'Y';
|
||||
|
||||
const { baseURL, proxyPattern, other } = createServiceConfig(env);
|
||||
|
||||
const proxy: Record<string, ProxyOptions> = createProxyItem({ baseURL, proxyPattern });
|
||||
const proxy: Record<string, ProxyOptions> = createProxyItem({ baseURL, proxyPattern }, isEnableProxyLog);
|
||||
|
||||
other.forEach(item => {
|
||||
Object.assign(proxy, createProxyItem(item));
|
||||
Object.assign(proxy, createProxyItem(item, isEnableProxyLog));
|
||||
});
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function createProxyItem(item: App.Service.ServiceConfigItem) {
|
||||
function createProxyItem(item: App.Service.ServiceConfigItem, enableLog: boolean) {
|
||||
const proxy: Record<string, ProxyOptions> = {};
|
||||
|
||||
proxy[item.proxyPattern] = {
|
||||
target: item.baseURL,
|
||||
changeOrigin: true,
|
||||
configure: (_proxy, options) => {
|
||||
_proxy.on('proxyReq', (_proxyReq, req, _res) => {
|
||||
if (!enableLog) return;
|
||||
|
||||
const requestUrl = `${lightBlue('[proxy url]')}: ${bgYellow(` ${req.method} `)} ${green(`${item.proxyPattern}${req.url}`)}`;
|
||||
|
||||
const proxyUrl = `${lightBlue('[real request url]')}: ${green(`${options.target}${req.url}`)}`;
|
||||
|
||||
consola.log(`${requestUrl}\n${proxyUrl}`);
|
||||
});
|
||||
_proxy.on('error', (_err, req, _res) => {
|
||||
if (!enableLog) return;
|
||||
consola.log(bgRed(`Error: ${req.method} `), green(`${options.target}${req.url}`));
|
||||
});
|
||||
},
|
||||
rewrite: path => path.replace(new RegExp(`^${item.proxyPattern}`), '')
|
||||
};
|
||||
|
||||
|
12
build/config/time.ts
Normal file
12
build/config/time.ts
Normal file
@ -0,0 +1,12 @@
|
||||
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;
|
||||
}
|
9
build/plugins/devtools.ts
Normal file
9
build/plugins/devtools.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import VueDevtools from 'vite-plugin-vue-devtools';
|
||||
|
||||
export function setupDevtoolsPlugin(viteEnv: Env.ImportMeta) {
|
||||
const { VITE_DEVTOOLS_LAUNCH_EDITOR } = viteEnv;
|
||||
|
||||
return VueDevtools({
|
||||
launchEditor: VITE_DEVTOOLS_LAUNCH_EDITOR
|
||||
});
|
||||
}
|
13
build/plugins/html.ts
Normal file
13
build/plugins/html.ts
Normal file
@ -0,0 +1,13 @@
|
||||
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;
|
||||
}
|
@ -1,25 +1,23 @@
|
||||
import type { PluginOption } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import VueDevtools from 'vite-plugin-vue-devtools';
|
||||
import progress from 'vite-plugin-progress';
|
||||
import { setupElegantRouter } from './router';
|
||||
import { setupUnocss } from './unocss';
|
||||
import { setupUnplugin } from './unplugin';
|
||||
import { setupHtmlPlugin } from './html';
|
||||
import { setupDevtoolsPlugin } from './devtools';
|
||||
|
||||
export function setupVitePlugins(viteEnv: Env.ImportMeta) {
|
||||
export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) {
|
||||
const plugins: PluginOption = [
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true
|
||||
}
|
||||
}),
|
||||
vue(),
|
||||
vueJsx(),
|
||||
VueDevtools(),
|
||||
setupDevtoolsPlugin(viteEnv),
|
||||
setupElegantRouter(),
|
||||
setupUnocss(viteEnv),
|
||||
...setupUnplugin(viteEnv),
|
||||
progress()
|
||||
progress(),
|
||||
setupHtmlPlugin(buildTime)
|
||||
];
|
||||
|
||||
return plugins;
|
||||
|
@ -8,9 +8,6 @@ export function setupElegantRouter() {
|
||||
base: 'src/layouts/base-layout/index.vue',
|
||||
blank: 'src/layouts/blank-layout/index.vue'
|
||||
},
|
||||
customRoutes: {
|
||||
names: ['exception_403', 'exception_404', 'exception_500']
|
||||
},
|
||||
routePathTransformer(routeName, routePath) {
|
||||
const key = routeName as RouteKey;
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import process from 'node:process';
|
||||
import path from 'node:path';
|
||||
import type { PluginOption } from 'vite';
|
||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||
import Icons from 'unplugin-icons/vite';
|
||||
import IconsResolver from 'unplugin-icons/resolver';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import { AntDesignVueResolver, NaiveUiResolver } from 'unplugin-vue-components/resolvers';
|
||||
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
|
||||
import { FileSystemIconLoader } from 'unplugin-icons/loaders';
|
||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||
|
||||
export function setupUnplugin(viteEnv: Env.ImportMeta) {
|
||||
const { VITE_ICON_PREFIX, VITE_ICON_LOCAL_PREFIX } = viteEnv;
|
||||
@ -31,9 +31,6 @@ export function setupUnplugin(viteEnv: Env.ImportMeta) {
|
||||
dts: 'src/typings/components.d.ts',
|
||||
types: [{ from: 'vue-router', names: ['RouterLink', 'RouterView'] }],
|
||||
resolvers: [
|
||||
AntDesignVueResolver({
|
||||
importStyle: false
|
||||
}),
|
||||
NaiveUiResolver(),
|
||||
IconsResolver({ customCollections: [collectionName], componentPrefix: VITE_ICON_PREFIX })
|
||||
]
|
||||
|
@ -7,7 +7,7 @@ export default defineConfig(
|
||||
'vue/multi-word-component-names': [
|
||||
'warn',
|
||||
{
|
||||
ignores: ['index', 'App', '[id]']
|
||||
ignores: ['index', 'App', 'Register', '[id]', '[url]']
|
||||
}
|
||||
],
|
||||
'vue/component-name-in-template-casing': [
|
||||
@ -17,7 +17,8 @@ export default defineConfig(
|
||||
registeredComponentsOnly: false,
|
||||
ignores: ['/^icon-/']
|
||||
}
|
||||
]
|
||||
],
|
||||
'unocss/order-attributify': 'off'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -4,6 +4,7 @@
|
||||
<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>
|
||||
|
95
package.json
95
package.json
@ -1,9 +1,8 @@
|
||||
{
|
||||
"name": "soybean-admin",
|
||||
"type": "module",
|
||||
"version": "1.0.1",
|
||||
"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的清新优雅的中后台模版。",
|
||||
"version": "1.3.15",
|
||||
"description": "A fresh and elegant admin template, based on Vue3、Vite7、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite7、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
|
||||
"author": {
|
||||
"name": "Soybean",
|
||||
"email": "soybeanjs@outlook.com",
|
||||
@ -20,18 +19,23 @@
|
||||
"keywords": [
|
||||
"Vue3 admin ",
|
||||
"vue-admin-template",
|
||||
"Vite5",
|
||||
"Vite7",
|
||||
"TypeScript",
|
||||
"naive-ui",
|
||||
"naive-ui-admin",
|
||||
"ant-design-vue v4",
|
||||
"UnoCSS"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.19.0",
|
||||
"pnpm": ">=10.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vite build --mode prod",
|
||||
"build:test": "vite build --mode test",
|
||||
"cleanup": "sa cleanup",
|
||||
"commit": "sa git-commit",
|
||||
"commit:zh": "sa git-commit -l=zh-cn",
|
||||
"dev": "vite --mode test",
|
||||
"dev:prod": "vite --mode prod",
|
||||
"gen-route": "sa gen-route",
|
||||
@ -44,64 +48,63 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@better-scroll/core": "2.5.1",
|
||||
"@iconify/vue": "4.1.1",
|
||||
"@iconify/vue": "5.0.0",
|
||||
"@sa/axios": "workspace:*",
|
||||
"@sa/color-palette": "workspace:*",
|
||||
"@sa/color": "workspace:*",
|
||||
"@sa/hooks": "workspace:*",
|
||||
"@sa/materials": "workspace:*",
|
||||
"@sa/utils": "workspace:*",
|
||||
"@vueuse/core": "10.9.0",
|
||||
"@vueuse/core": "13.9.0",
|
||||
"clipboard": "2.0.11",
|
||||
"dayjs": "1.11.10",
|
||||
"echarts": "5.5.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"naive-ui": "2.38.1",
|
||||
"dayjs": "1.11.18",
|
||||
"defu": "6.1.4",
|
||||
"echarts": "6.0.0",
|
||||
"json5": "2.2.3",
|
||||
"naive-ui": "2.43.1",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "2.1.7",
|
||||
"vue": "3.4.21",
|
||||
"vue-draggable-plus": "0.4.0",
|
||||
"vue-i18n": "9.10.2",
|
||||
"vue-router": "4.3.0"
|
||||
"pinia": "3.0.3",
|
||||
"tailwind-merge": "3.3.1",
|
||||
"vue": "3.5.21",
|
||||
"vue-draggable-plus": "0.6.0",
|
||||
"vue-i18n": "11.1.12",
|
||||
"vue-router": "4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@elegant-router/vue": "0.3.6",
|
||||
"@iconify/json": "2.2.196",
|
||||
"@elegant-router/vue": "0.3.8",
|
||||
"@iconify/json": "2.2.385",
|
||||
"@sa/scripts": "workspace:*",
|
||||
"@sa/uno-preset": "workspace:*",
|
||||
"@soybeanjs/eslint-config": "1.2.5",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "20.12.2",
|
||||
"@soybeanjs/eslint-config": "1.7.1",
|
||||
"@types/node": "24.5.1",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@unocss/eslint-config": "0.58.9",
|
||||
"@unocss/preset-icons": "0.58.9",
|
||||
"@unocss/preset-uno": "0.58.9",
|
||||
"@unocss/transformer-directives": "0.58.9",
|
||||
"@unocss/transformer-variant-group": "0.58.9",
|
||||
"@unocss/vite": "0.58.9",
|
||||
"@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.72.0",
|
||||
"simple-git-hooks": "2.11.1",
|
||||
"tsx": "4.7.1",
|
||||
"typescript": "5.4.3",
|
||||
"unplugin-icons": "0.18.5",
|
||||
"unplugin-vue-components": "0.26.0",
|
||||
"vite": "5.2.7",
|
||||
"@unocss/eslint-config": "66.5.1",
|
||||
"@unocss/preset-icons": "66.5.1",
|
||||
"@unocss/preset-uno": "66.5.1",
|
||||
"@unocss/transformer-directives": "66.5.1",
|
||||
"@unocss/transformer-variant-group": "66.5.1",
|
||||
"@unocss/vite": "66.5.1",
|
||||
"@vitejs/plugin-vue": "6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "5.1.1",
|
||||
"consola": "3.4.2",
|
||||
"eslint": "9.35.0",
|
||||
"eslint-plugin-vue": "10.4.0",
|
||||
"kolorist": "1.8.0",
|
||||
"sass": "1.92.1",
|
||||
"simple-git-hooks": "2.13.1",
|
||||
"tsx": "4.20.5",
|
||||
"typescript": "5.9.2",
|
||||
"unplugin-icons": "22.3.0",
|
||||
"unplugin-vue-components": "29.0.0",
|
||||
"vite": "7.1.5",
|
||||
"vite-plugin-progress": "0.0.7",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"vite-plugin-vue-devtools": "7.0.25",
|
||||
"vue-eslint-parser": "9.4.2",
|
||||
"vue-tsc": "2.0.7"
|
||||
"vite-plugin-vue-devtools": "8.0.2",
|
||||
"vue-eslint-parser": "10.2.0",
|
||||
"vue-tsc": "3.0.7"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"commit-msg": "pnpm sa git-commit-verify",
|
||||
"pre-commit": "pnpm typecheck && pnpm lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": "eslint --fix"
|
||||
"pre-commit": "pnpm typecheck && pnpm lint && git diff --exit-code"
|
||||
},
|
||||
"website": "https://admin.soybeanjs.cn"
|
||||
}
|
||||
|
20
packages/alova/package.json
Normal file
20
packages/alova/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@sa/alova",
|
||||
"version": "1.3.15",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./fetch": "./src/fetch.ts",
|
||||
"./client": "./src/client.ts",
|
||||
"./mock": "./src/mock.ts"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@alova/mock": "2.0.17",
|
||||
"@sa/utils": "workspace:*",
|
||||
"alova": "3.3.4"
|
||||
}
|
||||
}
|
1
packages/alova/src/client.ts
Normal file
1
packages/alova/src/client.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from 'alova/client';
|
2
packages/alova/src/constant.ts
Normal file
2
packages/alova/src/constant.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/** the backend error code key */
|
||||
export const BACKEND_ERROR_CODE = 'BACKEND_ERROR';
|
2
packages/alova/src/fetch.ts
Normal file
2
packages/alova/src/fetch.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import adapterFetch from 'alova/fetch';
|
||||
export default adapterFetch;
|
77
packages/alova/src/index.ts
Normal file
77
packages/alova/src/index.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { createAlova } from 'alova';
|
||||
import type { AlovaDefaultCacheAdapter, AlovaGenerics, AlovaGlobalCacheAdapter, AlovaRequestAdapter } from 'alova';
|
||||
import VueHook from 'alova/vue';
|
||||
import type { VueHookType } from 'alova/vue';
|
||||
import adapterFetch from 'alova/fetch';
|
||||
import { createServerTokenAuthentication } from 'alova/client';
|
||||
import type { FetchRequestInit } from 'alova/fetch';
|
||||
import { BACKEND_ERROR_CODE } from './constant';
|
||||
import type { CustomAlovaConfig, RequestOptions } from './type';
|
||||
|
||||
export const createAlovaRequest = <
|
||||
RequestConfig = FetchRequestInit,
|
||||
ResponseType = Response,
|
||||
ResponseHeader = Headers,
|
||||
L1Cache extends AlovaGlobalCacheAdapter = AlovaDefaultCacheAdapter,
|
||||
L2Cache extends AlovaGlobalCacheAdapter = AlovaDefaultCacheAdapter
|
||||
>(
|
||||
customConfig: CustomAlovaConfig<
|
||||
AlovaGenerics<any, any, RequestConfig, ResponseType, ResponseHeader, L1Cache, L2Cache, any>
|
||||
>,
|
||||
options: RequestOptions<AlovaGenerics<any, any, RequestConfig, ResponseType, ResponseHeader, L1Cache, L2Cache, any>>
|
||||
) => {
|
||||
const { tokenRefresher } = options;
|
||||
const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication<
|
||||
VueHookType,
|
||||
AlovaRequestAdapter<RequestConfig, ResponseType, ResponseHeader>
|
||||
>({
|
||||
refreshTokenOnSuccess: {
|
||||
isExpired: (response, method) => tokenRefresher?.isExpired(response, method) || false,
|
||||
handler: async (response, method) => tokenRefresher?.handler(response, method)
|
||||
},
|
||||
refreshTokenOnError: {
|
||||
isExpired: (response, method) => tokenRefresher?.isExpired(response, method) || false,
|
||||
handler: async (response, method) => tokenRefresher?.handler(response, method)
|
||||
}
|
||||
});
|
||||
|
||||
const instance = createAlova({
|
||||
...customConfig,
|
||||
timeout: customConfig.timeout ?? 10 * 1000,
|
||||
requestAdapter: (customConfig.requestAdapter as any) ?? adapterFetch(),
|
||||
statesHook: VueHook,
|
||||
beforeRequest: onAuthRequired(options.onRequest as any),
|
||||
responded: onResponseRefreshToken({
|
||||
onSuccess: async (response, method) => {
|
||||
// check if http status is success
|
||||
let error: any = null;
|
||||
let transformedData: any = null;
|
||||
try {
|
||||
if (await options.isBackendSuccess(response)) {
|
||||
transformedData = await options.transformBackendResponse(response);
|
||||
} else {
|
||||
error = new Error('the backend request error');
|
||||
error.code = BACKEND_ERROR_CODE;
|
||||
}
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
await options.onError?.(error, response, method);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return transformedData;
|
||||
},
|
||||
onComplete: options.onComplete,
|
||||
onError: (error, method) => options.onError?.(error, null, method)
|
||||
})
|
||||
});
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
export { BACKEND_ERROR_CODE };
|
||||
export type * from './type';
|
||||
export type * from 'alova';
|
1
packages/alova/src/mock.ts
Normal file
1
packages/alova/src/mock.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from '@alova/mock';
|
52
packages/alova/src/type.ts
Normal file
52
packages/alova/src/type.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import type { AlovaGenerics, AlovaOptions, AlovaRequestAdapter, Method, ResponseCompleteHandler } from 'alova';
|
||||
|
||||
export type CustomAlovaConfig<AG extends AlovaGenerics> = Omit<
|
||||
AlovaOptions<AG>,
|
||||
'statesHook' | 'beforeRequest' | 'responded' | 'requestAdapter'
|
||||
> & {
|
||||
/** request adapter. all request of alova will be sent by it. */
|
||||
requestAdapter?: AlovaRequestAdapter<AG['RequestConfig'], AG['Response'], AG['ResponseHeader']>;
|
||||
};
|
||||
|
||||
export interface RequestOptions<AG extends AlovaGenerics> {
|
||||
/**
|
||||
* The hook before request
|
||||
*
|
||||
* For example: You can add header token in this hook
|
||||
*
|
||||
* @param method alova Method Instance
|
||||
*/
|
||||
onRequest?: AlovaOptions<AG>['beforeRequest'];
|
||||
/**
|
||||
* The hook to check backend response is success or not
|
||||
*
|
||||
* @param response alova response
|
||||
*/
|
||||
isBackendSuccess: (response: AG['Response']) => Promise<boolean>;
|
||||
|
||||
/** The config to refresh token */
|
||||
tokenRefresher?: {
|
||||
/** detect the token is expired */
|
||||
isExpired(response: AG['Response'], Method: Method<AG>): Promise<boolean> | boolean;
|
||||
/** refresh token handler */
|
||||
handler(response: AG['Response'], Method: Method<AG>): Promise<void>;
|
||||
};
|
||||
|
||||
/** The hook after backend request complete */
|
||||
onComplete?: ResponseCompleteHandler<AG>;
|
||||
|
||||
/**
|
||||
* The hook to handle error
|
||||
*
|
||||
* For example: You can show error message in this hook
|
||||
*
|
||||
* @param error
|
||||
*/
|
||||
onError?: (error: any, response: AG['Response'] | null, methodInstance: Method<AG>) => any | Promise<any>;
|
||||
/**
|
||||
* transform backend response when the responseType is json
|
||||
*
|
||||
* @param response alova response
|
||||
*/
|
||||
transformBackendResponse: (response: AG['Response']) => any;
|
||||
}
|
20
packages/alova/tsconfig.json
Normal file
20
packages/alova/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"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"]
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/axios",
|
||||
"version": "1.0.1",
|
||||
"version": "1.3.15",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
@ -11,11 +11,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sa/utils": "workspace:*",
|
||||
"axios": "1.6.8",
|
||||
"axios-retry": "4.1.0",
|
||||
"qs": "6.12.0"
|
||||
"axios": "1.12.2",
|
||||
"axios-retry": "4.5.0",
|
||||
"qs": "6.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/qs": "6.9.14"
|
||||
"@types/qs": "6.14.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import axios, { AxiosError } from 'axios';
|
||||
import type { AxiosResponse, CancelTokenSource, CreateAxiosDefaults, InternalAxiosRequestConfig } from 'axios';
|
||||
import type { AxiosResponse, CreateAxiosDefaults, InternalAxiosRequestConfig } from 'axios';
|
||||
import axiosRetry from 'axios-retry';
|
||||
import { nanoid } from '@sa/utils';
|
||||
import { createAxiosConfig, createDefaultOptions, createRetryOptions } from './options';
|
||||
import { transformResponse } from './shared';
|
||||
import { BACKEND_ERROR_CODE, REQUEST_ID_KEY } from './constant';
|
||||
import type {
|
||||
CustomAxiosRequestConfig,
|
||||
@ -22,7 +23,7 @@ function createCommonRequest<ResponseData = any>(
|
||||
const axiosConf = createAxiosConfig(axiosConfig);
|
||||
const instance = axios.create(axiosConf);
|
||||
|
||||
const cancelTokenSourceMap = new Map<string, CancelTokenSource>();
|
||||
const abortControllerMap = new Map<string, AbortController>();
|
||||
|
||||
// config axios retry
|
||||
const retryOptions = createRetryOptions(axiosConf);
|
||||
@ -35,10 +36,12 @@ function createCommonRequest<ResponseData = any>(
|
||||
const requestId = nanoid();
|
||||
config.headers.set(REQUEST_ID_KEY, requestId);
|
||||
|
||||
// config cancel token
|
||||
const cancelTokenSource = axios.CancelToken.source();
|
||||
config.cancelToken = cancelTokenSource.token;
|
||||
cancelTokenSourceMap.set(requestId, cancelTokenSource);
|
||||
// config abort controller
|
||||
if (!config.signal) {
|
||||
const abortController = new AbortController();
|
||||
config.signal = abortController.signal;
|
||||
abortControllerMap.set(requestId, abortController);
|
||||
}
|
||||
|
||||
// handle config by hook
|
||||
const handledConfig = opts.onRequest?.(config) || config;
|
||||
@ -48,7 +51,11 @@ function createCommonRequest<ResponseData = any>(
|
||||
|
||||
instance.interceptors.response.use(
|
||||
async response => {
|
||||
if (opts.isBackendSuccess(response)) {
|
||||
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
|
||||
|
||||
await transformResponse(response);
|
||||
|
||||
if (responseType !== 'json' || opts.isBackendSuccess(response)) {
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
|
||||
@ -77,18 +84,18 @@ function createCommonRequest<ResponseData = any>(
|
||||
);
|
||||
|
||||
function cancelRequest(requestId: string) {
|
||||
const cancelTokenSource = cancelTokenSourceMap.get(requestId);
|
||||
if (cancelTokenSource) {
|
||||
cancelTokenSource.cancel();
|
||||
cancelTokenSourceMap.delete(requestId);
|
||||
const abortController = abortControllerMap.get(requestId);
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
abortControllerMap.delete(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
function cancelAllRequest() {
|
||||
cancelTokenSourceMap.forEach(cancelTokenSource => {
|
||||
cancelTokenSource.cancel();
|
||||
abortControllerMap.forEach(abortController => {
|
||||
abortController.abort();
|
||||
});
|
||||
cancelTokenSourceMap.clear();
|
||||
abortControllerMap.clear();
|
||||
}
|
||||
|
||||
return {
|
||||
@ -127,6 +134,7 @@ export function createRequest<ResponseData = any, State = Record<string, unknown
|
||||
|
||||
request.cancelRequest = cancelRequest;
|
||||
request.cancelAllRequest = cancelAllRequest;
|
||||
request.state = {} as State;
|
||||
|
||||
return request;
|
||||
}
|
||||
@ -157,12 +165,12 @@ export function createFlatRequest<ResponseData = any, State = Record<string, unk
|
||||
if (responseType === 'json') {
|
||||
const data = opts.transformBackendResponse(response);
|
||||
|
||||
return { data, error: null };
|
||||
return { data, error: null, response };
|
||||
}
|
||||
|
||||
return { data: response.data as MappedType<R, T>, error: null };
|
||||
} catch (error) {
|
||||
return { data: null, error };
|
||||
return { data: null, error, response: (error as AxiosError<ResponseData>).response };
|
||||
}
|
||||
} as FlatRequestInstance<State, ResponseData>;
|
||||
|
||||
|
@ -20,7 +20,7 @@ export function createDefaultOptions<ResponseData = any>(options?: Partial<Reque
|
||||
|
||||
export function createRetryOptions(config?: Partial<CreateAxiosDefaults>) {
|
||||
const retryConfig: IAxiosRetryConfig = {
|
||||
retries: 3
|
||||
retries: 0
|
||||
};
|
||||
|
||||
Object.assign(retryConfig, config);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { AxiosHeaderValue, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||
import type { ResponseType } from './type';
|
||||
|
||||
export function getContentType(config: InternalAxiosRequestConfig) {
|
||||
const contentType: AxiosHeaderValue = config.headers?.['Content-Type'] || 'application/json';
|
||||
@ -26,3 +27,53 @@ export function isResponseJson(response: AxiosResponse) {
|
||||
|
||||
return responseType === 'json' || responseType === undefined;
|
||||
}
|
||||
|
||||
export async function transformResponse(response: AxiosResponse) {
|
||||
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
|
||||
if (responseType === 'json') return;
|
||||
|
||||
const isJson = response.headers['content-type']?.includes('application/json');
|
||||
if (!isJson) return;
|
||||
|
||||
if (responseType === 'blob') {
|
||||
await transformBlobToJson(response);
|
||||
}
|
||||
|
||||
if (responseType === 'arrayBuffer') {
|
||||
await transformArrayBufferToJson(response);
|
||||
}
|
||||
}
|
||||
|
||||
export async function transformBlobToJson(response: AxiosResponse) {
|
||||
try {
|
||||
let data = response.data;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
|
||||
if (Object.prototype.toString.call(data) === '[object Blob]') {
|
||||
const json = await data.text();
|
||||
data = JSON.parse(json);
|
||||
}
|
||||
|
||||
response.data = data;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export async function transformArrayBufferToJson(response: AxiosResponse) {
|
||||
try {
|
||||
let data = response.data;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
|
||||
if (Object.prototype.toString.call(data) === '[object ArrayBuffer]') {
|
||||
const json = new TextDecoder().decode(data);
|
||||
data = JSON.parse(json);
|
||||
}
|
||||
|
||||
response.data = data;
|
||||
} catch {}
|
||||
}
|
||||
|
@ -69,7 +69,19 @@ export type CustomAxiosRequestConfig<R extends ResponseType = 'json'> = Omit<Axi
|
||||
};
|
||||
|
||||
export interface RequestInstanceCommon<T> {
|
||||
/**
|
||||
* cancel the request by request id
|
||||
*
|
||||
* if the request provide abort controller sign from config, it will not collect in the abort controller map
|
||||
*
|
||||
* @param requestId
|
||||
*/
|
||||
cancelRequest: (requestId: string) => void;
|
||||
/**
|
||||
* cancel all request
|
||||
*
|
||||
* if the request provide abort controller sign from config, it will not collect in the abort controller map
|
||||
*/
|
||||
cancelAllRequest: () => void;
|
||||
/** you can set custom state in the request instance */
|
||||
state: T;
|
||||
@ -80,18 +92,20 @@ export interface RequestInstance<S = Record<string, unknown>> extends RequestIns
|
||||
<T = any, R extends ResponseType = 'json'>(config: CustomAxiosRequestConfig<R>): Promise<MappedType<R, T>>;
|
||||
}
|
||||
|
||||
export type FlatResponseSuccessData<T = any> = {
|
||||
export type FlatResponseSuccessData<T = any, ResponseData = any> = {
|
||||
data: T;
|
||||
error: null;
|
||||
response: AxiosResponse<ResponseData>;
|
||||
};
|
||||
|
||||
export type FlatResponseFailData<ResponseData = any> = {
|
||||
data: null;
|
||||
error: AxiosError<ResponseData>;
|
||||
response: AxiosResponse<ResponseData>;
|
||||
};
|
||||
|
||||
export type FlatResponseData<T = any, ResponseData = any> =
|
||||
| FlatResponseSuccessData<T>
|
||||
| FlatResponseSuccessData<T, ResponseData>
|
||||
| FlatResponseFailData<ResponseData>;
|
||||
|
||||
export interface FlatRequestInstance<S = Record<string, unknown>, ResponseData = any> extends RequestInstanceCommon<S> {
|
||||
|
@ -1,29 +0,0 @@
|
||||
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();
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
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
@ -1,274 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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" }
|
||||
]
|
||||
}
|
||||
]
|
@ -1,95 +0,0 @@
|
||||
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;
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/** 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;
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/color-palette",
|
||||
"version": "1.0.1",
|
||||
"name": "@sa/color",
|
||||
"version": "1.3.15",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
@ -10,6 +10,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@sa/utils": "workspace:*",
|
||||
"colord": "2.9.3"
|
||||
}
|
||||
}
|
2
packages/color/src/constant/index.ts
Normal file
2
packages/color/src/constant/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './name';
|
||||
export * from './palette';
|
1579
packages/color/src/constant/name.ts
Normal file
1579
packages/color/src/constant/name.ts
Normal file
File diff suppressed because it is too large
Load Diff
356
packages/color/src/constant/palette.ts
Normal file
356
packages/color/src/constant/palette.ts
Normal file
@ -0,0 +1,356 @@
|
||||
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 }
|
||||
]
|
||||
}
|
||||
];
|
7
packages/color/src/index.ts
Normal file
7
packages/color/src/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { colorPalettes } from './constant';
|
||||
|
||||
export * from './palette';
|
||||
export * from './shared';
|
||||
export { colorPalettes };
|
||||
|
||||
export * from './types';
|
@ -1,74 +1,6 @@
|
||||
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();
|
||||
}
|
||||
import type { AnyColor, HsvColor } from 'colord';
|
||||
import { getHex, getHsv, isValidColor, mixColor } from '../shared';
|
||||
import type { ColorIndex } from '../types';
|
||||
|
||||
/** Hue step */
|
||||
const hueStep = 2;
|
||||
@ -86,32 +18,23 @@ const lightColorCount = 5;
|
||||
const darkColorCount = 4;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* Get AntD palette color by index
|
||||
*
|
||||
* @param color - Color
|
||||
* @param index - The color index of color palette (the main color index is 6)
|
||||
* @returns Hex color
|
||||
*/
|
||||
export function getColorPalette(color: AnyColor, index: ColorIndex): string {
|
||||
const transformColor = colord(color);
|
||||
|
||||
if (!transformColor.isValid()) {
|
||||
export function getAntDPaletteColorByIndex(color: AnyColor, index: ColorIndex): string {
|
||||
if (!isValidColor(color)) {
|
||||
throw new Error('invalid input color value');
|
||||
}
|
||||
|
||||
if (index === 6) {
|
||||
return colord(transformColor).toHex();
|
||||
return getHex(color);
|
||||
}
|
||||
|
||||
const isLight = index < 6;
|
||||
const hsv = transformColor.toHsv();
|
||||
const hsv = getHsv(color);
|
||||
const i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
|
||||
|
||||
const newHsv: HsvColor = {
|
||||
@ -120,7 +43,7 @@ export function getColorPalette(color: AnyColor, index: ColorIndex): string {
|
||||
v: getValue(hsv, i, isLight)
|
||||
};
|
||||
|
||||
return colord(newHsv).toHex();
|
||||
return getHex(newHsv);
|
||||
}
|
||||
|
||||
/** Map of dark color index and opacity */
|
||||
@ -131,32 +54,33 @@ const darkColorMap = [
|
||||
{ index: 5, opacity: 0.45 },
|
||||
{ index: 5, opacity: 0.65 },
|
||||
{ index: 5, opacity: 0.85 },
|
||||
{ index: 4, opacity: 0.9 },
|
||||
{ index: 5, opacity: 0.9 },
|
||||
{ index: 4, opacity: 0.93 },
|
||||
{ index: 3, opacity: 0.95 },
|
||||
{ index: 2, opacity: 0.97 },
|
||||
{ index: 1, opacity: 0.98 }
|
||||
];
|
||||
|
||||
/**
|
||||
* Get color palettes
|
||||
* Get AntD color palette
|
||||
*
|
||||
* @param color - Color
|
||||
* @param darkTheme - Dark theme
|
||||
* @param darkThemeMixColor - Dark theme mix color (default: #141414)
|
||||
*/
|
||||
export function getColorPalettes(color: AnyColor, darkTheme = false, darkThemeMixColor = '#141414'): string[] {
|
||||
const indexes: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
export function getAntDColorPalette(color: AnyColor, darkTheme = false, darkThemeMixColor = '#141414'): string[] {
|
||||
const indexes: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
|
||||
const patterns = indexes.map(index => getColorPalette(color, index));
|
||||
const patterns = indexes.map(index => getAntDPaletteColorByIndex(color, index));
|
||||
|
||||
if (darkTheme) {
|
||||
const darkPatterns = darkColorMap.map(({ index, opacity }) => {
|
||||
const darkColor = colord(darkThemeMixColor).mix(patterns[index], opacity);
|
||||
const darkColor = mixColor(darkThemeMixColor, patterns[index], opacity);
|
||||
|
||||
return darkColor;
|
||||
});
|
||||
|
||||
return darkPatterns.map(item => colord(item).toHex());
|
||||
return darkPatterns.map(item => getHex(item));
|
||||
}
|
||||
|
||||
return patterns;
|
45
packages/color/src/palette/index.ts
Normal file
45
packages/color/src/palette/index.ts
Normal file
@ -0,0 +1,45 @@
|
||||
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)!;
|
||||
}
|
152
packages/color/src/palette/recommend.ts
Normal file
152
packages/color/src/palette/recommend.ts
Normal file
@ -0,0 +1,152 @@
|
||||
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;
|
||||
}
|
93
packages/color/src/shared/colord.ts
Normal file
93
packages/color/src/shared/colord.ts
Normal file
@ -0,0 +1,93 @@
|
||||
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 };
|
2
packages/color/src/shared/index.ts
Normal file
2
packages/color/src/shared/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './colord';
|
||||
export * from './name';
|
@ -1,6 +1,11 @@
|
||||
import { getHex, getHsl, getRgb } from './color';
|
||||
import colorNames from './json/color-name.json';
|
||||
import { colorNames } from '../constant';
|
||||
import { getHex, getHsl, getRgb } from './colord';
|
||||
|
||||
/**
|
||||
* Get color name
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
export function getColorName(color: string) {
|
||||
const hex = getHex(color);
|
||||
const rgb = getRgb(color);
|
||||
@ -17,15 +22,13 @@ export function getColorName(color: string) {
|
||||
colorNames.some((item, index) => {
|
||||
const [hexValue, colorName] = item;
|
||||
|
||||
const hexcode = `#${hexValue}`;
|
||||
|
||||
const match = hex === hexcode;
|
||||
const match = hex === hexValue;
|
||||
|
||||
if (match) {
|
||||
name = colorName;
|
||||
} else {
|
||||
const { r, g, b } = getRgb(hexcode);
|
||||
const { h, s, l } = getHsl(hexcode);
|
||||
const { r, g, b } = getRgb(hexValue);
|
||||
const { h, s, l } = getHsl(hexValue);
|
||||
|
||||
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;
|
||||
@ -40,7 +43,7 @@ export function getColorName(color: string) {
|
||||
return match;
|
||||
});
|
||||
|
||||
name = cl < 0 ? 'Invalid Color' : colorNames[cl][1];
|
||||
name = colorNames[cl][1];
|
||||
|
||||
return name;
|
||||
}
|
58
packages/color/src/types/index.ts
Normal file
58
packages/color/src/types/index.ts
Normal file
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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;
|
20
packages/color/tsconfig.json
Normal file
20
packages/color/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"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"]
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/hooks",
|
||||
"version": "1.0.1",
|
||||
"version": "1.3.15",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
@ -10,6 +10,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@sa/axios": "workspace:*"
|
||||
"@sa/axios": "workspace:*",
|
||||
"@sa/utils": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
@ -7,4 +7,5 @@ import useHookTable from './use-table';
|
||||
|
||||
export { useBoolean, useLoading, useCountDown, useContext, useSvgIconRender, useHookTable };
|
||||
|
||||
export * from './use-signal';
|
||||
export * from './use-table';
|
||||
|
@ -2,40 +2,59 @@ import { computed, onScopeDispose, ref } from 'vue';
|
||||
import { useRafFn } from '@vueuse/core';
|
||||
|
||||
/**
|
||||
* count down
|
||||
* A hook for implementing a countdown timer. It uses `requestAnimationFrame` for smooth and accurate timing,
|
||||
* independent of the screen refresh rate.
|
||||
*
|
||||
* @param seconds - count down seconds
|
||||
* @param initialSeconds - The total number of seconds for the countdown.
|
||||
*/
|
||||
export default function useCountDown(seconds: number) {
|
||||
const FPS_PER_SECOND = 60;
|
||||
export default function useCountDown(initialSeconds: number) {
|
||||
const remainingSeconds = ref(0);
|
||||
|
||||
const fps = ref(0);
|
||||
const count = computed(() => Math.ceil(remainingSeconds.value));
|
||||
|
||||
const count = computed(() => Math.ceil(fps.value / FPS_PER_SECOND));
|
||||
|
||||
const isCounting = computed(() => fps.value > 0);
|
||||
const isCounting = computed(() => remainingSeconds.value > 0);
|
||||
|
||||
const { pause, resume } = useRafFn(
|
||||
() => {
|
||||
if (fps.value > 0) {
|
||||
fps.value -= 1;
|
||||
} else {
|
||||
({ delta }) => {
|
||||
// delta: milliseconds elapsed since the last frame.
|
||||
|
||||
// If countdown already reached zero or below, ensure it's 0 and stop.
|
||||
if (remainingSeconds.value <= 0) {
|
||||
remainingSeconds.value = 0;
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate seconds passed since the last frame.
|
||||
const secondsPassed = delta / 1000;
|
||||
remainingSeconds.value -= secondsPassed;
|
||||
|
||||
// If countdown has finished after decrementing.
|
||||
if (remainingSeconds.value <= 0) {
|
||||
remainingSeconds.value = 0;
|
||||
pause();
|
||||
}
|
||||
},
|
||||
{ immediate: false }
|
||||
{ immediate: false } // The timer does not start automatically.
|
||||
);
|
||||
|
||||
function start(updateSeconds: number = seconds) {
|
||||
fps.value = FPS_PER_SECOND * updateSeconds;
|
||||
/**
|
||||
* Starts the countdown.
|
||||
*
|
||||
* @param [updatedSeconds=initialSeconds] - Optionally, start with a new duration. Default is `initialSeconds`
|
||||
*/
|
||||
function start(updatedSeconds: number = initialSeconds) {
|
||||
remainingSeconds.value = updatedSeconds;
|
||||
resume();
|
||||
}
|
||||
|
||||
/** Stops the countdown and resets the remaining time to 0. */
|
||||
function stop() {
|
||||
fps.value = 0;
|
||||
remainingSeconds.value = 0;
|
||||
pause();
|
||||
}
|
||||
|
||||
// Ensure the rAF loop is cleaned up when the component is unmounted.
|
||||
onScopeDispose(() => {
|
||||
pause();
|
||||
});
|
||||
|
144
packages/hooks/src/use-signal.ts
Normal file
144
packages/hooks/src/use-signal.ts
Normal file
@ -0,0 +1,144 @@
|
||||
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;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
import type { Ref, VNodeChild } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import useBoolean from './use-boolean';
|
||||
import useLoading from './use-loading';
|
||||
|
||||
@ -7,9 +8,11 @@ export type MaybePromise<T> = T | Promise<T>;
|
||||
|
||||
export type ApiFn = (args: any) => Promise<unknown>;
|
||||
|
||||
export type TableColumnCheckTitle = string | ((...args: any) => VNodeChild);
|
||||
|
||||
export type TableColumnCheck = {
|
||||
key: string;
|
||||
title: string;
|
||||
title: TableColumnCheckTitle;
|
||||
checked: boolean;
|
||||
};
|
||||
|
||||
@ -65,11 +68,11 @@ export default function useHookTable<A extends ApiFn, T, C>(config: TableConfig<
|
||||
|
||||
const { apiFn, apiParams, transformer, immediate = true, getColumnChecks, getColumns } = config;
|
||||
|
||||
const searchParams: NonNullable<Parameters<A>[0]> = reactive({ ...apiParams });
|
||||
const searchParams: NonNullable<Parameters<A>[0]> = reactive(jsonClone({ ...apiParams }));
|
||||
|
||||
const allColumns = ref(config.columns()) as Ref<C[]>;
|
||||
|
||||
const data: Ref<T[]> = ref([]);
|
||||
const data: Ref<TableDataWithIndex<T>[]> = ref([]);
|
||||
|
||||
const columnChecks: Ref<TableColumnCheck[]> = ref(getColumnChecks(config.columns()));
|
||||
|
||||
@ -129,7 +132,7 @@ export default function useHookTable<A extends ApiFn, T, C>(config: TableConfig<
|
||||
|
||||
/** reset search params */
|
||||
function resetSearchParams() {
|
||||
Object.assign(searchParams, apiParams);
|
||||
Object.assign(searchParams, jsonClone(apiParams));
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/materials",
|
||||
"version": "1.0.1",
|
||||
"version": "1.3.15",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
@ -11,7 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sa/utils": "workspace:*",
|
||||
"simplebar-vue": "2.3.3"
|
||||
"simplebar-vue": "2.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typed-css-modules": "0.9.1"
|
||||
|
@ -203,7 +203,7 @@ function handleClickMask() {
|
||||
<!-- Main Content -->
|
||||
<main
|
||||
:id="isContentScroll ? scrollElId : undefined"
|
||||
class="flex flex-grow flex-col"
|
||||
class="flex flex-col flex-grow"
|
||||
:class="[commonClass, contentClass, leftGapClass, { 'overflow-y-auto': isContentScroll }]"
|
||||
>
|
||||
<slot></slot>
|
||||
|
@ -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 rounded-4px border-solid px-12px py-4px"
|
||||
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="[
|
||||
style['button-tab'],
|
||||
{ [style['button-tab_dark']]: darkMode },
|
||||
|
@ -25,31 +25,6 @@ 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;
|
||||
|
||||
@ -88,7 +63,7 @@ function handleClose() {
|
||||
<slot></slot>
|
||||
<template #suffix>
|
||||
<slot name="suffix">
|
||||
<SvgClose v-if="closable" :class="[style['svg-close']]" @click="handleClose" />
|
||||
<SvgClose v-if="closable" :class="[style['svg-close']]" @pointerdown.stop="handleClose" />
|
||||
</slot>
|
||||
</template>
|
||||
</component>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { addColorAlpha, transformColorWithOpacity } from '@sa/utils';
|
||||
import { addColorAlpha, transformColorWithOpacity } from '@sa/color';
|
||||
import type { PageTabCssVars, PageTabCssVarsProps } from '../../types';
|
||||
|
||||
/** The active color of the tab */
|
||||
|
@ -2,23 +2,10 @@
|
||||
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"
|
||||
@click.stop="handleClick"
|
||||
>
|
||||
<div class=":soy: relative h-16px w-16px inline-flex items-center justify-center rd-50% text-14px">
|
||||
<svg width="1em" height="1em" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
fill="currentColor"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/fetch",
|
||||
"version": "1.0.1",
|
||||
"version": "1.3.15",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
@ -10,6 +10,6 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"ofetch": "1.3.4"
|
||||
"ofetch": "1.4.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/scripts",
|
||||
"version": "1.0.1",
|
||||
"version": "1.3.15",
|
||||
"bin": {
|
||||
"sa": "./bin.ts"
|
||||
},
|
||||
@ -13,15 +13,15 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@soybeanjs/changelog": "0.3.15",
|
||||
"bumpp": "9.4.0",
|
||||
"c12": "1.10.0",
|
||||
"@soybeanjs/changelog": "0.3.25",
|
||||
"bumpp": "10.2.3",
|
||||
"c12": "3.3.0",
|
||||
"cac": "6.7.14",
|
||||
"consola": "3.2.3",
|
||||
"consola": "3.4.2",
|
||||
"enquirer": "2.4.1",
|
||||
"execa": "8.0.1",
|
||||
"execa": "9.6.0",
|
||||
"kolorist": "1.8.0",
|
||||
"npm-check-updates": "16.14.18",
|
||||
"rimraf": "5.0.5"
|
||||
"npm-check-updates": "18.1.1",
|
||||
"rimraf": "6.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { prompt } from 'enquirer';
|
||||
import { bgRed, green, red, yellow } from 'kolorist';
|
||||
import { execCommand } from '../shared';
|
||||
import type { CliOption } from '../types';
|
||||
import { locales } from '../locales';
|
||||
import type { Lang } from '../locales';
|
||||
|
||||
interface PromptObject {
|
||||
types: string;
|
||||
@ -14,13 +14,11 @@ interface PromptObject {
|
||||
/**
|
||||
* Git commit with Conventional Commits standard
|
||||
*
|
||||
* @param gitCommitTypes
|
||||
* @param gitCommitScopes
|
||||
* @param lang
|
||||
*/
|
||||
export async function gitCommit(
|
||||
gitCommitTypes: CliOption['gitCommitTypes'],
|
||||
gitCommitScopes: CliOption['gitCommitScopes']
|
||||
) {
|
||||
export async function gitCommit(lang: Lang = 'en-us') {
|
||||
const { gitCommitMessages, gitCommitTypes, gitCommitScopes } = locales[lang];
|
||||
|
||||
const typesChoices = gitCommitTypes.map(([value, msg]) => {
|
||||
const nameWithSuffix = `${value}:`;
|
||||
|
||||
@ -41,19 +39,19 @@ export async function gitCommit(
|
||||
{
|
||||
name: 'types',
|
||||
type: 'select',
|
||||
message: 'Please select a type',
|
||||
message: gitCommitMessages.types,
|
||||
choices: typesChoices
|
||||
},
|
||||
{
|
||||
name: 'scopes',
|
||||
type: 'select',
|
||||
message: 'Please select a scope',
|
||||
message: gitCommitMessages.scopes,
|
||||
choices: scopesChoices
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
message: `Please enter a description (add prefix ${yellow('!')} to indicate breaking change)`
|
||||
message: gitCommitMessages.description
|
||||
}
|
||||
]);
|
||||
|
||||
@ -67,20 +65,20 @@ export async function gitCommit(
|
||||
}
|
||||
|
||||
/** Git commit message verify */
|
||||
export async function gitCommitVerify() {
|
||||
export async function gitCommitVerify(lang: Lang = 'en-us', ignores: RegExp[] = []) {
|
||||
const gitPath = await execCommand('git', ['rev-parse', '--show-toplevel']);
|
||||
|
||||
const gitMsgPath = path.join(gitPath, '.git', 'COMMIT_EDITMSG');
|
||||
|
||||
const commitMsg = readFileSync(gitMsgPath, 'utf8').trim();
|
||||
|
||||
if (ignores.some(regExp => regExp.test(commitMsg))) return;
|
||||
|
||||
const REG_EXP = /(?<type>[a-z]+)(?:\((?<scope>.+)\))?(?<breaking>!)?: (?<description>.+)/i;
|
||||
|
||||
if (!REG_EXP.test(commitMsg)) {
|
||||
throw new Error(
|
||||
`${bgRed(' ERROR ')} ${red('git commit message must match the Conventional Commits standard!')}\n\n${green(
|
||||
'Recommended to use the command `pnpm commit` to generate Conventional Commits compliant commit information.\nGet more info about Conventional Commits, follow this link: https://conventionalcommits.org'
|
||||
)}`
|
||||
);
|
||||
const errorMsg = locales[lang].gitCommitVerify;
|
||||
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
}
|
||||
|
@ -12,32 +12,18 @@ const defaultOptions: CliOption = {
|
||||
'**/node_modules',
|
||||
'!node_modules/**'
|
||||
],
|
||||
gitCommitTypes: [
|
||||
['feat', 'A new feature'],
|
||||
['fix', 'A bug fix'],
|
||||
['docs', 'Documentation only changes'],
|
||||
['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'],
|
||||
['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'],
|
||||
['chore', "Other changes that don't modify src or test files"],
|
||||
['revert', 'Reverts a previous commit']
|
||||
],
|
||||
gitCommitScopes: [
|
||||
['projects', 'project'],
|
||||
['components', 'components'],
|
||||
['hooks', 'hook functions'],
|
||||
['utils', 'utils functions'],
|
||||
['types', 'TS declaration'],
|
||||
['styles', 'style'],
|
||||
['deps', 'project dependencies'],
|
||||
['release', 'release project'],
|
||||
['other', 'other changes']
|
||||
],
|
||||
ncuCommandArgs: ['--deep', '-u'],
|
||||
changelogOptions: {}
|
||||
changelogOptions: {},
|
||||
gitCommitVerifyIgnores: [
|
||||
/^((Merge pull request)|(Merge (.*?) into (.*?)|(Merge branch (.*?)))(?:\r?\n)*$)/m,
|
||||
/^(Merge tag (.*?))(?:\r?\n)*$/m,
|
||||
/^(R|r)evert (.*)/,
|
||||
/^(amend|fixup|squash)!/,
|
||||
/^(Merged (.*?)(in|into) (.*)|Merged PR (.*): (.*))/,
|
||||
/^Merge remote-tracking branch(\s*)(.*)/,
|
||||
/^Automatic merge(.*)/,
|
||||
/^Auto-merged (.*?) into (.*)/
|
||||
]
|
||||
};
|
||||
|
||||
export async function loadCliOptions(overrides?: Partial<CliOption>, cwd = process.cwd()) {
|
||||
|
@ -3,6 +3,7 @@ import { blue, lightGreen } from 'kolorist';
|
||||
import { version } from '../package.json';
|
||||
import { cleanup, genChangelog, generateRoute, gitCommit, gitCommitVerify, release, updatePkg } from './commands';
|
||||
import { loadCliOptions } from './config';
|
||||
import type { Lang } from './locales';
|
||||
|
||||
type Command = 'cleanup' | 'update-pkg' | 'git-commit' | 'git-commit-verify' | 'changelog' | 'release' | 'gen-route';
|
||||
|
||||
@ -18,13 +19,19 @@ interface CommandArg {
|
||||
/** Generate changelog by total tags */
|
||||
total?: boolean;
|
||||
/**
|
||||
* The glob pattern of dirs to cleanup
|
||||
* The glob pattern of dirs to clean up
|
||||
*
|
||||
* If not set, it will use the default value
|
||||
*
|
||||
* Multiple values use "," to separate them
|
||||
*/
|
||||
cleanupDir?: string;
|
||||
/**
|
||||
* display lang of cli
|
||||
*
|
||||
* @default 'en-us'
|
||||
*/
|
||||
lang?: Lang;
|
||||
}
|
||||
|
||||
export async function setupCli() {
|
||||
@ -44,6 +51,7 @@ export async function setupCli() {
|
||||
'-c, --cleanupDir <dir>',
|
||||
'The glob pattern of dirs to cleanup, If not set, it will use the default value, Multiple values use "," to separate them'
|
||||
)
|
||||
.option('-l, --lang <lang>', 'display lang of cli', { default: 'en-us', type: [String] })
|
||||
.help();
|
||||
|
||||
const commands: CommandWithAction<CommandArg> = {
|
||||
@ -61,14 +69,14 @@ export async function setupCli() {
|
||||
},
|
||||
'git-commit': {
|
||||
desc: 'git commit, generate commit message which match Conventional Commits standard',
|
||||
action: async () => {
|
||||
await gitCommit(cliOptions.gitCommitTypes, cliOptions.gitCommitScopes);
|
||||
action: async args => {
|
||||
await gitCommit(args?.lang);
|
||||
}
|
||||
},
|
||||
'git-commit-verify': {
|
||||
desc: 'verify git commit message, make sure it match Conventional Commits standard',
|
||||
action: async () => {
|
||||
await gitCommitVerify();
|
||||
action: async args => {
|
||||
await gitCommitVerify(args?.lang, cliOptions.gitCommitVerifyIgnores);
|
||||
}
|
||||
},
|
||||
changelog: {
|
||||
|
82
packages/scripts/src/locales/index.ts
Normal file
82
packages/scripts/src/locales/index.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { bgRed, green, red, yellow } from 'kolorist';
|
||||
|
||||
export type Lang = 'zh-cn' | 'en-us';
|
||||
|
||||
export const locales = {
|
||||
'zh-cn': {
|
||||
gitCommitMessages: {
|
||||
types: '请选择提交类型',
|
||||
scopes: '请选择提交范围',
|
||||
description: `请输入描述信息(${yellow('!')}开头表示破坏性改动`
|
||||
},
|
||||
gitCommitTypes: [
|
||||
['feat', '新功能'],
|
||||
['feat-wip', '开发中的功能,比如某功能的部分代码'],
|
||||
['fix', '修复Bug'],
|
||||
['docs', '只涉及文档更新'],
|
||||
['typo', '代码或文档勘误,比如错误拼写'],
|
||||
['style', '修改代码风格,不影响代码含义的变更'],
|
||||
['refactor', '代码重构,既不修复 bug 也不添加功能的代码变更'],
|
||||
['perf', '可提高性能的代码更改'],
|
||||
['optimize', '优化代码质量的代码更改'],
|
||||
['test', '添加缺失的测试或更正现有测试'],
|
||||
['build', '影响构建系统或外部依赖项的更改'],
|
||||
['ci', '对 CI 配置文件和脚本的更改'],
|
||||
['chore', '没有修改src或测试文件的其他变更'],
|
||||
['revert', '还原先前的提交']
|
||||
] as [string, string][],
|
||||
gitCommitScopes: [
|
||||
['projects', '项目'],
|
||||
['packages', '包'],
|
||||
['components', '组件'],
|
||||
['hooks', '钩子函数'],
|
||||
['utils', '工具函数'],
|
||||
['types', 'TS类型声明'],
|
||||
['styles', '代码风格'],
|
||||
['deps', '项目依赖'],
|
||||
['release', '发布项目新版本'],
|
||||
['other', '其他的变更']
|
||||
] as [string, string][],
|
||||
gitCommitVerify: `${bgRed(' 错误 ')} ${red('git 提交信息必须符合 Conventional Commits 标准!')}\n\n${green(
|
||||
'推荐使用命令 `pnpm commit` 生成符合 Conventional Commits 标准的提交信息。\n获取有关 Conventional Commits 的更多信息,请访问此链接: https://conventionalcommits.org'
|
||||
)}`
|
||||
},
|
||||
'en-us': {
|
||||
gitCommitMessages: {
|
||||
types: 'Please select a type',
|
||||
scopes: 'Please select a scope',
|
||||
description: `Please enter a description (add prefix ${yellow('!')} to indicate breaking change)`
|
||||
},
|
||||
gitCommitTypes: [
|
||||
['feat', 'A new feature'],
|
||||
['feat-wip', 'Features in development, such as partial code for a certain feature'],
|
||||
['fix', 'A bug fix'],
|
||||
['docs', 'Documentation only changes'],
|
||||
['typo', 'Code or document corrections, such as spelling errors'],
|
||||
['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'],
|
||||
['chore', "Other changes that don't modify src or test files"],
|
||||
['revert', 'Reverts a previous commit']
|
||||
] as [string, string][],
|
||||
gitCommitScopes: [
|
||||
['projects', 'project'],
|
||||
['packages', 'packages'],
|
||||
['components', 'components'],
|
||||
['hooks', 'hook functions'],
|
||||
['utils', 'utils functions'],
|
||||
['types', 'TS declaration'],
|
||||
['styles', 'style'],
|
||||
['deps', 'project dependencies'],
|
||||
['release', 'release project'],
|
||||
['other', 'other changes']
|
||||
] as [string, string][],
|
||||
gitCommitVerify: `${bgRed(' ERROR ')} ${red('git commit message must match the Conventional Commits standard!')}\n\n${green(
|
||||
'Recommended to use the command `pnpm commit` to generate Conventional Commits compliant commit information.\nGet more info about Conventional Commits, follow this link: https://conventionalcommits.org'
|
||||
)}`
|
||||
}
|
||||
} satisfies Record<Lang, Record<string, unknown>>;
|
@ -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?.trim() || '';
|
||||
return (res?.stdout as string)?.trim() || '';
|
||||
}
|
||||
|
@ -14,10 +14,6 @@ export interface CliOption {
|
||||
* ```
|
||||
*/
|
||||
cleanupDirs: string[];
|
||||
/** Git commit types */
|
||||
gitCommitTypes: [string, string][];
|
||||
/** Git commit scopes */
|
||||
gitCommitScopes: [string, string][];
|
||||
/**
|
||||
* Npm-check-updates command args
|
||||
*
|
||||
@ -30,4 +26,6 @@ export interface CliOption {
|
||||
* @link https://github.com/soybeanjs/changelog
|
||||
*/
|
||||
changelogOptions: Partial<ChangelogOption>;
|
||||
/** The ignore pattern list of git commit verify */
|
||||
gitCommitVerifyIgnores: RegExp[];
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/uno-preset",
|
||||
"version": "1.0.1",
|
||||
"version": "1.3.15",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/utils",
|
||||
"version": "1.0.1",
|
||||
"version": "1.3.15",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
@ -12,8 +12,9 @@
|
||||
"dependencies": {
|
||||
"colord": "2.9.3",
|
||||
"crypto-js": "4.2.0",
|
||||
"klona": "2.0.6",
|
||||
"localforage": "1.10.0",
|
||||
"nanoid": "5.0.6"
|
||||
"nanoid": "5.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/crypto-js": "4.2.2"
|
||||
|
@ -1,4 +1,4 @@
|
||||
export * from './color';
|
||||
export * from './crypto';
|
||||
export * from './storage';
|
||||
export * from './nanoid';
|
||||
export * from './klona';
|
||||
|
3
packages/utils/src/klona.ts
Normal file
3
packages/utils/src/klona.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { klona as jsonClone } from 'klona/json';
|
||||
|
||||
export { jsonClone };
|
@ -3,7 +3,7 @@ import localforage from 'localforage';
|
||||
/** The storage type */
|
||||
export type StorageType = 'local' | 'session';
|
||||
|
||||
export function createStorage<T extends object>(type: StorageType) {
|
||||
export function createStorage<T extends object>(type: StorageType, storagePrefix: string) {
|
||||
const stg = type === 'session' ? window.sessionStorage : window.localStorage;
|
||||
|
||||
const storage = {
|
||||
@ -16,7 +16,7 @@ export function createStorage<T extends object>(type: StorageType) {
|
||||
set<K extends keyof T>(key: K, value: T[K]) {
|
||||
const json = JSON.stringify(value);
|
||||
|
||||
stg.setItem(key as string, json);
|
||||
stg.setItem(`${storagePrefix}${key as string}`, json);
|
||||
},
|
||||
/**
|
||||
* Get session
|
||||
@ -24,7 +24,7 @@ export function createStorage<T extends object>(type: StorageType) {
|
||||
* @param key Session key
|
||||
*/
|
||||
get<K extends keyof T>(key: K): T[K] | null {
|
||||
const json = stg.getItem(key as string);
|
||||
const json = stg.getItem(`${storagePrefix}${key as string}`);
|
||||
if (json) {
|
||||
let storageData: T[K] | null = null;
|
||||
|
||||
@ -37,12 +37,12 @@ export function createStorage<T extends object>(type: StorageType) {
|
||||
}
|
||||
}
|
||||
|
||||
stg.removeItem(key as string);
|
||||
stg.removeItem(`${storagePrefix}${key as string}`);
|
||||
|
||||
return null;
|
||||
},
|
||||
remove(key: keyof T) {
|
||||
stg.removeItem(key as string);
|
||||
stg.removeItem(`${storagePrefix}${key as string}`);
|
||||
},
|
||||
clear() {
|
||||
stg.clear();
|
||||
|
20
packages/utils/tsconfig.json
Normal file
20
packages/utils/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"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"]
|
||||
}
|
12912
pnpm-lock.yaml
12912
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
25
src/App.vue
25
src/App.vue
@ -1,8 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { NConfigProvider, darkTheme } from 'naive-ui';
|
||||
import type { WatermarkProps } from 'naive-ui';
|
||||
import { useAppStore } from './store/modules/app';
|
||||
import { useThemeStore } from './store/modules/theme';
|
||||
import { useAuthStore } from './store/modules/auth';
|
||||
import { naiveDateLocales, naiveLocales } from './locales/naive';
|
||||
|
||||
defineOptions({
|
||||
@ -11,6 +13,7 @@ defineOptions({
|
||||
|
||||
const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const naiveDarkTheme = computed(() => (themeStore.darkMode ? darkTheme : undefined));
|
||||
|
||||
@ -21,6 +24,27 @@ const naiveLocale = computed(() => {
|
||||
const naiveDateLocale = computed(() => {
|
||||
return naiveDateLocales[appStore.locale];
|
||||
});
|
||||
|
||||
const watermarkProps = computed<WatermarkProps>(() => {
|
||||
const content =
|
||||
themeStore.watermark.enableUserName && authStore.userInfo.userName
|
||||
? authStore.userInfo.userName
|
||||
: themeStore.watermark.text;
|
||||
|
||||
return {
|
||||
content,
|
||||
cross: true,
|
||||
fullscreen: true,
|
||||
fontSize: 16,
|
||||
lineHeight: 16,
|
||||
width: 384,
|
||||
height: 384,
|
||||
xOffset: 12,
|
||||
yOffset: 60,
|
||||
rotate: -15,
|
||||
zIndex: 9999
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -33,6 +57,7 @@ const naiveDateLocale = computed(() => {
|
||||
>
|
||||
<AppProvider>
|
||||
<RouterView class="bg-layout" />
|
||||
<NWatermark v-if="themeStore.watermark.visible" v-bind="watermarkProps" />
|
||||
</AppProvider>
|
||||
</NConfigProvider>
|
||||
</template>
|
||||
|
@ -21,11 +21,14 @@ const columns = defineModel<NaiveUI.TableColumnCheck[]>('columns', {
|
||||
{{ $t('common.columnSetting') }}
|
||||
</NButton>
|
||||
</template>
|
||||
<VueDraggable v-model="columns">
|
||||
<VueDraggable v-model="columns" :animation="150" filter=".none_draggable">
|
||||
<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 cursor-move text-icon" />
|
||||
<NCheckbox v-model:checked="item.checked">
|
||||
{{ item.title }}
|
||||
<icon-mdi-drag class="mr-8px h-full cursor-move text-icon" />
|
||||
<NCheckbox v-model:checked="item.checked" class="none_draggable flex-1">
|
||||
<template v-if="typeof item.title === 'function'">
|
||||
<component :is="item.title" />
|
||||
</template>
|
||||
<template v-else>{{ item.title }}</template>
|
||||
</NCheckbox>
|
||||
</div>
|
||||
</VueDraggable>
|
||||
|
@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'TableHeaderOperation'
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-container text-base_text transition-300" :class="{ 'bg-inverted text-#1f1f1f': inverted }">
|
||||
<div class="bg-container text-base-text transition-300" :class="{ 'bg-inverted text-#1f1f1f': inverted }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { $t } from '@/locales';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({ name: 'ExceptionBase' });
|
||||
|
||||
|
@ -31,13 +31,25 @@ const tooltipContent = computed(() => {
|
||||
return $t('icon.lang');
|
||||
});
|
||||
|
||||
/** Add bottom margin to all options except the last one for proper visual separation */
|
||||
const dropdownOptions = computed(() => {
|
||||
const lastIndex = props.langOptions.length - 1;
|
||||
|
||||
return props.langOptions.map((option, index) => ({
|
||||
...option,
|
||||
props: {
|
||||
class: index < lastIndex ? 'mb-1' : undefined
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
function changeLang(lang: App.I18n.LangType) {
|
||||
emit('changeLang', lang);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDropdown :value="lang" :options="langOptions" trigger="hover" @select="changeLang">
|
||||
<NDropdown :value="lang" :options="dropdownOptions" trigger="hover" @select="changeLang">
|
||||
<div>
|
||||
<ButtonIcon :tooltip-content="tooltipContent" tooltip-placement="left">
|
||||
<SvgIcon icon="heroicons:language" />
|
||||
|
@ -9,9 +9,13 @@ interface Props {
|
||||
collapsed?: boolean;
|
||||
/** Arrow style icon */
|
||||
arrowIcon?: boolean;
|
||||
zIndex?: number;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
arrowIcon: false,
|
||||
zIndex: 98
|
||||
});
|
||||
|
||||
type NumberBool = 0 | 1;
|
||||
|
||||
@ -36,7 +40,12 @@ const icon = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ButtonIcon :tooltip-content="collapsed ? $t('icon.expand') : $t('icon.collapse')" tooltip-placement="bottom-start">
|
||||
<ButtonIcon
|
||||
:key="String(collapsed)"
|
||||
:tooltip-content="collapsed ? $t('icon.expand') : $t('icon.collapse')"
|
||||
tooltip-placement="bottom-start"
|
||||
:z-index="zIndex"
|
||||
>
|
||||
<SvgIcon :icon="icon" />
|
||||
</ButtonIcon>
|
||||
</template>
|
||||
|
@ -15,7 +15,7 @@ const icon = computed(() => (props.pin ? 'mdi-pin-off' : 'mdi-pin'));
|
||||
|
||||
<template>
|
||||
<ButtonIcon
|
||||
:tooltip-content="pin ? $t('icon.pin') : $t('icon.unpin')"
|
||||
:tooltip-content="pin ? $t('icon.unpin') : $t('icon.pin')"
|
||||
tooltip-placement="bottom-start"
|
||||
:z-index="100"
|
||||
>
|
||||
|
@ -1,7 +1,6 @@
|
||||
<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',
|
||||
@ -21,60 +20,29 @@ interface Props {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
class: 'h-36px text-icon',
|
||||
class: '',
|
||||
icon: '',
|
||||
tooltipContent: '',
|
||||
tooltipPlacement: 'bottom',
|
||||
zIndex: 98
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
const DEFAULT_CLASS = 'h-[36px] text-icon';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 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">
|
||||
<NTooltip :placement="tooltipPlacement" :z-index="zIndex" :disabled="!tooltipContent">
|
||||
<template #trigger>
|
||||
<Button :class-name="cls" v-bind="$attrs">
|
||||
<NButton quaternary :class="twMerge(DEFAULT_CLASS, props.class)" v-bind="$attrs">
|
||||
<div class="flex-center gap-8px">
|
||||
<slot>
|
||||
<SvgIcon :icon="icon" />
|
||||
</slot>
|
||||
</Button>
|
||||
</div>
|
||||
</NButton>
|
||||
</template>
|
||||
{{ tooltipContent }}
|
||||
</NTooltip>
|
||||
<Button v-else :class-name="cls" v-bind="$attrs">
|
||||
<slot>
|
||||
<SvgIcon :icon="icon" />
|
||||
</slot>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { computed, useAttrs } from 'vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
|
||||
defineOptions({ name: 'SvgIcon' });
|
||||
defineOptions({ name: 'SvgIcon', inheritAttrs: false });
|
||||
|
||||
/**
|
||||
* Props
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { getColorPalette } from '@sa/utils';
|
||||
import { getPaletteColorByNumber } from '@sa/color';
|
||||
|
||||
defineOptions({ name: 'WaveBg' });
|
||||
|
||||
@ -11,8 +11,8 @@ interface Props {
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const lightColor = computed(() => getColorPalette(props.themeColor, 3));
|
||||
const darkColor = computed(() => getColorPalette(props.themeColor, 6));
|
||||
const lightColor = computed(() => getPaletteColorByNumber(props.themeColor, 200));
|
||||
const darkColor = computed(() => getPaletteColorByNumber(props.themeColor, 500));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { transformRecordToOption } from '@/utils/common';
|
||||
|
||||
export const GLOBAL_HEADER_MENU_ID = '__GLOBAL_HEADER_MENU__';
|
||||
|
||||
export const GLOBAL_SIDER_MENU_ID = '__GLOBAL_SIDER_MENU__';
|
||||
|
||||
export const themeSchemaRecord: Record<UnionKey.ThemeScheme, App.I18n.I18nKey> = {
|
||||
light: 'theme.themeSchema.light',
|
||||
dark: 'theme.themeSchema.dark',
|
||||
@ -50,3 +54,12 @@ export const themePageAnimationModeRecord: Record<UnionKey.ThemePageAnimateMode,
|
||||
};
|
||||
|
||||
export const themePageAnimationModeOptions = transformRecordToOption(themePageAnimationModeRecord);
|
||||
|
||||
export const resetCacheStrategyRecord: Record<UnionKey.ResetCacheStrategy, App.I18n.I18nKey> = {
|
||||
close: 'theme.resetCacheStrategy.close',
|
||||
refresh: 'theme.resetCacheStrategy.refresh'
|
||||
};
|
||||
|
||||
export const resetCacheStrategyOptions = transformRecordToOption(resetCacheStrategyRecord);
|
||||
|
||||
export const DARK_CLASS = 'dark';
|
||||
|
@ -1,29 +0,0 @@
|
||||
import { transformRecordToOption } from '@/utils/common';
|
||||
|
||||
export const enableStatusRecord: Record<Api.Common.EnableStatus, App.I18n.I18nKey> = {
|
||||
'1': 'page.manage.common.status.enable',
|
||||
'2': 'page.manage.common.status.disable'
|
||||
};
|
||||
|
||||
export const enableStatusOptions = transformRecordToOption(enableStatusRecord);
|
||||
|
||||
export const userGenderRecord: Record<Api.SystemManage.UserGender, App.I18n.I18nKey> = {
|
||||
'1': 'page.manage.user.gender.male',
|
||||
'2': 'page.manage.user.gender.female'
|
||||
};
|
||||
|
||||
export const userGenderOptions = transformRecordToOption(userGenderRecord);
|
||||
|
||||
export const menuTypeRecord: Record<Api.SystemManage.MenuType, App.I18n.I18nKey> = {
|
||||
'1': 'page.manage.menu.type.directory',
|
||||
'2': 'page.manage.menu.type.menu'
|
||||
};
|
||||
|
||||
export const menuTypeOptions = transformRecordToOption(menuTypeRecord);
|
||||
|
||||
export const menuIconTypeRecord: Record<Api.SystemManage.IconType, App.I18n.I18nKey> = {
|
||||
'1': 'page.manage.menu.iconType.iconify',
|
||||
'2': 'page.manage.menu.iconType.local'
|
||||
};
|
||||
|
||||
export const menuIconTypeOptions = transformRecordToOption(menuIconTypeRecord);
|
@ -1,7 +1,7 @@
|
||||
import { computed } from 'vue';
|
||||
import { useCountDown, useLoading } from '@sa/hooks';
|
||||
import { $t } from '@/locales';
|
||||
import { REG_PHONE } from '@/constants/reg';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
export function useCaptcha() {
|
||||
const { loading, startLoading, endLoading } = useLoading();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user