mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2026-03-09 13:24:24 +08:00
Compare commits
110 Commits
30e3cdc7b9
...
example
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2aa59c1879 | ||
|
|
85d5f13630 | ||
|
|
3c2cbb7488 | ||
|
|
6fc6f1c9b3 | ||
|
|
781a18f453 | ||
|
|
6ff74c0c9d | ||
|
|
b867c2908f | ||
|
|
9d48ca5f1c | ||
|
|
b520db3ef4 | ||
|
|
f96c3c9e11 | ||
|
|
a37949f2af | ||
|
|
54107acaac | ||
|
|
cbfb932f8e | ||
|
|
dacee143c0 | ||
|
|
2a0231da55 | ||
|
|
d73947a5ab | ||
|
|
0081b9c022 | ||
|
|
963c1b3c79 | ||
|
|
702a2b2711 | ||
|
|
c9306dcda9 | ||
|
|
25020a9812 | ||
|
|
5c02884d11 | ||
|
|
246400b7f1 | ||
|
|
e9f1fc0628 | ||
|
|
102d55274c | ||
|
|
0919dd0743 | ||
|
|
93cbde737a | ||
|
|
08c8e82072 | ||
|
|
c70c2d2147 | ||
|
|
e46b02d5a9 | ||
|
|
30b5e6e96a | ||
|
|
cfe62d5afb | ||
|
|
1740ca2cfa | ||
|
|
643ccec544 | ||
|
|
bf9baf0d14 | ||
|
|
bbadd41e0e | ||
|
|
7e1e1716f9 | ||
|
|
a63a343e9c | ||
|
|
c816f4dc3b | ||
|
|
ec20829a22 | ||
|
|
f5e6a205b3 | ||
|
|
ff83d333a5 | ||
|
|
9a294204a2 | ||
|
|
a5c9a19838 | ||
|
|
03f098bec6 | ||
|
|
69788d0134 | ||
|
|
745a9077ec | ||
|
|
60c307ccda | ||
|
|
823651139a | ||
|
|
d1c6081c25 | ||
|
|
b9e873e53c | ||
|
|
aabe7fe592 | ||
|
|
7e436b6328 | ||
|
|
e019defef3 | ||
|
|
a2178d3405 | ||
|
|
0c0d606ae5 | ||
|
|
a6545265ac | ||
|
|
641f3160d6 | ||
|
|
3fbf2e2a5f | ||
|
|
405b1b41cc | ||
|
|
da6cf09e76 | ||
|
|
5bfc7613f7 | ||
|
|
7710bcdb0a | ||
|
|
b36d914e48 | ||
|
|
bbacff0581 | ||
|
|
f86bca063a | ||
|
|
48e005bcaf | ||
|
|
d825b6e260 | ||
|
|
05b0147b47 | ||
|
|
e7cd34ec37 | ||
|
|
a9dce21878 | ||
|
|
24320ab899 | ||
|
|
2f1f2c2b2a | ||
|
|
3a970a090d | ||
|
|
4a7e9c2669 | ||
|
|
d6f3effce0 | ||
|
|
fa4367a7b9 | ||
|
|
c425822b3a | ||
|
|
447bda2b48 | ||
|
|
c0303a8637 | ||
|
|
c7ab61bbd4 | ||
|
|
ec6a497452 | ||
|
|
074077dfaa | ||
|
|
fcf866783c | ||
|
|
1c9e36c47b | ||
|
|
d3210edc6a | ||
|
|
799ebcc714 | ||
|
|
4a8b9032cd | ||
|
|
8fea803fca | ||
|
|
599d0f10e8 | ||
|
|
781f88d227 | ||
|
|
5739346484 | ||
|
|
d194f0b1b6 | ||
|
|
19bea283c9 | ||
|
|
53a8e5678a | ||
|
|
10b231918a | ||
|
|
81d51a9630 | ||
|
|
fbebef3acb | ||
|
|
12d7bf94ee | ||
|
|
0478154db8 | ||
|
|
994ef2b9b1 | ||
|
|
ef32eecfdb | ||
|
|
83ae0bad0f | ||
|
|
9a9e2d144a | ||
|
|
a2110a98cf | ||
|
|
daf6600c21 | ||
|
|
a9b3a961af | ||
|
|
3a35d62e6f | ||
|
|
aaf28a42c2 | ||
|
|
019f80faef |
2
.github/ISSUE_TEMPLATE/bug-report_cn.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug-report_cn.yaml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name: 🐞 Bug提交
|
name: 🐞 Bug提交
|
||||||
description: 在使用软件或功能的过程中遇到了错误
|
description: 在使用软件或功能的过程中遇到了错误
|
||||||
title: '[Bug]: '
|
title: '[Bug]: '
|
||||||
labels: [ "bug?" ]
|
labels: ['bug?']
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/bug-report_en.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug-report_en.yaml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name: 🐞 Bug Report
|
name: 🐞 Bug Report
|
||||||
description: Encountered an error while using the software or feature
|
description: Encountered an error while using the software or feature
|
||||||
title: '[Bug]: '
|
title: '[Bug]: '
|
||||||
labels: [ "bug?" ]
|
labels: ['bug?']
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: 🚀 功能请求
|
name: 🚀 功能请求
|
||||||
description: 提出一个想法以帮助我们改进W&B
|
description: 提出一个想法以帮助我们改进W&B
|
||||||
title: "[功能]: "
|
title: '[功能]: '
|
||||||
labels:
|
labels:
|
||||||
- "功能请求"
|
- '功能请求'
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: 🚀 Feature Request
|
name: 🚀 Feature Request
|
||||||
description: Suggest an idea to help us improve W&B
|
description: Suggest an idea to help us improve W&B
|
||||||
title: "[Feature]: "
|
title: '[Feature]: '
|
||||||
labels:
|
labels:
|
||||||
- "feature_request"
|
- 'feature_request'
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
|||||||
34
.github/workflows/opencode.yml
vendored
Normal file
34
.github/workflows/opencode.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: opencode
|
||||||
|
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
pull_request_review_comment:
|
||||||
|
types: [created]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
opencode:
|
||||||
|
if: |
|
||||||
|
contains(github.event.comment.body, ' /oc') ||
|
||||||
|
startsWith(github.event.comment.body, '/oc') ||
|
||||||
|
contains(github.event.comment.body, ' /opencode') ||
|
||||||
|
startsWith(github.event.comment.body, '/opencode')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
issues: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Run opencode
|
||||||
|
uses: anomalyco/opencode/github@latest
|
||||||
|
env:
|
||||||
|
GOOGLE_GENERATIVE_AI_API_KEY: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }}
|
||||||
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||||
|
with:
|
||||||
|
model: google/gemini-3-pro-preview
|
||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -6,7 +6,7 @@ permissions:
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "v*"
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
|
|||||||
11
.oxfmtrc.json
Normal file
11
.oxfmtrc.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"ignorePatterns": ["src/typings/components.d.ts", "src/typings/elegant-router.d.ts", "src/router/elegant"],
|
||||||
|
"printWidth": 120,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"htmlWhitespaceSensitivity": "ignore",
|
||||||
|
"experimentalSortPackageJson": {
|
||||||
|
"sortScripts": true
|
||||||
|
}
|
||||||
|
}
|
||||||
14
.oxlintrc.json
Normal file
14
.oxlintrc.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"categories": {
|
||||||
|
"correctness": "error",
|
||||||
|
"suspicious": "error"
|
||||||
|
},
|
||||||
|
"plugins": ["eslint", "typescript", "unicorn", "oxc", "import", "vue"],
|
||||||
|
"rules": {
|
||||||
|
"import/no-unassigned-import": "off",
|
||||||
|
"unicorn/consistent-function-scoping": "off",
|
||||||
|
"unicorn/no-array-sort": "off",
|
||||||
|
"unicorn/no-array-reverse": "off",
|
||||||
|
"unicorn/require-module-specifiers": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
19
.vscode/settings.json
vendored
19
.vscode/settings.json
vendored
@@ -1,21 +1,11 @@
|
|||||||
{
|
{
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": "explicit",
|
"source.fixAll.eslint": "explicit",
|
||||||
"source.organizeImports": "never"
|
"source.fixAll.oxc": "explicit"
|
||||||
},
|
},
|
||||||
"editor.formatOnSave": false,
|
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||||
"eslint.validate": [
|
"editor.formatOnSave": true,
|
||||||
"html",
|
"eslint.validate": ["vue"],
|
||||||
"css",
|
|
||||||
"scss",
|
|
||||||
"json",
|
|
||||||
"jsonc",
|
|
||||||
"javascript",
|
|
||||||
"javascriptreact",
|
|
||||||
"typescript",
|
|
||||||
"typescriptreact",
|
|
||||||
"vue"
|
|
||||||
],
|
|
||||||
"i18n-ally.displayLanguage": "zh-cn",
|
"i18n-ally.displayLanguage": "zh-cn",
|
||||||
"i18n-ally.enabledParsers": ["ts"],
|
"i18n-ally.enabledParsers": ["ts"],
|
||||||
"i18n-ally.enabledFrameworks": ["vue"],
|
"i18n-ally.enabledFrameworks": ["vue"],
|
||||||
@@ -26,6 +16,5 @@
|
|||||||
"moduleResolution": "node"
|
"moduleResolution": "node"
|
||||||
},
|
},
|
||||||
"prettier.enable": false,
|
"prettier.enable": false,
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
|
||||||
"unocss.root": ["./"]
|
"unocss.root": ["./"]
|
||||||
}
|
}
|
||||||
|
|||||||
71
CHANGELOG.md
71
CHANGELOG.md
@@ -1,5 +1,60 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [v2.1.0](https://github.com/soybeanjs/soybean-admin/compare/v2.0.2...v2.1.0) (2026-03-09)
|
||||||
|
|
||||||
|
### 🚨 Breaking Changes
|
||||||
|
|
||||||
|
- **projects**: integrate oxlint and oxfmt - by **soybeanfe** [<samp>(6ff74)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6ff74c0c)
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- **components**:
|
||||||
|
- Column settings support fixed columns. - by @m-xlsea [<samp>(70658)</samp>](https://github.com/soybeanjs/soybean-admin/commit/70658643)
|
||||||
|
- Add “Select All” to TableColumnSetting - by @wenyuanw [<samp>(0081b)</samp>](https://github.com/soybeanjs/soybean-admin/commit/0081b9c0)
|
||||||
|
- **logo**:
|
||||||
|
- use new logo - by @soybeanjs [<samp>(5aac5)</samp>](https://github.com/soybeanjs/soybean-admin/commit/5aac540a)
|
||||||
|
- **projects**:
|
||||||
|
- add the plugin: vite-plugin-vue-transition-root-validator, to optimize the development experience. - by **Azir-11** [<samp>(30e3c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/30e3cdc7)
|
||||||
|
- **types**:
|
||||||
|
- Added type definition `force` to router push options. - by @m-xlsea [<samp>(a3794)</samp>](https://github.com/soybeanjs/soybean-admin/commit/a37949f2)
|
||||||
|
- **workflows**:
|
||||||
|
- add opencode workflow for issue and PR comment triggers - by @soybeanjs [<samp>(dacee)</samp>](https://github.com/soybeanjs/soybean-admin/commit/dacee143)
|
||||||
|
|
||||||
|
### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- **projects**:
|
||||||
|
- fix the long list TableColumnSetting component exceeds the viewport. - by **skyfeiz** [<samp>(cbfb9)</samp>](https://github.com/soybeanjs/soybean-admin/commit/cbfb932f)
|
||||||
|
- fix NButton props conflicts. - by **skyfeiz** [<samp>(54107)</samp>](https://github.com/soybeanjs/soybean-admin/commit/54107aca)
|
||||||
|
- ensure HTML lang attribute is updated when setting locale - by @pan0xc [<samp>(f96c3)</samp>](https://github.com/soybeanjs/soybean-admin/commit/f96c3c9e)
|
||||||
|
- correct HTML lang attribute to standard format - by @pan0xc [<samp>(b520d)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b520db3e)
|
||||||
|
- **router**:
|
||||||
|
- simplify route guard logic and remove unnecessary next calls - by **soybeanfe** [<samp>(3c2cb)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3c2cbb74)
|
||||||
|
|
||||||
|
### 🛠 Optimizations
|
||||||
|
|
||||||
|
- **projects**:
|
||||||
|
- modify the injection location of the token. - by **Azir-11** [<samp>(9d48c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9d48ca5f)
|
||||||
|
- optimize unocss config - by **soybeanfe** [<samp>(6fc6f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/6fc6f1c9)
|
||||||
|
|
||||||
|
### 📖 Documentation
|
||||||
|
|
||||||
|
- **projects**: V2 has been released. - by **skyfeiz** [<samp>(d7394)</samp>](https://github.com/soybeanjs/soybean-admin/commit/d73947a5)
|
||||||
|
|
||||||
|
### 🏡 Chore
|
||||||
|
|
||||||
|
- **deps**:
|
||||||
|
- update deps - by @soybeanjs [<samp>(232e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/232e1ac4)
|
||||||
|
- update deps - by @soybeanjs [<samp>(2a023)</samp>](https://github.com/soybeanjs/soybean-admin/commit/2a0231da)
|
||||||
|
- update deps - by **soybeanfe** [<samp>(b867c)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b867c290)
|
||||||
|
|
||||||
|
### 🎨 Styles
|
||||||
|
|
||||||
|
- **projects**: fix lint code and format code - by **soybeanfe** [<samp>(781a1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/781a18f4)
|
||||||
|
|
||||||
|
### ❤️ Contributors
|
||||||
|
|
||||||
|
[](https://github.com/pan0xc) [](https://github.com/m-xlsea) [](https://github.com/soybeanjs) [](https://github.com/wenyuanw)
|
||||||
|
[soybeanfe](mailto:honghuangdc@gmail.com), [Azir-11](mailto:2075125282@qq.com), [skyfeiz](mailto:webzhangfei@163.com),
|
||||||
|
|
||||||
## [v2.0.2](https://github.com/soybeanjs/soybean-admin/compare/v2.0.1...v2.0.2) (2025-12-23)
|
## [v2.0.2](https://github.com/soybeanjs/soybean-admin/compare/v2.0.1...v2.0.2) (2025-12-23)
|
||||||
|
|
||||||
@@ -25,7 +80,7 @@
|
|||||||
- update QQ group image in README - by @soybeanjs [<samp>(46081)</samp>](https://github.com/soybeanjs/soybean-admin/commit/46081c36)
|
- update QQ group image in README - by @soybeanjs [<samp>(46081)</samp>](https://github.com/soybeanjs/soybean-admin/commit/46081c36)
|
||||||
- **projects**:
|
- **projects**:
|
||||||
- support theme presets to only set partial content. - by **Azir-11** [<samp>(9da84)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9da847fb)
|
- support theme presets to only set partial content. - by **Azir-11** [<samp>(9da84)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9da847fb)
|
||||||
- support theme perset to override component library presets. - by **Azir-11** [<samp>(60517)</samp>](https://github.com/soybeanjs/soybean-admin/commit/605173a1)
|
- support theme perset to override component library presets. - by **Azir-11** [<samp>(60517)</samp>](https://github.com/soybeanjs/soybean-admin/commit/605173a1)
|
||||||
- support pinning and unpinning of tabs - by **hooke** [<samp>(b8a76)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b8a767d7)
|
- support pinning and unpinning of tabs - by **hooke** [<samp>(b8a76)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b8a767d7)
|
||||||
- hybrid layout mode auto select first deepest child menu - by @paynezhuang [<samp>(94019)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9401925f)
|
- hybrid layout mode auto select first deepest child menu - by @paynezhuang [<samp>(94019)</samp>](https://github.com/soybeanjs/soybean-admin/commit/9401925f)
|
||||||
|
|
||||||
@@ -425,13 +480,13 @@
|
|||||||
### 🏡 Chore
|
### 🏡 Chore
|
||||||
|
|
||||||
- **deps**:
|
- **deps**:
|
||||||
- add vscode recommend plugin close #738 - by @tu6ge in https://github.com/soybeanjs/soybean-admin/issues/739 and https://github.com/soybeanjs/soybean-admin/issues/738 [<samp>(61244)</samp>](https://github.com/soybeanjs/soybean-admin/commit/61244f0f)
|
- add vscode recommend plugin close #738 - by @tu6ge in https://github.com/soybeanjs/soybean-admin/issues/739 and https://github.com/soybeanjs/soybean-admin/issues/738 [<samp>(61244)</samp>](https://github.com/soybeanjs/soybean-admin/commit/61244f0f)
|
||||||
- update deps - by @soybeanjs [<samp>(41b5f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/41b5f493)
|
- update deps - by @soybeanjs [<samp>(41b5f)</samp>](https://github.com/soybeanjs/soybean-admin/commit/41b5f493)
|
||||||
- update deps - by @soybeanjs [<samp>(3e4e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3e4e17ab)
|
- update deps - by @soybeanjs [<samp>(3e4e1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/3e4e17ab)
|
||||||
|
|
||||||
### 🤖 CI
|
### 🤖 CI
|
||||||
|
|
||||||
- **hooks**: remove lint-staged in git hook. close #724 - by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/743 and https://github.com/soybeanjs/soybean-admin/issues/724 [<samp>(c3abc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c3abc3df)
|
- **hooks**: remove lint-staged in git hook. close #724 - by @Azir-11 in https://github.com/soybeanjs/soybean-admin/issues/743 and https://github.com/soybeanjs/soybean-admin/issues/724 [<samp>(c3abc)</samp>](https://github.com/soybeanjs/soybean-admin/commit/c3abc3df)
|
||||||
|
|
||||||
### ❤️ Contributors
|
### ❤️ Contributors
|
||||||
|
|
||||||
@@ -551,7 +606,7 @@
|
|||||||
|
|
||||||
### 📖 Documentation
|
### 📖 Documentation
|
||||||
|
|
||||||
- **projects**: ✏️ add element-plus version link - by **一寸灰** in https://github.com/honghuangdc/soybean-admin/issues/679 [<samp>(5c6ab)</samp>](https://github.com/honghuangdc/soybean-admin/commit/5c6ab0b)
|
- **projects**: ✏️ add element-plus version link - by **一寸灰** in https://github.com/honghuangdc/soybean-admin/issues/679 [<samp>(5c6ab)</samp>](https://github.com/honghuangdc/soybean-admin/commit/5c6ab0b)
|
||||||
|
|
||||||
### 🏡 Chore
|
### 🏡 Chore
|
||||||
|
|
||||||
@@ -593,7 +648,6 @@
|
|||||||
|
|
||||||
### ❤️ Contributors
|
### ❤️ Contributors
|
||||||
|
|
||||||
|
|
||||||
[Soybean](mailto:soybeanjs@outlook.com), [青菜白玉汤](mailto:79054161+Azir-11@users.noreply.github.com)
|
[Soybean](mailto:soybeanjs@outlook.com), [青菜白玉汤](mailto:79054161+Azir-11@users.noreply.github.com)
|
||||||
|
|
||||||
## [v1.3.8](https://github.com/soybeanjs/soybean-admin/compare/v1.3.7...v1.3.8) (2024-10-25)
|
## [v1.3.8](https://github.com/soybeanjs/soybean-admin/compare/v1.3.7...v1.3.8) (2024-10-25)
|
||||||
@@ -1640,7 +1694,7 @@
|
|||||||
- Optimize internationalized menu search code - by **燕博文** [<samp>(8c1ef)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8c1ef4b)
|
- Optimize internationalized menu search code - by **燕博文** [<samp>(8c1ef)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8c1ef4b)
|
||||||
- Optimize menu search code - by **燕博文** [<samp>(296a2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/296a2d2)
|
- Optimize menu search code - by **燕博文** [<samp>(296a2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/296a2d2)
|
||||||
- perf count-to - by @honghuangdc [<samp>(b2c61)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b2c61f0)
|
- perf count-to - by @honghuangdc [<samp>(b2c61)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b2c61f0)
|
||||||
- components name is converted to uppercase - by **燕博文** [<samp>(04aa1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/04aa10b)
|
- components name is converted to uppercase - by **燕博文** [<samp>(04aa1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/04aa10b)
|
||||||
- perf global-search - by @honghuangdc [<samp>(72745)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7274522)
|
- perf global-search - by @honghuangdc [<samp>(72745)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7274522)
|
||||||
- **projects**:
|
- **projects**:
|
||||||
- perf code - by @honghuangdc [<samp>(8081e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8081e19)
|
- perf code - by @honghuangdc [<samp>(8081e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8081e19)
|
||||||
@@ -1917,7 +1971,7 @@
|
|||||||
- Optimize internationalized menu search code - by **燕博文** [<samp>(8c1ef)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8c1ef4b)
|
- Optimize internationalized menu search code - by **燕博文** [<samp>(8c1ef)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8c1ef4b)
|
||||||
- Optimize menu search code - by **燕博文** [<samp>(296a2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/296a2d2)
|
- Optimize menu search code - by **燕博文** [<samp>(296a2)</samp>](https://github.com/soybeanjs/soybean-admin/commit/296a2d2)
|
||||||
- perf count-to - by @honghuangdc [<samp>(b2c61)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b2c61f0)
|
- perf count-to - by @honghuangdc [<samp>(b2c61)</samp>](https://github.com/soybeanjs/soybean-admin/commit/b2c61f0)
|
||||||
- components name is converted to uppercase - by **燕博文** [<samp>(04aa1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/04aa10b)
|
- components name is converted to uppercase - by **燕博文** [<samp>(04aa1)</samp>](https://github.com/soybeanjs/soybean-admin/commit/04aa10b)
|
||||||
- perf global-search - by @honghuangdc [<samp>(72745)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7274522)
|
- perf global-search - by @honghuangdc [<samp>(72745)</samp>](https://github.com/soybeanjs/soybean-admin/commit/7274522)
|
||||||
- **projects**:
|
- **projects**:
|
||||||
- perf code - by @honghuangdc [<samp>(8081e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8081e19)
|
- perf code - by @honghuangdc [<samp>(8081e)</samp>](https://github.com/soybeanjs/soybean-admin/commit/8081e19)
|
||||||
@@ -3312,6 +3366,3 @@
|
|||||||
### ❤️ Contributors
|
### ❤️ Contributors
|
||||||
|
|
||||||
[](https://github.com/honghuangdc) [](https://github.com/soybeanjs)
|
[](https://github.com/honghuangdc) [](https://github.com/soybeanjs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,6 @@
|
|||||||
[](https://github.com/honghuangdc) [](https://github.com/Azir-11)
|
[](https://github.com/honghuangdc) [](https://github.com/Azir-11)
|
||||||
[Azir](mailto:2075125282@qq.com),
|
[Azir](mailto:2075125282@qq.com),
|
||||||
|
|
||||||
|
|
||||||
## [v1.3.0](https://github.com/soybeanjs/soybean-admin/compare/v1.2.8...v1.3.0) (2024-07-22)
|
## [v1.3.0](https://github.com/soybeanjs/soybean-admin/compare/v1.2.8...v1.3.0) (2024-07-22)
|
||||||
|
|
||||||
### 🚨 破坏性变更
|
### 🚨 破坏性变更
|
||||||
@@ -182,7 +181,6 @@
|
|||||||
|
|
||||||
[](https://github.com/honghuangdc) [](https://github.com/mmdapl) [](https://github.com/Azir-11)
|
[](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)
|
## [v1.2.8](https://github.com/soybeanjs/soybean-admin/compare/v1.2.7...v1.2.8) (2024-07-20)
|
||||||
|
|
||||||
### 🐞 修复
|
### 🐞 修复
|
||||||
@@ -210,7 +208,6 @@
|
|||||||
[](https://github.com/honghuangdc)
|
[](https://github.com/honghuangdc)
|
||||||
[dodu2014](mailto:dodu@live.cn)
|
[dodu2014](mailto:dodu@live.cn)
|
||||||
|
|
||||||
|
|
||||||
## [v1.2.7](https://github.com/honghuangdc/soybean-admin/compare/v1.2.6...v1.2.7) (2024-07-12)
|
## [v1.2.7](https://github.com/honghuangdc/soybean-admin/compare/v1.2.6...v1.2.7) (2024-07-12)
|
||||||
|
|
||||||
### 🛠 优化
|
### 🛠 优化
|
||||||
@@ -294,7 +291,6 @@
|
|||||||
[](https://github.com/soybeanjs) [](https://github.com/Azir-11)
|
[](https://github.com/soybeanjs) [](https://github.com/Azir-11)
|
||||||
[CHENZL](mailto:zlong5568863@qq.com)
|
[CHENZL](mailto:zlong5568863@qq.com)
|
||||||
|
|
||||||
|
|
||||||
## [v1.2.4](https://github.com/soybeanjs/soybean-admin/compare/v1.2.3...v1.2.4) (2024-06-14)
|
## [v1.2.4](https://github.com/soybeanjs/soybean-admin/compare/v1.2.3...v1.2.4) (2024-06-14)
|
||||||
|
|
||||||
### 🛠 优化
|
### 🛠 优化
|
||||||
@@ -372,7 +368,6 @@
|
|||||||
|
|
||||||
[](https://github.com/soybeanjs)
|
[](https://github.com/soybeanjs)
|
||||||
|
|
||||||
|
|
||||||
## [v1.2.0](https://github.com/soybeanjs/soybean-admin/compare/v1.1.5...v1.2.0) (2024-06-06)
|
## [v1.2.0](https://github.com/soybeanjs/soybean-admin/compare/v1.1.5...v1.2.0) (2024-06-06)
|
||||||
|
|
||||||
### 🚀 功能
|
### 🚀 功能
|
||||||
@@ -593,7 +588,6 @@
|
|||||||
[](https://github.com/honghuangdc)
|
[](https://github.com/honghuangdc)
|
||||||
[paynezhuang](mailto:paynezhuang@gmail.com)
|
[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)
|
## [v1.1.0-beta.1](https://github.com/soybeanjs/soybean-admin/compare/v1.0.9...v1.1.0-beta.1) (2024-05-07)
|
||||||
|
|
||||||
### 🚀 功能
|
### 🚀 功能
|
||||||
|
|||||||
@@ -23,14 +23,10 @@
|
|||||||
> [!NOTE]
|
> [!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
|
> 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
|
## 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.
|
[`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
|
## Features
|
||||||
|
|
||||||
- **Cutting-edge technology application**: using the latest popular technology stack such as Vue3, Vite7, TypeScript, Pinia and UnoCSS.
|
- **Cutting-edge technology application**: using the latest popular technology stack such as Vue3, Vite7, TypeScript, Pinia and UnoCSS.
|
||||||
@@ -45,7 +41,6 @@
|
|||||||
- **Command line tool**: built-in efficient command line tool, git commit, delete file, release, 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.
|
- **Mobile adaptation**: perfectly support mobile terminal to realize adaptive layout.
|
||||||
|
|
||||||
|
|
||||||
## Version
|
## Version
|
||||||
|
|
||||||
- **NaiveUI Version:**
|
- **NaiveUI Version:**
|
||||||
@@ -72,7 +67,6 @@
|
|||||||
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin/tree/legacy)
|
- [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin/tree/legacy)
|
||||||
- [Gitcode Repository](https://gitcode.com/soybeanjs/soybean-admin/tree/legacy)
|
- [Gitcode Repository](https://gitcode.com/soybeanjs/soybean-admin/tree/legacy)
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [Link](https://docs.soybeanjs.cn)
|
- [Link](https://docs.soybeanjs.cn)
|
||||||
@@ -92,7 +86,6 @@
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
**Environment Preparation**
|
**Environment Preparation**
|
||||||
@@ -119,6 +112,7 @@ git clone https://gitcode.com/soybeanjs/soybean-admin.git
|
|||||||
```bash
|
```bash
|
||||||
pnpm i
|
pnpm i
|
||||||
```
|
```
|
||||||
|
|
||||||
> Since this project uses the pnpm monorepo management method, please do not use npm or yarn to install dependencies.
|
> Since this project uses the pnpm monorepo management method, please do not use npm or yarn to install dependencies.
|
||||||
|
|
||||||
**Start Project**
|
**Start Project**
|
||||||
@@ -153,7 +147,6 @@ Refer to the [Code Synchronization](https://docs.soybeanjs.cn/guide/sync) docume
|
|||||||
|
|
||||||
More ecosystem please refer to [Ecosystem](https://docs.soybeanjs.cn/awesome) document.
|
More ecosystem please refer to [Ecosystem](https://docs.soybeanjs.cn/awesome) document.
|
||||||
|
|
||||||
|
|
||||||
## How to Contribute
|
## 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).
|
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).
|
||||||
@@ -167,8 +160,8 @@ This project has built-in `commit` command, you can execute `pnpm commit` to gen
|
|||||||
It is recommended to use the latest version of Chrome in development for a better experience.
|
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/) |
|
| [<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 |
|
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||||
|
|
||||||
## OpenSource Author
|
## OpenSource Author
|
||||||
|
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -23,9 +23,6 @@
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> `SoybeanAdmin` 快速上手系列视频已在 [Bilibili](https://www.bilibili.com/video/BV1YKdRYXELC) 上线 [点击这里](https://www.bilibili.com/video/BV1YKdRYXELC) 前往查看
|
> `SoybeanAdmin` 快速上手系列视频已在 [Bilibili](https://www.bilibili.com/video/BV1YKdRYXELC) 上线 [点击这里](https://www.bilibili.com/video/BV1YKdRYXELC) 前往查看
|
||||||
|
|
||||||
> [!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` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
|
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite7, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件,代码规范严谨,实现了自动化的文件路由系统。此外,它还采用了基于 ApiFox 的在线Mock数据方案。`SoybeanAdmin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
|
||||||
@@ -44,7 +41,6 @@
|
|||||||
- **命令行工具**:内置高效的命令行工具,git提交、删除文件、发布等。
|
- **命令行工具**:内置高效的命令行工具,git提交、删除文件、发布等。
|
||||||
- **移动端适配**:完美支持移动端,实现自适应布局。
|
- **移动端适配**:完美支持移动端,实现自适应布局。
|
||||||
|
|
||||||
|
|
||||||
## 版本
|
## 版本
|
||||||
|
|
||||||
- **NaiveUI 版本:**
|
- **NaiveUI 版本:**
|
||||||
@@ -68,13 +64,11 @@
|
|||||||
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin/tree/legacy)
|
- [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin/tree/legacy)
|
||||||
- [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin/tree/legacy)
|
- [Gitcode 仓库](https://gitcode.com/soybeanjs/soybean-admin/tree/legacy)
|
||||||
|
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
|
|
||||||
- [地址](https://docs.soybeanjs.cn)
|
- [地址](https://docs.soybeanjs.cn)
|
||||||
- [旧版文档](https://legacy-docs.soybeanjs.cn)
|
- [旧版文档](https://legacy-docs.soybeanjs.cn)
|
||||||
|
|
||||||
|
|
||||||
## 合作事项
|
## 合作事项
|
||||||
|
|
||||||
我们非常感谢大家对 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 的支持!为了进一步回馈社区,并助力企业和开发者实现个性化需求,我们现提供多种合作服务,期待与您携手共赢。
|
我们非常感谢大家对 [`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 的支持!为了进一步回馈社区,并助力企业和开发者实现个性化需求,我们现提供多种合作服务,期待与您携手共赢。
|
||||||
@@ -99,11 +93,10 @@
|
|||||||
|
|
||||||
- **Email**: [soybeanjs@outlook.com](mailto:soybeanjs@outlook.com)
|
- **Email**: [soybeanjs@outlook.com](mailto:soybeanjs@outlook.com)
|
||||||
- **GitHub Issues**: 欢迎通过 [GitHub Issues](https://github.com/soybeanjs/soybean-admin/issues/new) 联系我们,进行初步的合作洽谈。
|
- **GitHub Issues**: 欢迎通过 [GitHub Issues](https://github.com/soybeanjs/soybean-admin/issues/new) 联系我们,进行初步的合作洽谈。
|
||||||
- **商务合作微信**: honghuangdc
|
- **商务合作微信**: honghuangdc
|
||||||
|
|
||||||
期待与您开展深入合作,共同推动 SoybeanAdmin 项目及其在更多领域的成功应用!
|
期待与您开展深入合作,共同推动 SoybeanAdmin 项目及其在更多领域的成功应用!
|
||||||
|
|
||||||
|
|
||||||
## 示例图片
|
## 示例图片
|
||||||
|
|
||||||

|

|
||||||
@@ -118,7 +111,6 @@
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## 使用
|
## 使用
|
||||||
|
|
||||||
**环境准备**
|
**环境准备**
|
||||||
@@ -145,6 +137,7 @@ git clone https://gitcode.com/soybeanjs/soybean-admin.git
|
|||||||
```bash
|
```bash
|
||||||
pnpm i
|
pnpm i
|
||||||
```
|
```
|
||||||
|
|
||||||
> 由于本项目采用了 pnpm monorepo 的管理方式,因此请不要使用 npm 或 yarn 来安装依赖。
|
> 由于本项目采用了 pnpm monorepo 的管理方式,因此请不要使用 npm 或 yarn 来安装依赖。
|
||||||
|
|
||||||
**启动项目**
|
**启动项目**
|
||||||
@@ -179,29 +172,26 @@ pnpm build
|
|||||||
|
|
||||||
更多周边生态请翻阅 [周边生态](https://docs.soybeanjs.cn/zh/awesome) 文档。
|
更多周边生态请翻阅 [周边生态](https://docs.soybeanjs.cn/zh/awesome) 文档。
|
||||||
|
|
||||||
|
|
||||||
## 如何贡献
|
## 如何贡献
|
||||||
|
|
||||||
我们热烈欢迎并感谢所有形式的贡献。如果您有任何想法或建议,欢迎通过提交 [pull requests](https://github.com/soybeanjs/soybean-admin/pulls) 或创建 GitHub [issue](https://github.com/soybeanjs/soybean-admin/issues/new) 来分享。
|
我们热烈欢迎并感谢所有形式的贡献。如果您有任何想法或建议,欢迎通过提交 [pull requests](https://github.com/soybeanjs/soybean-admin/pulls) 或创建 GitHub [issue](https://github.com/soybeanjs/soybean-admin/issues/new) 来分享。
|
||||||
|
|
||||||
## Git 提交规范
|
## Git 提交规范
|
||||||
|
|
||||||
本项目已内置 `commit` 命令,您可以通过执行 `pnpm commit` 来生成符合 [Conventional Commits]([conventionalcommits](https://www.conventionalcommits.org/)) 规范的提交信息。在提交PR时,请务必使用 `commit` 命令来创建提交信息,以确保信息的规范性。
|
本项目已内置 `commit` 命令,您可以通过执行 `pnpm commit` 来生成符合 [Conventional Commits](<[conventionalcommits](https://www.conventionalcommits.org/)>) 规范的提交信息。在提交PR时,请务必使用 `commit` 命令来创建提交信息,以确保信息的规范性。
|
||||||
|
|
||||||
|
|
||||||
## 浏览器支持
|
## 浏览器支持
|
||||||
|
|
||||||
推荐使用最新版的 Chrome 浏览器进行开发,以获得更好的体验。
|
推荐使用最新版的 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/) |
|
| [<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 |
|
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||||
|
|
||||||
## 开源作者
|
## 开源作者
|
||||||
|
|
||||||
[Soybean](https://github.com/honghuangdc)
|
[Soybean](https://github.com/honghuangdc)
|
||||||
|
|
||||||
|
|
||||||
## 贡献者
|
## 贡献者
|
||||||
|
|
||||||
感谢以下贡献者的贡献。如果您想为本项目做出贡献,请参考 [如何贡献](#如何贡献)。
|
感谢以下贡献者的贡献。如果您想为本项目做出贡献,请参考 [如何贡献](#如何贡献)。
|
||||||
|
|||||||
@@ -8,6 +8,23 @@ export function setupElegantRouter() {
|
|||||||
base: 'src/layouts/base-layout/index.vue',
|
base: 'src/layouts/base-layout/index.vue',
|
||||||
blank: 'src/layouts/blank-layout/index.vue'
|
blank: 'src/layouts/blank-layout/index.vue'
|
||||||
},
|
},
|
||||||
|
customRoutes: {
|
||||||
|
names: [
|
||||||
|
'exception_403',
|
||||||
|
'exception_404',
|
||||||
|
'exception_500',
|
||||||
|
'document_project',
|
||||||
|
'document_project-link',
|
||||||
|
'document_video',
|
||||||
|
'document_vue',
|
||||||
|
'document_vite',
|
||||||
|
'document_unocss',
|
||||||
|
'document_naive',
|
||||||
|
'document_pro-naive',
|
||||||
|
'document_antd',
|
||||||
|
'document_alova'
|
||||||
|
]
|
||||||
|
},
|
||||||
routePathTransformer(routeName, routePath) {
|
routePathTransformer(routeName, routePath) {
|
||||||
const key = routeName as RouteKey;
|
const key = routeName as RouteKey;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import unocss from '@unocss/vite';
|
import { presetIcons } from 'unocss';
|
||||||
import presetIcons from '@unocss/preset-icons';
|
import unocss from 'unocss/vite';
|
||||||
import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders';
|
import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders';
|
||||||
|
|
||||||
export function setupUnocss(viteEnv: Env.ImportMeta) {
|
export function setupUnocss(viteEnv: Env.ImportMeta) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Icons from 'unplugin-icons/vite';
|
|||||||
import IconsResolver from 'unplugin-icons/resolver';
|
import IconsResolver from 'unplugin-icons/resolver';
|
||||||
import Components from 'unplugin-vue-components/vite';
|
import Components from 'unplugin-vue-components/vite';
|
||||||
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
|
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
|
||||||
|
import { ProNaiveUIResolver } from 'pro-naive-ui-resolver';
|
||||||
import { FileSystemIconLoader } from 'unplugin-icons/loaders';
|
import { FileSystemIconLoader } from 'unplugin-icons/loaders';
|
||||||
|
|
||||||
export function setupUnplugin(viteEnv: Env.ImportMeta) {
|
export function setupUnplugin(viteEnv: Env.ImportMeta) {
|
||||||
@@ -32,6 +33,7 @@ export function setupUnplugin(viteEnv: Env.ImportMeta) {
|
|||||||
types: [{ from: 'vue-router', names: ['RouterLink', 'RouterView'] }],
|
types: [{ from: 'vue-router', names: ['RouterLink', 'RouterView'] }],
|
||||||
resolvers: [
|
resolvers: [
|
||||||
NaiveUiResolver(),
|
NaiveUiResolver(),
|
||||||
|
ProNaiveUIResolver(),
|
||||||
IconsResolver({ customCollections: [collectionName], componentPrefix: VITE_ICON_PREFIX })
|
IconsResolver({ customCollections: [collectionName], componentPrefix: VITE_ICON_PREFIX })
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,24 +1,12 @@
|
|||||||
import { defineConfig } from '@soybeanjs/eslint-config';
|
import { defineConfig } from '@soybeanjs/eslint-config-vue';
|
||||||
|
|
||||||
export default defineConfig(
|
export default defineConfig({
|
||||||
{ vue: true, unocss: true },
|
'vue/component-name-in-template-casing': [
|
||||||
{
|
'warn',
|
||||||
rules: {
|
'PascalCase',
|
||||||
'vue/multi-word-component-names': [
|
{
|
||||||
'warn',
|
registeredComponentsOnly: false,
|
||||||
{
|
ignores: ['/^icon-/']
|
||||||
ignores: ['index', 'App', 'Register', '[id]', '[url]']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'vue/component-name-in-template-casing': [
|
|
||||||
'warn',
|
|
||||||
'PascalCase',
|
|
||||||
{
|
|
||||||
registeredComponentsOnly: false,
|
|
||||||
ignores: ['/^icon-/']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'unocss/order-attributify': 'off'
|
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
);
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh-cmn-Hans">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.svg" />
|
<link rel="icon" href="/favicon.svg" />
|
||||||
|
|||||||
125
package.json
125
package.json
@@ -1,35 +1,31 @@
|
|||||||
{
|
{
|
||||||
"name": "soybean-admin",
|
"name": "soybean-admin",
|
||||||
"type": "module",
|
"version": "2.1.0",
|
||||||
"version": "2.0.2",
|
|
||||||
"description": "A fresh and elegant admin template, based on Vue3、Vite7、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite7、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
|
"description": "A fresh and elegant admin template, based on Vue3、Vite7、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite7、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
|
||||||
|
"keywords": [
|
||||||
|
"TypeScript",
|
||||||
|
"UnoCSS",
|
||||||
|
"Vite7",
|
||||||
|
"Vue3 admin ",
|
||||||
|
"ant-design-vue v4",
|
||||||
|
"naive-ui",
|
||||||
|
"naive-ui-admin",
|
||||||
|
"vue-admin-template"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/soybeanjs/soybean-admin",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/soybeanjs/soybean-admin/issues"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Soybean",
|
"name": "Soybean",
|
||||||
"email": "soybeanjs@outlook.com",
|
"email": "soybeanjs@outlook.com",
|
||||||
"url": "https://github.com/soybeanjs"
|
"url": "https://github.com/soybeanjs"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
|
||||||
"homepage": "https://github.com/soybeanjs/soybean-admin",
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/soybeanjs/soybean-admin.git"
|
"url": "https://github.com/soybeanjs/soybean-admin.git"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"type": "module",
|
||||||
"url": "https://github.com/soybeanjs/soybean-admin/issues"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"Vue3 admin ",
|
|
||||||
"vue-admin-template",
|
|
||||||
"Vite7",
|
|
||||||
"TypeScript",
|
|
||||||
"naive-ui",
|
|
||||||
"naive-ui-admin",
|
|
||||||
"ant-design-vue v4",
|
|
||||||
"UnoCSS"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.19.0",
|
|
||||||
"pnpm": ">=10.5.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build --mode prod",
|
"build": "vite build --mode prod",
|
||||||
"build:test": "vite build --mode test",
|
"build:test": "vite build --mode test",
|
||||||
@@ -38,8 +34,9 @@
|
|||||||
"commit:zh": "sa git-commit -l=zh-cn",
|
"commit:zh": "sa git-commit -l=zh-cn",
|
||||||
"dev": "vite --mode test",
|
"dev": "vite --mode test",
|
||||||
"dev:prod": "vite --mode prod",
|
"dev:prod": "vite --mode prod",
|
||||||
|
"fmt": "oxfmt",
|
||||||
"gen-route": "sa gen-route",
|
"gen-route": "sa gen-route",
|
||||||
"lint": "eslint . --fix",
|
"lint": "oxlint --fix && eslint --fix .",
|
||||||
"prepare": "simple-git-hooks",
|
"prepare": "simple-git-hooks",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"release": "sa release",
|
"release": "sa release",
|
||||||
@@ -47,65 +44,91 @@
|
|||||||
"update-pkg": "sa update-pkg"
|
"update-pkg": "sa update-pkg"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@antv/data-set": "0.11.8",
|
||||||
|
"@antv/g2": "5.4.8",
|
||||||
|
"@antv/g6": "5.0.51",
|
||||||
"@better-scroll/core": "2.5.1",
|
"@better-scroll/core": "2.5.1",
|
||||||
"@iconify/vue": "5.0.0",
|
"@iconify/vue": "5.0.0",
|
||||||
|
"@sa/alova": "workspace:*",
|
||||||
"@sa/axios": "workspace:*",
|
"@sa/axios": "workspace:*",
|
||||||
"@sa/color": "workspace:*",
|
"@sa/color": "workspace:*",
|
||||||
"@sa/hooks": "workspace:*",
|
"@sa/hooks": "workspace:*",
|
||||||
"@sa/materials": "workspace:*",
|
"@sa/materials": "workspace:*",
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"@vueuse/core": "14.1.0",
|
"@visactor/vchart": "2.0.19",
|
||||||
|
"@visactor/vchart-theme": "1.12.3",
|
||||||
|
"@visactor/vtable-editors": "1.23.3",
|
||||||
|
"@visactor/vtable-gantt": "1.23.3",
|
||||||
|
"@visactor/vue-vtable": "1.23.3",
|
||||||
|
"@vueuse/components": "14.2.1",
|
||||||
|
"@vueuse/core": "14.2.1",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
"dayjs": "1.11.19",
|
"dayjs": "1.11.19",
|
||||||
"defu": "6.1.4",
|
"defu": "6.1.4",
|
||||||
|
"dhtmlx-gantt": "9.1.2",
|
||||||
|
"dompurify": "3.3.2",
|
||||||
"echarts": "6.0.0",
|
"echarts": "6.0.0",
|
||||||
|
"jsbarcode": "3.12.3",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"naive-ui": "2.43.2",
|
"naive-ui": "2.44.1",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"pinia": "3.0.4",
|
"pinia": "3.0.4",
|
||||||
"tailwind-merge": "3.4.0",
|
"pinyin-pro": "3.28.0",
|
||||||
"vue": "3.5.26",
|
"print-js": "1.6.0",
|
||||||
"vue-draggable-plus": "0.6.0",
|
"pro-naive-ui": "3.2.2",
|
||||||
"vue-i18n": "11.2.7",
|
"swiper": "12.1.2",
|
||||||
"vue-router": "4.6.4"
|
"tailwind-merge": "3.5.0",
|
||||||
|
"typeit": "8.8.7",
|
||||||
|
"vditor": "3.11.2",
|
||||||
|
"vue": "3.5.29",
|
||||||
|
"vue-draggable-plus": "0.6.1",
|
||||||
|
"vue-i18n": "11.3.0",
|
||||||
|
"vue-pdf-embed": "2.1.4",
|
||||||
|
"vue-router": "5.0.3",
|
||||||
|
"wangeditor": "4.7.15",
|
||||||
|
"xgplayer": "3.0.23",
|
||||||
|
"xlsx": "0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@amap/amap-jsapi-types": "0.0.15",
|
||||||
"@elegant-router/vue": "0.3.8",
|
"@elegant-router/vue": "0.3.8",
|
||||||
"@iconify/json": "2.2.417",
|
"@iconify/json": "2.2.447",
|
||||||
"@sa/scripts": "workspace:*",
|
"@sa/scripts": "workspace:*",
|
||||||
"@sa/uno-preset": "workspace:*",
|
"@sa/uno-preset": "workspace:*",
|
||||||
"@soybeanjs/eslint-config": "1.7.5",
|
"@soybeanjs/eslint-config-vue": "^0.0.2",
|
||||||
"@types/node": "25.0.3",
|
"@types/bmapgl": "0.0.7",
|
||||||
|
"@types/node": "25.3.5",
|
||||||
"@types/nprogress": "0.2.3",
|
"@types/nprogress": "0.2.3",
|
||||||
"@unocss/eslint-config": "66.5.10",
|
"@vitejs/plugin-vue": "6.0.4",
|
||||||
"@unocss/preset-icons": "66.5.10",
|
"@vitejs/plugin-vue-jsx": "5.1.4",
|
||||||
"@unocss/preset-uno": "66.5.10",
|
|
||||||
"@unocss/transformer-directives": "66.5.10",
|
|
||||||
"@unocss/transformer-variant-group": "66.5.10",
|
|
||||||
"@unocss/vite": "66.5.10",
|
|
||||||
"@vitejs/plugin-vue": "6.0.3",
|
|
||||||
"@vitejs/plugin-vue-jsx": "5.1.3",
|
|
||||||
"consola": "3.4.2",
|
"consola": "3.4.2",
|
||||||
"eslint": "9.39.2",
|
"eslint": "10.0.3",
|
||||||
"eslint-plugin-vue": "10.6.2",
|
|
||||||
"kolorist": "1.8.0",
|
"kolorist": "1.8.0",
|
||||||
"sass": "1.97.1",
|
"oxfmt": "^0.36.0",
|
||||||
|
"oxlint": "^1.51.0",
|
||||||
|
"pro-naive-ui-resolver": "1.0.2",
|
||||||
|
"sass": "1.97.3",
|
||||||
"simple-git-hooks": "2.13.1",
|
"simple-git-hooks": "2.13.1",
|
||||||
"tsx": "4.21.0",
|
"tsx": "4.21.0",
|
||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
"unplugin-icons": "22.5.0",
|
"unocss": "^66.6.6",
|
||||||
"unplugin-vue-components": "30.0.0",
|
"unplugin-icons": "23.0.1",
|
||||||
"vite": "7.3.0",
|
"unplugin-vue-components": "31.0.0",
|
||||||
|
"vite": "7.3.1",
|
||||||
"vite-plugin-progress": "0.0.7",
|
"vite-plugin-progress": "0.0.7",
|
||||||
"vite-plugin-svg-icons": "2.0.1",
|
"vite-plugin-svg-icons": "2.0.1",
|
||||||
"vite-plugin-vue-devtools": "8.0.5",
|
"vite-plugin-vue-devtools": "8.0.7",
|
||||||
"vite-plugin-vue-transition-root-validator": "^0.0.4",
|
"vite-plugin-vue-transition-root-validator": "^0.1.0",
|
||||||
"vue-eslint-parser": "10.2.0",
|
"vue-eslint-parser": "10.4.0",
|
||||||
"vue-tsc": "3.2.1"
|
"vue-tsc": "3.2.5"
|
||||||
},
|
},
|
||||||
"simple-git-hooks": {
|
"simple-git-hooks": {
|
||||||
"commit-msg": "pnpm sa git-commit-verify",
|
"commit-msg": "pnpm sa git-commit-verify",
|
||||||
"pre-commit": "pnpm typecheck && pnpm lint && git diff --exit-code"
|
"pre-commit": "pnpm typecheck && pnpm lint && pnpm fmt && git diff --exit-code"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0",
|
||||||
|
"pnpm": ">=10.5.0"
|
||||||
},
|
},
|
||||||
"website": "https://admin.soybeanjs.cn"
|
"website": "https://admin.soybeanjs.cn"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/alova",
|
"name": "@sa/alova",
|
||||||
"version": "2.0.2",
|
"version": "2.1.0",
|
||||||
|
"typesVersions": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
"./fetch": "./src/fetch.ts",
|
"./fetch": "./src/fetch.ts",
|
||||||
"./client": "./src/client.ts",
|
"./client": "./src/client.ts",
|
||||||
"./mock": "./src/mock.ts"
|
"./mock": "./src/mock.ts"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
|
||||||
"*": {
|
|
||||||
"*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alova/mock": "2.0.18",
|
"@alova/mock": "2.0.19",
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"alova": "3.4.1"
|
"alova": "3.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/axios",
|
"name": "@sa/axios",
|
||||||
"version": "2.0.2",
|
"version": "2.1.0",
|
||||||
|
"typesVersions": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
|
||||||
"*": {
|
|
||||||
"*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"axios": "1.13.2",
|
"axios": "1.13.6",
|
||||||
"axios-retry": "4.5.0",
|
"axios-retry": "4.5.0",
|
||||||
"qs": "6.14.0"
|
"qs": "6.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/qs": "6.14.0"
|
"@types/qs": "6.15.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/color",
|
"name": "@sa/color",
|
||||||
"version": "2.0.2",
|
"version": "2.1.0",
|
||||||
"exports": {
|
|
||||||
".": "./src/index.ts"
|
|
||||||
},
|
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
"*": {
|
"*": {
|
||||||
"*": ["./src/*"]
|
"*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"colord": "2.9.3"
|
"colord": "2.9.3"
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/hooks",
|
"name": "@sa/hooks",
|
||||||
"version": "2.0.2",
|
"version": "2.1.0",
|
||||||
"exports": {
|
|
||||||
".": "./src/index.ts"
|
|
||||||
},
|
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
"*": {
|
"*": {
|
||||||
"*": ["./src/*"]
|
"*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sa/axios": "workspace:*",
|
"@sa/axios": "workspace:*",
|
||||||
"@sa/utils": "workspace:*"
|
"@sa/utils": "workspace:*"
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/materials",
|
"name": "@sa/materials",
|
||||||
"version": "2.0.2",
|
"version": "2.1.0",
|
||||||
"exports": {
|
|
||||||
".": "./src/index.ts"
|
|
||||||
},
|
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
"*": {
|
"*": {
|
||||||
"*": ["./src/*"]
|
"*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"simplebar-vue": "2.4.2"
|
"simplebar-vue": "2.4.2"
|
||||||
|
|||||||
@@ -1,28 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/scripts",
|
"name": "@sa/scripts",
|
||||||
"version": "2.0.2",
|
"version": "2.1.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"sa": "./bin.ts"
|
"sa": "./bin.ts"
|
||||||
},
|
},
|
||||||
|
"typesVersions": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
|
||||||
"*": {
|
|
||||||
"*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@soybeanjs/changelog": "0.4.3",
|
"@soybeanjs/changelog": "0.4.3",
|
||||||
"bumpp": "10.3.2",
|
"bumpp": "10.4.1",
|
||||||
"c12": "3.3.3",
|
"c12": "3.3.3",
|
||||||
"cac": "6.7.14",
|
"cac": "6.7.14",
|
||||||
"consola": "3.4.2",
|
"consola": "3.4.2",
|
||||||
"enquirer": "2.4.1",
|
"enquirer": "2.4.1",
|
||||||
"execa": "9.6.1",
|
"execa": "9.6.1",
|
||||||
"kolorist": "1.8.0",
|
"kolorist": "1.8.0",
|
||||||
"npm-check-updates": "19.2.0",
|
"npm-check-updates": "19.6.3",
|
||||||
"picomatch": "4.0.3",
|
"picomatch": "4.0.3",
|
||||||
"rimraf": "6.1.2"
|
"rimraf": "6.1.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import cac from 'cac';
|
import { cac } from 'cac';
|
||||||
import { blue, lightGreen } from 'kolorist';
|
import { blue, lightGreen } from 'kolorist';
|
||||||
import { version } from '../package.json';
|
import { version } from '../package.json';
|
||||||
import { cleanup, genChangelog, generateRoute, gitCommit, gitCommitVerify, release, updatePkg } from './commands';
|
import { cleanup, genChangelog, generateRoute, gitCommit, gitCommitVerify, release, updatePkg } from './commands';
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/uno-preset",
|
"name": "@sa/uno-preset",
|
||||||
"version": "2.0.2",
|
"version": "2.1.0",
|
||||||
"exports": {
|
|
||||||
".": "./src/index.ts"
|
|
||||||
},
|
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
"*": {
|
"*": {
|
||||||
"*": ["./src/*"]
|
"*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// @unocss-include
|
// @unocss-include
|
||||||
|
|
||||||
import type { Preset } from '@unocss/core';
|
import type { Preset } from '@unocss/core';
|
||||||
import type { Theme } from '@unocss/preset-uno';
|
import type { Theme } from '@unocss/preset-mini';
|
||||||
|
|
||||||
export function presetSoybeanAdmin(): Preset<Theme> {
|
export function presetSoybeanAdmin(): Preset<Theme> {
|
||||||
const preset: Preset<Theme> = {
|
const preset: Preset<Theme> = {
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@sa/utils",
|
"name": "@sa/utils",
|
||||||
"version": "2.0.2",
|
"version": "2.1.0",
|
||||||
"exports": {
|
|
||||||
".": "./src/index.ts"
|
|
||||||
},
|
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
"*": {
|
"*": {
|
||||||
"*": ["./src/*"]
|
"*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colord": "2.9.3",
|
"colord": "2.9.3",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
|
|||||||
6384
pnpm-lock.yaml
generated
6384
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,2 @@
|
|||||||
packages:
|
packages:
|
||||||
- "packages/*"
|
- 'packages/*'
|
||||||
|
|||||||
1
src/assets/svg-icon/alova.svg
Normal file
1
src/assets/svg-icon/alova.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.7 KiB |
3
src/assets/svg-icon/visactor.svg
Normal file
3
src/assets/svg-icon/visactor.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 10C149.706 10 190 50.2944 190 100C190 108.029 188.949 115.812 186.976 123.219L136.254 39.4666H136.195C131.638 32.531 123.789 27.9518 114.87 27.9518C105.952 27.9518 98.1024 32.531 93.5456 39.4666H93.4853L92.8532 40.5821C92.731 40.7907 92.6117 41.0012 92.4953 41.2136L36.9057 139.312C34.7053 143.082 33.4445 147.467 33.4445 152.147C33.4445 156.134 34.3599 159.908 35.9919 163.269C19.9218 147.012 10 124.665 10 100C10 50.2944 50.2944 10 100 10ZM53.4722 177.056C55.2341 177.441 57.0641 177.644 58.9415 177.644C73.0231 177.644 84.4384 166.228 84.4384 152.147C84.4384 147.63 83.2642 143.388 81.2043 139.709L81.2172 139.704L68.7932 118.202C62.0989 106.608 66.0716 91.7816 77.6663 85.0874C89.261 78.3932 104.087 82.3658 110.781 93.9605L154.607 168.236L154.625 168.229C155.082 168.971 155.575 169.689 156.103 170.378C140.717 182.659 121.216 190 100 190C82.9725 190 67.0494 185.271 53.4722 177.056Z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts" generic="T extends Record<string, unknown>, K = never">
|
<script setup lang="ts" generic="T extends Record<string, unknown>, K = never">
|
||||||
|
import { computed } from 'vue';
|
||||||
import { VueDraggable } from 'vue-draggable-plus';
|
import { VueDraggable } from 'vue-draggable-plus';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
@@ -22,6 +23,40 @@ function handleFixed(column: NaiveUI.TableColumnCheck) {
|
|||||||
const nextIndex = index === fixedOptions.length - 1 ? 0 : index + 1;
|
const nextIndex = index === fixedOptions.length - 1 ? 0 : index + 1;
|
||||||
column.fixed = fixedOptions[nextIndex];
|
column.fixed = fixedOptions[nextIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const visibleStats = computed(() => {
|
||||||
|
let total = 0;
|
||||||
|
let checked = 0;
|
||||||
|
|
||||||
|
columns.value.forEach(column => {
|
||||||
|
if (!column.visible) return;
|
||||||
|
|
||||||
|
total += 1;
|
||||||
|
if (column.checked) checked += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { total, checked };
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectAllChecked = computed(() => {
|
||||||
|
const { total, checked } = visibleStats.value;
|
||||||
|
|
||||||
|
return total > 0 && checked === total;
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectAllIndeterminate = computed(() => {
|
||||||
|
const { total, checked } = visibleStats.value;
|
||||||
|
|
||||||
|
return checked > 0 && checked < total;
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleSelectAll(checked: boolean) {
|
||||||
|
columns.value.forEach(column => {
|
||||||
|
if (!column.visible) return;
|
||||||
|
|
||||||
|
column.checked = checked;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -34,35 +69,48 @@ function handleFixed(column: NaiveUI.TableColumnCheck) {
|
|||||||
{{ $t('common.columnSetting') }}
|
{{ $t('common.columnSetting') }}
|
||||||
</NButton>
|
</NButton>
|
||||||
</template>
|
</template>
|
||||||
<VueDraggable v-model="columns" :animation="150" filter=".none_draggable">
|
<div>
|
||||||
<div
|
<div class="h-36px flex-y-center rd-4px pl-26px hover:(bg-primary bg-opacity-20)">
|
||||||
v-for="item in columns"
|
<NCheckbox
|
||||||
:key="item.key"
|
:checked="selectAllChecked"
|
||||||
class="h-36px flex-y-center justify-between gap-6px"
|
:indeterminate="selectAllIndeterminate"
|
||||||
:class="{ hidden: !item.visible }"
|
:disabled="visibleStats.total === 0"
|
||||||
>
|
class="flex-1"
|
||||||
<div class="flex-y-center rd-4px hover:(bg-primary bg-opacity-20)">
|
@update:checked="toggleSelectAll"
|
||||||
<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>
|
|
||||||
<ButtonIcon
|
|
||||||
:disabled="!item.checked"
|
|
||||||
text
|
|
||||||
:focusable="false"
|
|
||||||
:tooltip-content="$t(tooltipRecord[item.fixed!])"
|
|
||||||
@click="handleFixed(item)"
|
|
||||||
>
|
>
|
||||||
<icon-octicon-pin-16 v-if="item.fixed === 'unFixed'" />
|
{{ $t('common.selectAll') }}
|
||||||
<icon-octicon-pin-16 v-else-if="item.fixed === 'left'" class="rotate-270" />
|
</NCheckbox>
|
||||||
<icon-octicon-pin-slash-16 v-else />
|
|
||||||
</ButtonIcon>
|
|
||||||
</div>
|
</div>
|
||||||
</VueDraggable>
|
<NDivider class="!my-4px" />
|
||||||
|
<VueDraggable v-model="columns" :animation="150" filter=".none_draggable" class="max-h-[200px] overflow-y-auto">
|
||||||
|
<div
|
||||||
|
v-for="item in columns"
|
||||||
|
:key="item.key"
|
||||||
|
class="h-36px flex-y-center justify-between gap-6px"
|
||||||
|
:class="{ hidden: !item.visible }"
|
||||||
|
>
|
||||||
|
<div class="h-full flex-y-center flex-1 rd-4px hover:(bg-primary bg-opacity-20)">
|
||||||
|
<icon-mdi-drag class="mr-8px h-full cursor-move text-icon" />
|
||||||
|
<NCheckbox v-model:checked="item.checked" class="none_draggable flex-1">
|
||||||
|
<template v-if="typeof item.title === 'function'">
|
||||||
|
<component :is="item.title" />
|
||||||
|
</template>
|
||||||
|
<template v-else>{{ item.title }}</template>
|
||||||
|
</NCheckbox>
|
||||||
|
</div>
|
||||||
|
<ButtonIcon
|
||||||
|
:disabled="!item.checked"
|
||||||
|
:focusable="false"
|
||||||
|
:tooltip-content="$t(tooltipRecord[item.fixed!])"
|
||||||
|
@click="handleFixed(item)"
|
||||||
|
>
|
||||||
|
<icon-octicon-pin-16 v-if="item.fixed === 'unFixed'" />
|
||||||
|
<icon-octicon-pin-16 v-else-if="item.fixed === 'left'" class="rotate-270" />
|
||||||
|
<icon-octicon-pin-slash-16 v-else />
|
||||||
|
</ButtonIcon>
|
||||||
|
</div>
|
||||||
|
</VueDraggable>
|
||||||
|
</div>
|
||||||
</NPopover>
|
</NPopover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
78
src/components/custom/custom-icon-select.vue
Normal file
78
src/components/custom/custom-icon-select.vue
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'CustomIconSelect' });
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** Selected icon */
|
||||||
|
value: string;
|
||||||
|
/** List of icons */
|
||||||
|
icons: string[];
|
||||||
|
/** Icon for when nothing is selected */
|
||||||
|
emptyIcon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
emptyIcon: 'mdi:apps'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:value', val: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const modelValue = computed({
|
||||||
|
get() {
|
||||||
|
return props.value;
|
||||||
|
},
|
||||||
|
set(val: string) {
|
||||||
|
emit('update:value', val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedIcon = computed(() => modelValue.value || props.emptyIcon);
|
||||||
|
|
||||||
|
const searchValue = ref('');
|
||||||
|
|
||||||
|
const iconsList = computed(() => props.icons.filter(v => v.includes(searchValue.value)));
|
||||||
|
|
||||||
|
function handleChange(iconItem: string) {
|
||||||
|
modelValue.value = iconItem;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NPopover placement="bottom-end" trigger="click">
|
||||||
|
<template #trigger>
|
||||||
|
<NInput v-model:value="modelValue" readonly placeholder="点击选择图标">
|
||||||
|
<template #suffix>
|
||||||
|
<SvgIcon :icon="selectedIcon" class="p-5px text-30px" />
|
||||||
|
</template>
|
||||||
|
</NInput>
|
||||||
|
</template>
|
||||||
|
<template #header>
|
||||||
|
<NInput v-model:value="searchValue" placeholder="搜索图标"></NInput>
|
||||||
|
</template>
|
||||||
|
<div v-if="iconsList.length > 0" class="grid grid-cols-9 h-auto overflow-auto">
|
||||||
|
<span v-for="iconItem in iconsList" :key="iconItem" @click="handleChange(iconItem)">
|
||||||
|
<SvgIcon
|
||||||
|
:icon="iconItem"
|
||||||
|
class="m-2px cursor-pointer border-1px border-#d9d9d9 p-5px text-30px"
|
||||||
|
:class="{ 'border-primary': modelValue === iconItem }"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<NEmpty v-else class="w-306px" description="你什么也找不到" />
|
||||||
|
</NPopover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.n-input-wrapper) {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-input__suffix) {
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
18
src/components/custom/github-link.vue
Normal file
18
src/components/custom/github-link.vue
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import WebSiteLink from './web-site-link.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'GithubLink' });
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** github link */
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<WebSiteLink label="github地址:" :link="link" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
23
src/components/custom/web-site-link.vue
Normal file
23
src/components/custom/web-site-link.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineOptions({ name: 'WebSiteLink' });
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** Web site name */
|
||||||
|
label: string;
|
||||||
|
/** Web site link */
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p>
|
||||||
|
<span>{{ label }}</span>
|
||||||
|
<a class="text-blue-500" :href="link" target="#">
|
||||||
|
{{ link }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
29
src/constants/business.ts
Normal file
29
src/constants/business.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
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);
|
||||||
8
src/constants/map-sdk.ts
Normal file
8
src/constants/map-sdk.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/** baidu map sdk url */
|
||||||
|
export const BAIDU_MAP_SDK_URL = `https://api.map.baidu.com/getscript?v=3.0&ak=KSezYymXPth1DIGILRX3oYN9PxbOQQmU&services=&t=20210201100830&s=1`;
|
||||||
|
|
||||||
|
/** Amap sdk url */
|
||||||
|
export const AMAP_SDK_URL = 'https://webapi.amap.com/maps?v=2.0&key=e7bd02bd504062087e6563daf4d6721d';
|
||||||
|
|
||||||
|
/** tencent sdk url */
|
||||||
|
export const TENCENT_MAP_SDK_URL = 'https://map.qq.com/api/gljs?v=1.exp&key=A6DBZ-KXPLW-JKSRY-ONZF4-CPHY3-K6BL7';
|
||||||
@@ -39,11 +39,7 @@ export function useNaiveTable<ResponseData, ApiData>(options: UseNaiveTableOptio
|
|||||||
});
|
});
|
||||||
|
|
||||||
// calculate the total width of the table this is used for horizontal scrolling
|
// calculate the total width of the table this is used for horizontal scrolling
|
||||||
const scrollX = computed(() => {
|
const scrollX = computed(() => getScrollX(result.columns.value));
|
||||||
return result.columns.value.reduce((acc, column) => {
|
|
||||||
return acc + Number(column.width ?? column.minWidth ?? 120);
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
scope.run(() => {
|
scope.run(() => {
|
||||||
watch(
|
watch(
|
||||||
@@ -135,6 +131,8 @@ export function useNaivePaginatedTable<ResponseData, ApiData>(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const scrollX = computed(() => getScrollX(result.columns.value));
|
||||||
|
|
||||||
async function getDataByPage(page: number = 1) {
|
async function getDataByPage(page: number = 1) {
|
||||||
if (page !== pagination.page) {
|
if (page !== pagination.page) {
|
||||||
pagination.page = page;
|
pagination.page = page;
|
||||||
@@ -166,6 +164,7 @@ export function useNaivePaginatedTable<ResponseData, ApiData>(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
|
scrollX,
|
||||||
getDataByPage,
|
getDataByPage,
|
||||||
pagination,
|
pagination,
|
||||||
mobilePagination
|
mobilePagination
|
||||||
@@ -319,3 +318,9 @@ function getColumns<Column extends NaiveUI.TableColumn<any>>(cols: Column[], che
|
|||||||
export function isTableColumnHasKey<T>(column: NaiveUI.TableColumn<T>): column is NaiveUI.TableColumnWithKey<T> {
|
export function isTableColumnHasKey<T>(column: NaiveUI.TableColumn<T>): column is NaiveUI.TableColumnWithKey<T> {
|
||||||
return Boolean((column as NaiveUI.TableColumnWithKey<T>).key);
|
return Boolean((column as NaiveUI.TableColumnWithKey<T>).key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getScrollX<T>(columns: NaiveUI.TableColumn<T>[], minWidth: number = 120) {
|
||||||
|
return columns.reduce((acc, column) => {
|
||||||
|
return acc + Number(column.width ?? column.minWidth ?? minWidth);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|||||||
158
src/hooks/common/vchart.ts
Normal file
158
src/hooks/common/vchart.ts
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import { computed, effectScope, onScopeDispose, ref, watch } from 'vue';
|
||||||
|
import { useElementSize } from '@vueuse/core';
|
||||||
|
import VChart, { registerLiquidChart } from '@visactor/vchart';
|
||||||
|
import type { ISpec, ITheme } from '@visactor/vchart';
|
||||||
|
import light from '@visactor/vchart-theme/public/light.json';
|
||||||
|
import dark from '@visactor/vchart-theme/public/dark.json';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
|
||||||
|
registerLiquidChart();
|
||||||
|
|
||||||
|
// register the theme
|
||||||
|
VChart.ThemeManager.registerTheme('light', light as ITheme);
|
||||||
|
VChart.ThemeManager.registerTheme('dark', dark as ITheme);
|
||||||
|
|
||||||
|
interface ChartHooks {
|
||||||
|
onRender?: (chart: VChart) => void | Promise<void>;
|
||||||
|
onUpdated?: (chart: VChart) => void | Promise<void>;
|
||||||
|
onDestroy?: (chart: VChart) => void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useVChart<T extends ISpec>(specFactory: () => T, hooks: ChartHooks = {}) {
|
||||||
|
const scope = effectScope();
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
const darkMode = computed(() => themeStore.darkMode);
|
||||||
|
|
||||||
|
const domRef = ref<HTMLElement | null>(null);
|
||||||
|
const initialSize = { width: 0, height: 0 };
|
||||||
|
const { width, height } = useElementSize(domRef, initialSize);
|
||||||
|
|
||||||
|
let chart: VChart | null = null;
|
||||||
|
const spec: T = specFactory();
|
||||||
|
|
||||||
|
const { onRender, onUpdated, onDestroy } = hooks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* whether can render chart
|
||||||
|
*
|
||||||
|
* when domRef is ready and initialSize is valid
|
||||||
|
*/
|
||||||
|
function canRender() {
|
||||||
|
return domRef.value && initialSize.width > 0 && initialSize.height > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** is chart rendered */
|
||||||
|
function isRendered() {
|
||||||
|
return Boolean(domRef.value && chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update chart spec
|
||||||
|
*
|
||||||
|
* @param callback callback function
|
||||||
|
*/
|
||||||
|
async function updateSpec(callback: (opts: T, optsFactory: () => T) => ISpec = () => spec) {
|
||||||
|
if (!isRendered()) return;
|
||||||
|
|
||||||
|
const updatedOpts = callback(spec, specFactory);
|
||||||
|
|
||||||
|
Object.assign(spec, updatedOpts);
|
||||||
|
|
||||||
|
if (isRendered()) {
|
||||||
|
chart?.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
chart?.updateSpec({ ...updatedOpts }, true);
|
||||||
|
|
||||||
|
await onUpdated?.(chart!);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSpec(newSpec: T) {
|
||||||
|
chart?.updateSpec(newSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** render chart */
|
||||||
|
async function render() {
|
||||||
|
if (!isRendered()) {
|
||||||
|
// apply the theme
|
||||||
|
if (darkMode.value) {
|
||||||
|
VChart.ThemeManager.setCurrentTheme('dark');
|
||||||
|
} else {
|
||||||
|
VChart.ThemeManager.setCurrentTheme('light');
|
||||||
|
}
|
||||||
|
|
||||||
|
chart = new VChart(spec, { dom: domRef.value as HTMLElement });
|
||||||
|
chart.renderSync();
|
||||||
|
|
||||||
|
await onRender?.(chart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** resize chart */
|
||||||
|
function resize() {
|
||||||
|
// chart?.resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** destroy chart */
|
||||||
|
async function destroy() {
|
||||||
|
if (!chart) return;
|
||||||
|
|
||||||
|
await onDestroy?.(chart);
|
||||||
|
chart?.release();
|
||||||
|
chart = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** change chart theme */
|
||||||
|
async function changeTheme() {
|
||||||
|
await destroy();
|
||||||
|
await render();
|
||||||
|
await onUpdated?.(chart!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* render chart by size
|
||||||
|
*
|
||||||
|
* @param w width
|
||||||
|
* @param h height
|
||||||
|
*/
|
||||||
|
async function renderChartBySize(w: number, h: number) {
|
||||||
|
initialSize.width = w;
|
||||||
|
initialSize.height = h;
|
||||||
|
|
||||||
|
// size is abnormal, destroy chart
|
||||||
|
if (!canRender()) {
|
||||||
|
await destroy();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize chart
|
||||||
|
if (isRendered()) {
|
||||||
|
resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// render chart
|
||||||
|
await render();
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.run(() => {
|
||||||
|
watch([width, height], ([newWidth, newHeight]) => {
|
||||||
|
renderChartBySize(newWidth, newHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(darkMode, () => {
|
||||||
|
changeTheme();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onScopeDispose(() => {
|
||||||
|
destroy();
|
||||||
|
scope.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
domRef,
|
||||||
|
updateSpec,
|
||||||
|
setSpec
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ function loginOrRegister() {
|
|||||||
toLogin();
|
toLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
type DropdownKey = 'logout';
|
type DropdownKey = 'user-center' | 'logout';
|
||||||
|
|
||||||
type DropdownOption =
|
type DropdownOption =
|
||||||
| {
|
| {
|
||||||
@@ -33,6 +33,15 @@ type DropdownOption =
|
|||||||
|
|
||||||
const options = computed(() => {
|
const options = computed(() => {
|
||||||
const opts: DropdownOption[] = [
|
const opts: DropdownOption[] = [
|
||||||
|
{
|
||||||
|
label: $t('common.userCenter'),
|
||||||
|
key: 'user-center',
|
||||||
|
icon: SvgIconVNode({ icon: 'ph:user-circle', fontSize: 18 })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'divider',
|
||||||
|
key: 'divider'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: $t('common.logout'),
|
label: $t('common.logout'),
|
||||||
key: 'logout',
|
key: 'logout',
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ export const $t = i18n.global.t as App.I18n.$T;
|
|||||||
|
|
||||||
export function setLocale(locale: App.I18n.LangType) {
|
export function setLocale(locale: App.I18n.LangType) {
|
||||||
i18n.global.locale.value = locale;
|
i18n.global.locale.value = locale;
|
||||||
|
|
||||||
|
document?.querySelector('html')?.setAttribute('lang', locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocale(): App.I18n.LangType {
|
export function getLocale(): App.I18n.LangType {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const local: App.I18n.Schema = {
|
|||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
close: 'Close',
|
close: 'Close',
|
||||||
check: 'Check',
|
check: 'Check',
|
||||||
|
selectAll: 'Select All',
|
||||||
expandColumn: 'Expand Column',
|
expandColumn: 'Expand Column',
|
||||||
columnSetting: 'Column Setting',
|
columnSetting: 'Column Setting',
|
||||||
config: 'Config',
|
config: 'Config',
|
||||||
@@ -228,7 +229,80 @@ const local: App.I18n.Schema = {
|
|||||||
404: 'Page Not Found',
|
404: 'Page Not Found',
|
||||||
500: 'Server Error',
|
500: 'Server Error',
|
||||||
'iframe-page': 'Iframe',
|
'iframe-page': 'Iframe',
|
||||||
home: 'Home'
|
home: 'Home',
|
||||||
|
document: 'Document',
|
||||||
|
document_project: 'Project Document',
|
||||||
|
'document_project-link': 'Project Document(External Link)',
|
||||||
|
document_video: 'Video Tutorial',
|
||||||
|
document_vue: 'Vue Document',
|
||||||
|
document_vite: 'Vite Document',
|
||||||
|
document_unocss: 'UnoCSS Document',
|
||||||
|
document_naive: 'Naive UI Document',
|
||||||
|
'document_pro-naive': 'Pro Naive UI Document',
|
||||||
|
document_antd: 'Ant Design Vue Document',
|
||||||
|
document_alova: 'Alova Document',
|
||||||
|
'user-center': 'User Center',
|
||||||
|
about: 'About',
|
||||||
|
function: 'System Function',
|
||||||
|
alova: 'Alova Example',
|
||||||
|
alova_request: 'Alova Request',
|
||||||
|
alova_scenes: 'Scenario Request',
|
||||||
|
'pro-naive': 'Pro Naive Example',
|
||||||
|
'pro-naive_form': 'Form',
|
||||||
|
'pro-naive_form_basic': 'Basic Form',
|
||||||
|
'pro-naive_form_query': 'Query Form',
|
||||||
|
'pro-naive_form_step': 'Step Form',
|
||||||
|
'pro-naive_table': 'Table',
|
||||||
|
'pro-naive_table_remote': 'Remote',
|
||||||
|
'pro-naive_table_row-edit': 'Row Edit',
|
||||||
|
function_tab: 'Tab',
|
||||||
|
'function_multi-tab': 'Multi Tab',
|
||||||
|
'function_hide-child': 'Hide Child',
|
||||||
|
'function_hide-child_one': 'Hide Child',
|
||||||
|
'function_hide-child_two': 'Two',
|
||||||
|
'function_hide-child_three': 'Three',
|
||||||
|
function_request: 'Request',
|
||||||
|
'function_toggle-auth': 'Toggle Auth',
|
||||||
|
'function_super-page': 'Super Admin Visible',
|
||||||
|
manage: 'System Manage',
|
||||||
|
manage_user: 'User Manage',
|
||||||
|
'manage_user-detail': 'User Detail',
|
||||||
|
manage_role: 'Role Manage',
|
||||||
|
manage_menu: 'Menu Manage',
|
||||||
|
'multi-menu': 'Multi Menu',
|
||||||
|
'multi-menu_first': 'Menu One',
|
||||||
|
'multi-menu_first_child': 'Menu One Child',
|
||||||
|
'multi-menu_second': 'Menu Two',
|
||||||
|
'multi-menu_second_child': 'Menu Two Child',
|
||||||
|
'multi-menu_second_child_home': 'Menu Two Child Home',
|
||||||
|
exception: 'Exception',
|
||||||
|
exception_403: '403',
|
||||||
|
exception_404: '404',
|
||||||
|
exception_500: '500',
|
||||||
|
plugin: 'Plugin',
|
||||||
|
plugin_copy: 'Copy',
|
||||||
|
plugin_charts: 'Charts',
|
||||||
|
plugin_charts_echarts: 'ECharts',
|
||||||
|
plugin_charts_antv: 'AntV',
|
||||||
|
plugin_charts_vchart: 'VChart',
|
||||||
|
plugin_editor: 'Editor',
|
||||||
|
plugin_editor_quill: 'Quill',
|
||||||
|
plugin_editor_markdown: 'Markdown',
|
||||||
|
plugin_icon: 'Icon',
|
||||||
|
plugin_map: 'Map',
|
||||||
|
plugin_print: 'Print',
|
||||||
|
plugin_swiper: 'Swiper',
|
||||||
|
plugin_video: 'Video',
|
||||||
|
plugin_barcode: 'Barcode',
|
||||||
|
plugin_pinyin: 'pinyin',
|
||||||
|
plugin_excel: 'Excel',
|
||||||
|
plugin_pdf: 'PDF preview',
|
||||||
|
plugin_gantt: 'Gantt Chart',
|
||||||
|
plugin_gantt_dhtmlx: 'dhtmlxGantt',
|
||||||
|
plugin_gantt_vtable: 'VTableGantt',
|
||||||
|
plugin_typeit: 'Typeit',
|
||||||
|
plugin_tables: 'Tables',
|
||||||
|
plugin_tables_vtable: 'VTable'
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
@@ -277,6 +351,19 @@ const local: App.I18n.Schema = {
|
|||||||
title: 'Bind WeChat'
|
title: 'Bind WeChat'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
about: {
|
||||||
|
title: 'About',
|
||||||
|
introduction: `SoybeanAdmin is an elegant 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.`,
|
||||||
|
projectInfo: {
|
||||||
|
title: 'Project Info',
|
||||||
|
version: 'Version',
|
||||||
|
latestBuildTime: 'Latest Build Time',
|
||||||
|
githubLink: 'Github Link',
|
||||||
|
previewLink: 'Preview Link'
|
||||||
|
},
|
||||||
|
prdDep: 'Production Dependency',
|
||||||
|
devDep: 'Development Dependency'
|
||||||
|
},
|
||||||
home: {
|
home: {
|
||||||
branchDesc:
|
branchDesc:
|
||||||
'For the convenience of everyone in developing and updating the merge, we have streamlined the code of the main branch, only retaining the homepage menu, and the rest of the content has been moved to the example branch for maintenance. The preview address displays the content of the example branch.',
|
'For the convenience of everyone in developing and updating the merge, we have streamlined the code of the main branch, only retaining the homepage menu, and the rest of the content has been moved to the example branch for maintenance. The preview address displays the content of the example branch.',
|
||||||
@@ -305,6 +392,253 @@ const local: App.I18n.Schema = {
|
|||||||
desc5: 'Soybean just wrote some of the workbench pages casually, and it was enough to see!'
|
desc5: 'Soybean just wrote some of the workbench pages casually, and it was enough to see!'
|
||||||
},
|
},
|
||||||
creativity: 'Creativity'
|
creativity: 'Creativity'
|
||||||
|
},
|
||||||
|
function: {
|
||||||
|
tab: {
|
||||||
|
tabOperate: {
|
||||||
|
title: 'Tab Operation',
|
||||||
|
addTab: 'Add Tab',
|
||||||
|
addTabDesc: 'To about page',
|
||||||
|
closeTab: 'Close Tab',
|
||||||
|
closeCurrentTab: 'Close Current Tab',
|
||||||
|
closeAboutTab: 'Close "About" Tab',
|
||||||
|
addMultiTab: 'Add Multi Tab',
|
||||||
|
addMultiTabDesc1: 'To MultiTab page',
|
||||||
|
addMultiTabDesc2: 'To MultiTab page(with query params)'
|
||||||
|
},
|
||||||
|
tabTitle: {
|
||||||
|
title: 'Tab Title',
|
||||||
|
changeTitle: 'Change Title',
|
||||||
|
change: 'Change',
|
||||||
|
resetTitle: 'Reset Title',
|
||||||
|
reset: 'Reset'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
multiTab: {
|
||||||
|
routeParam: 'Route Param',
|
||||||
|
backTab: 'Back function_tab'
|
||||||
|
},
|
||||||
|
toggleAuth: {
|
||||||
|
toggleAccount: 'Toggle Account',
|
||||||
|
authHook: 'Auth Hook Function `hasAuth`',
|
||||||
|
superAdminVisible: 'Super Admin Visible',
|
||||||
|
adminVisible: 'Admin Visible',
|
||||||
|
adminOrUserVisible: 'Admin and User Visible'
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
repeatedErrorOccurOnce: 'Repeated Request Error Occurs Once',
|
||||||
|
repeatedError: 'Repeated Request Error',
|
||||||
|
repeatedErrorMsg1: 'Custom Request Error 1',
|
||||||
|
repeatedErrorMsg2: 'Custom Request Error 2'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
alova: {
|
||||||
|
scenes: {
|
||||||
|
captchaSend: 'Captcha Send',
|
||||||
|
autoRequest: 'Auto Request',
|
||||||
|
visibilityRequestTips: 'Automatically request when switching browser window',
|
||||||
|
pollingRequestTips: 'It will request every 3 seconds',
|
||||||
|
networkRequestTips: 'Automatically request after network reconnecting',
|
||||||
|
refreshTime: 'Refresh Time',
|
||||||
|
startRequest: 'Start Request',
|
||||||
|
stopRequest: 'Stop Request',
|
||||||
|
requestCrossComponent: 'Request Cross Component',
|
||||||
|
triggerAllRequest: 'Manually Trigger All Automated Requests'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
proNaive: {
|
||||||
|
form: {
|
||||||
|
basic: {
|
||||||
|
title: 'Basic Example',
|
||||||
|
appName: 'ApplicationName',
|
||||||
|
appStatus: 'ApplicationStatus',
|
||||||
|
createTime: 'CreateTime',
|
||||||
|
responseDate: 'ResponseDate',
|
||||||
|
specificationInfo: 'SpecificationInfo',
|
||||||
|
specificate: 'Specificate',
|
||||||
|
specificationName: 'SpecificationName',
|
||||||
|
specificationValue: 'SpecificationValue',
|
||||||
|
specificationColorRed: 'Red',
|
||||||
|
specificationColorOrange: 'Orange',
|
||||||
|
addSpecificateItem: 'Add Specificate Item',
|
||||||
|
fillValue: 'FillValue',
|
||||||
|
reset: 'Reset',
|
||||||
|
submit: 'Submit',
|
||||||
|
add: 'Add',
|
||||||
|
delete: 'Delete',
|
||||||
|
color: 'Color',
|
||||||
|
normal: 'Normal',
|
||||||
|
anomaly: 'Anomaly'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
title1: 'Query Example, which expands by default',
|
||||||
|
title2: 'Query Example, which fold by default, and two lines are retained when folding',
|
||||||
|
appName: 'ApplicationName',
|
||||||
|
appStatus: 'ApplicationStatus',
|
||||||
|
createTime: 'CreateTime',
|
||||||
|
responseDate: 'ResponseDate',
|
||||||
|
endDate: 'EndDate',
|
||||||
|
field: 'Field'
|
||||||
|
},
|
||||||
|
step: {
|
||||||
|
title: 'Step Form',
|
||||||
|
step1: {
|
||||||
|
title: 'Form 1',
|
||||||
|
field: 'Form 1 field',
|
||||||
|
nextStep: 'Next Step'
|
||||||
|
},
|
||||||
|
step2: {
|
||||||
|
title: 'Form 2',
|
||||||
|
field: 'Form 2 field',
|
||||||
|
prevStep: 'Prev Step',
|
||||||
|
submit: 'Submit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
remote: {
|
||||||
|
filterCondition: 'Filter Condition',
|
||||||
|
name: 'Name',
|
||||||
|
createTime: 'CreateTime',
|
||||||
|
responseTime: 'ResponseTime',
|
||||||
|
title: 'Remote Loading',
|
||||||
|
replicableText: 'Replicable Text',
|
||||||
|
tags: 'Tags',
|
||||||
|
dateFormatting: 'Date Formatting',
|
||||||
|
image: 'Image'
|
||||||
|
},
|
||||||
|
rowEdit: {
|
||||||
|
title: 'Edit Table',
|
||||||
|
reset: 'Reset',
|
||||||
|
submit: 'Submit',
|
||||||
|
edit: 'Edit',
|
||||||
|
delete: 'Delete',
|
||||||
|
save: 'Save',
|
||||||
|
task: 'Task',
|
||||||
|
score: 'Score',
|
||||||
|
time: 'Time',
|
||||||
|
name: 'Name',
|
||||||
|
action: 'Action'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
manage: {
|
||||||
|
common: {
|
||||||
|
status: {
|
||||||
|
enable: 'Enable',
|
||||||
|
disable: 'Disable'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
title: 'Role List',
|
||||||
|
roleName: 'Role Name',
|
||||||
|
roleCode: 'Role Code',
|
||||||
|
roleStatus: 'Role Status',
|
||||||
|
roleDesc: 'Role Description',
|
||||||
|
menuAuth: 'Menu Auth',
|
||||||
|
buttonAuth: 'Button Auth',
|
||||||
|
form: {
|
||||||
|
roleName: 'Please enter role name',
|
||||||
|
roleCode: 'Please enter role code',
|
||||||
|
roleStatus: 'Please select role status',
|
||||||
|
roleDesc: 'Please enter role description'
|
||||||
|
},
|
||||||
|
addRole: 'Add Role',
|
||||||
|
editRole: 'Edit Role'
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
title: 'User List',
|
||||||
|
userName: 'User Name',
|
||||||
|
userGender: 'Gender',
|
||||||
|
nickName: 'Nick Name',
|
||||||
|
userPhone: 'Phone Number',
|
||||||
|
userEmail: 'Email',
|
||||||
|
userStatus: 'User Status',
|
||||||
|
userRole: 'User Role',
|
||||||
|
form: {
|
||||||
|
userName: 'Please enter user name',
|
||||||
|
userGender: 'Please select gender',
|
||||||
|
nickName: 'Please enter nick name',
|
||||||
|
userPhone: 'Please enter phone number',
|
||||||
|
userEmail: 'Please enter email',
|
||||||
|
userStatus: 'Please select user status',
|
||||||
|
userRole: 'Please select user role'
|
||||||
|
},
|
||||||
|
addUser: 'Add User',
|
||||||
|
editUser: 'Edit User',
|
||||||
|
gender: {
|
||||||
|
male: 'Male',
|
||||||
|
female: 'Female'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
home: 'Home',
|
||||||
|
title: 'Menu List',
|
||||||
|
id: 'ID',
|
||||||
|
parentId: 'Parent ID',
|
||||||
|
menuType: 'Menu Type',
|
||||||
|
menuName: 'Menu Name',
|
||||||
|
routeName: 'Route Name',
|
||||||
|
routePath: 'Route Path',
|
||||||
|
pathParam: 'Path Param',
|
||||||
|
layout: 'Layout Component',
|
||||||
|
page: 'Page Component',
|
||||||
|
i18nKey: 'I18n Key',
|
||||||
|
icon: 'Icon',
|
||||||
|
localIcon: 'Local Icon',
|
||||||
|
iconTypeTitle: 'Icon Type',
|
||||||
|
order: 'Order',
|
||||||
|
constant: 'Constant',
|
||||||
|
keepAlive: 'Keep Alive',
|
||||||
|
href: 'Href',
|
||||||
|
hideInMenu: 'Hide In Menu',
|
||||||
|
activeMenu: 'Active Menu',
|
||||||
|
multiTab: 'Multi Tab',
|
||||||
|
fixedIndexInTab: 'Fixed Index In Tab',
|
||||||
|
query: 'Query Params',
|
||||||
|
button: 'Button',
|
||||||
|
buttonCode: 'Button Code',
|
||||||
|
buttonDesc: 'Button Desc',
|
||||||
|
menuStatus: 'Menu Status',
|
||||||
|
form: {
|
||||||
|
home: 'Please select home',
|
||||||
|
menuType: 'Please select menu type',
|
||||||
|
menuName: 'Please enter menu name',
|
||||||
|
routeName: 'Please enter route name',
|
||||||
|
routePath: 'Please enter route path',
|
||||||
|
pathParam: 'Please enter path param',
|
||||||
|
page: 'Please select page component',
|
||||||
|
layout: 'Please select layout component',
|
||||||
|
i18nKey: 'Please enter i18n key',
|
||||||
|
icon: 'Please enter iconify name',
|
||||||
|
localIcon: 'Please enter local icon name',
|
||||||
|
order: 'Please enter order',
|
||||||
|
keepAlive: 'Please select whether to cache route',
|
||||||
|
href: 'Please enter href',
|
||||||
|
hideInMenu: 'Please select whether to hide menu',
|
||||||
|
activeMenu: 'Please select route name of the highlighted menu',
|
||||||
|
multiTab: 'Please select whether to support multiple tabs',
|
||||||
|
fixedInTab: 'Please select whether to fix in the tab',
|
||||||
|
fixedIndexInTab: 'Please enter the index fixed in the tab',
|
||||||
|
queryKey: 'Please enter route parameter Key',
|
||||||
|
queryValue: 'Please enter route parameter Value',
|
||||||
|
button: 'Please select whether it is a button',
|
||||||
|
buttonCode: 'Please enter button code',
|
||||||
|
buttonDesc: 'Please enter button description',
|
||||||
|
menuStatus: 'Please select menu status'
|
||||||
|
},
|
||||||
|
addMenu: 'Add Menu',
|
||||||
|
editMenu: 'Edit Menu',
|
||||||
|
addChildMenu: 'Add Child Menu',
|
||||||
|
type: {
|
||||||
|
directory: 'Directory',
|
||||||
|
menu: 'Menu'
|
||||||
|
},
|
||||||
|
iconType: {
|
||||||
|
iconify: 'Iconify Icon',
|
||||||
|
local: 'Local Icon'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const local: App.I18n.Schema = {
|
|||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
close: '关闭',
|
close: '关闭',
|
||||||
check: '勾选',
|
check: '勾选',
|
||||||
|
selectAll: '全选',
|
||||||
expandColumn: '展开列',
|
expandColumn: '展开列',
|
||||||
columnSetting: '列设置',
|
columnSetting: '列设置',
|
||||||
config: '配置',
|
config: '配置',
|
||||||
@@ -224,7 +225,80 @@ const local: App.I18n.Schema = {
|
|||||||
404: '页面不存在',
|
404: '页面不存在',
|
||||||
500: '服务器错误',
|
500: '服务器错误',
|
||||||
'iframe-page': '外链页面',
|
'iframe-page': '外链页面',
|
||||||
home: '首页'
|
home: '首页',
|
||||||
|
document: '文档',
|
||||||
|
document_project: '项目文档',
|
||||||
|
'document_project-link': '项目文档(外链)',
|
||||||
|
document_video: '视频教程',
|
||||||
|
document_vue: 'Vue文档',
|
||||||
|
document_vite: 'Vite文档',
|
||||||
|
document_unocss: 'UnoCSS文档',
|
||||||
|
document_naive: 'Naive UI文档',
|
||||||
|
'document_pro-naive': 'Pro Naive UI文档',
|
||||||
|
document_antd: 'Ant Design Vue文档',
|
||||||
|
document_alova: 'Alova文档',
|
||||||
|
'user-center': '个人中心',
|
||||||
|
about: '关于',
|
||||||
|
function: '系统功能',
|
||||||
|
alova: 'alova示例',
|
||||||
|
alova_request: 'alova请求',
|
||||||
|
alova_scenes: '场景化请求',
|
||||||
|
'pro-naive': 'Pro Naive UI 示例',
|
||||||
|
'pro-naive_form': '表单',
|
||||||
|
'pro-naive_form_basic': '基础表单',
|
||||||
|
'pro-naive_form_query': '查询表单',
|
||||||
|
'pro-naive_form_step': '分步表单',
|
||||||
|
'pro-naive_table': '表格',
|
||||||
|
'pro-naive_table_remote': '远程加载',
|
||||||
|
'pro-naive_table_row-edit': '行编辑',
|
||||||
|
function_tab: '标签页',
|
||||||
|
'function_multi-tab': '多标签页',
|
||||||
|
'function_hide-child': '隐藏子菜单',
|
||||||
|
'function_hide-child_one': '隐藏子菜单',
|
||||||
|
'function_hide-child_two': '菜单二',
|
||||||
|
'function_hide-child_three': '菜单三',
|
||||||
|
function_request: '请求',
|
||||||
|
'function_toggle-auth': '切换权限',
|
||||||
|
'function_super-page': '超级管理员可见',
|
||||||
|
manage: '系统管理',
|
||||||
|
manage_user: '用户管理',
|
||||||
|
'manage_user-detail': '用户详情',
|
||||||
|
manage_role: '角色管理',
|
||||||
|
manage_menu: '菜单管理',
|
||||||
|
'multi-menu': '多级菜单',
|
||||||
|
'multi-menu_first': '菜单一',
|
||||||
|
'multi-menu_first_child': '菜单一子菜单',
|
||||||
|
'multi-menu_second': '菜单二',
|
||||||
|
'multi-menu_second_child': '菜单二子菜单',
|
||||||
|
'multi-menu_second_child_home': '菜单二子菜单首页',
|
||||||
|
exception: '异常页',
|
||||||
|
exception_403: '403',
|
||||||
|
exception_404: '404',
|
||||||
|
exception_500: '500',
|
||||||
|
plugin: '插件示例',
|
||||||
|
plugin_copy: '剪贴板',
|
||||||
|
plugin_charts: '图表',
|
||||||
|
plugin_charts_echarts: 'ECharts',
|
||||||
|
plugin_charts_antv: 'AntV',
|
||||||
|
plugin_charts_vchart: 'VChart',
|
||||||
|
plugin_editor: '编辑器',
|
||||||
|
plugin_editor_quill: '富文本编辑器',
|
||||||
|
plugin_editor_markdown: 'MD 编辑器',
|
||||||
|
plugin_icon: '图标',
|
||||||
|
plugin_map: '地图',
|
||||||
|
plugin_print: '打印',
|
||||||
|
plugin_swiper: 'Swiper',
|
||||||
|
plugin_video: '视频',
|
||||||
|
plugin_barcode: '条形码',
|
||||||
|
plugin_pinyin: '拼音',
|
||||||
|
plugin_excel: 'Excel',
|
||||||
|
plugin_pdf: 'PDF 预览',
|
||||||
|
plugin_gantt: '甘特图',
|
||||||
|
plugin_gantt_dhtmlx: 'dhtmlxGantt',
|
||||||
|
plugin_gantt_vtable: 'VTableGantt',
|
||||||
|
plugin_typeit: '打字机',
|
||||||
|
plugin_tables: '表格',
|
||||||
|
plugin_tables_vtable: 'VTable'
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
@@ -273,6 +347,19 @@ const local: App.I18n.Schema = {
|
|||||||
title: '绑定微信'
|
title: '绑定微信'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
about: {
|
||||||
|
title: '关于',
|
||||||
|
introduction: `SoybeanAdmin 是一个优雅且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite7, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件,代码规范严谨,实现了自动化的文件路由系统。此外,它还采用了基于 ApiFox 的在线Mock数据方案。SoybeanAdmin 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。`,
|
||||||
|
projectInfo: {
|
||||||
|
title: '项目信息',
|
||||||
|
version: '版本',
|
||||||
|
latestBuildTime: '最新构建时间',
|
||||||
|
githubLink: 'Github 地址',
|
||||||
|
previewLink: '预览地址'
|
||||||
|
},
|
||||||
|
prdDep: '生产依赖',
|
||||||
|
devDep: '开发依赖'
|
||||||
|
},
|
||||||
home: {
|
home: {
|
||||||
branchDesc:
|
branchDesc:
|
||||||
'为了方便大家开发和更新合并,我们对main分支的代码进行了精简,只保留了首页菜单,其余内容已移至example分支进行维护。预览地址显示的内容即为example分支的内容。',
|
'为了方便大家开发和更新合并,我们对main分支的代码进行了精简,只保留了首页菜单,其余内容已移至example分支进行维护。预览地址显示的内容即为example分支的内容。',
|
||||||
@@ -301,6 +388,253 @@ const local: App.I18n.Schema = {
|
|||||||
desc5: 'Soybean 刚才把工作台页面随便写了一些,凑合能看了!'
|
desc5: 'Soybean 刚才把工作台页面随便写了一些,凑合能看了!'
|
||||||
},
|
},
|
||||||
creativity: '创意'
|
creativity: '创意'
|
||||||
|
},
|
||||||
|
function: {
|
||||||
|
tab: {
|
||||||
|
tabOperate: {
|
||||||
|
title: '标签页操作',
|
||||||
|
addTab: '添加标签页',
|
||||||
|
addTabDesc: '跳转到关于页面',
|
||||||
|
closeTab: '关闭标签页',
|
||||||
|
closeCurrentTab: '关闭当前标签页',
|
||||||
|
closeAboutTab: '关闭"关于"标签页',
|
||||||
|
addMultiTab: '添加多标签页',
|
||||||
|
addMultiTabDesc1: '跳转到多标签页页面',
|
||||||
|
addMultiTabDesc2: '跳转到多标签页页面(带有查询参数)'
|
||||||
|
},
|
||||||
|
tabTitle: {
|
||||||
|
title: '标签页标题',
|
||||||
|
changeTitle: '修改标题',
|
||||||
|
change: '修改',
|
||||||
|
resetTitle: '重置标题',
|
||||||
|
reset: '重置'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
multiTab: {
|
||||||
|
routeParam: '路由参数',
|
||||||
|
backTab: '返回 function_tab'
|
||||||
|
},
|
||||||
|
toggleAuth: {
|
||||||
|
toggleAccount: '切换账号',
|
||||||
|
authHook: '权限钩子函数 `hasAuth`',
|
||||||
|
superAdminVisible: '超级管理员可见',
|
||||||
|
adminVisible: '管理员可见',
|
||||||
|
adminOrUserVisible: '管理员和用户可见'
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
repeatedErrorOccurOnce: '重复请求错误只出现一次',
|
||||||
|
repeatedError: '重复请求错误',
|
||||||
|
repeatedErrorMsg1: '自定义请求错误 1',
|
||||||
|
repeatedErrorMsg2: '自定义请求错误 2'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
alova: {
|
||||||
|
scenes: {
|
||||||
|
captchaSend: '发送验证码',
|
||||||
|
autoRequest: '自动请求',
|
||||||
|
visibilityRequestTips: '浏览器窗口切换自动请求数据',
|
||||||
|
pollingRequestTips: '每3秒自动请求一次',
|
||||||
|
networkRequestTips: '网络重连后自动请求',
|
||||||
|
refreshTime: '更新时间',
|
||||||
|
startRequest: '开始请求',
|
||||||
|
stopRequest: '停止请求',
|
||||||
|
requestCrossComponent: '跨组件触发请求',
|
||||||
|
triggerAllRequest: '手动触发所有自动请求'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
proNaive: {
|
||||||
|
form: {
|
||||||
|
basic: {
|
||||||
|
title: '基础示例',
|
||||||
|
appName: '应用名称',
|
||||||
|
appStatus: '应用状态',
|
||||||
|
createTime: '创建时间',
|
||||||
|
responseDate: '响应日期',
|
||||||
|
specificationInfo: '规格信息',
|
||||||
|
specificate: '规格',
|
||||||
|
specificationName: '规格名',
|
||||||
|
specificationValue: '规格值',
|
||||||
|
specificationColorRed: '红',
|
||||||
|
specificationColorOrange: '橙',
|
||||||
|
addSpecificateItem: '添加规格项',
|
||||||
|
fillValue: '填充值',
|
||||||
|
reset: '重置',
|
||||||
|
submit: '提交',
|
||||||
|
add: '新建',
|
||||||
|
delete: '删除',
|
||||||
|
color: '颜色',
|
||||||
|
normal: '正常',
|
||||||
|
anomaly: '异常'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
title1: '查询表单,默认展开',
|
||||||
|
title2: '查询表单,默认折叠,折叠时保留2行',
|
||||||
|
appName: '应用名称',
|
||||||
|
appStatus: '应用状态',
|
||||||
|
createTime: '创建时间',
|
||||||
|
responseDate: '响应日期',
|
||||||
|
endDate: '结束日期',
|
||||||
|
field: '字段'
|
||||||
|
},
|
||||||
|
step: {
|
||||||
|
title: '分步表单',
|
||||||
|
step1: {
|
||||||
|
title: '表单1',
|
||||||
|
field: '表单1字段',
|
||||||
|
nextStep: '下一步'
|
||||||
|
},
|
||||||
|
step2: {
|
||||||
|
title: '表单2',
|
||||||
|
field: '表单2字段',
|
||||||
|
prevStep: '上一步',
|
||||||
|
submit: '提交'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
remote: {
|
||||||
|
filterCondition: '筛选条件',
|
||||||
|
name: '名称',
|
||||||
|
createTime: '创建时间',
|
||||||
|
responseTime: '响应时间',
|
||||||
|
title: '远程加载',
|
||||||
|
replicableText: '可复制文本',
|
||||||
|
tags: 'tags',
|
||||||
|
dateFormatting: '日期格式化',
|
||||||
|
image: '图片'
|
||||||
|
},
|
||||||
|
rowEdit: {
|
||||||
|
title: '编辑表格',
|
||||||
|
reset: '重置',
|
||||||
|
submit: '提交',
|
||||||
|
edit: '编辑',
|
||||||
|
delete: '删除',
|
||||||
|
save: '保存',
|
||||||
|
task: '任务',
|
||||||
|
score: '评分',
|
||||||
|
time: '时间',
|
||||||
|
name: '名称',
|
||||||
|
action: '操作'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
manage: {
|
||||||
|
common: {
|
||||||
|
status: {
|
||||||
|
enable: '启用',
|
||||||
|
disable: '禁用'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
title: '角色列表',
|
||||||
|
roleName: '角色名称',
|
||||||
|
roleCode: '角色编码',
|
||||||
|
roleStatus: '角色状态',
|
||||||
|
roleDesc: '角色描述',
|
||||||
|
menuAuth: '菜单权限',
|
||||||
|
buttonAuth: '按钮权限',
|
||||||
|
form: {
|
||||||
|
roleName: '请输入角色名称',
|
||||||
|
roleCode: '请输入角色编码',
|
||||||
|
roleStatus: '请选择角色状态',
|
||||||
|
roleDesc: '请输入角色描述'
|
||||||
|
},
|
||||||
|
addRole: '新增角色',
|
||||||
|
editRole: '编辑角色'
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
title: '用户列表',
|
||||||
|
userName: '用户名',
|
||||||
|
userGender: '性别',
|
||||||
|
nickName: '昵称',
|
||||||
|
userPhone: '手机号',
|
||||||
|
userEmail: '邮箱',
|
||||||
|
userStatus: '用户状态',
|
||||||
|
userRole: '用户角色',
|
||||||
|
form: {
|
||||||
|
userName: '请输入用户名',
|
||||||
|
userGender: '请选择性别',
|
||||||
|
nickName: '请输入昵称',
|
||||||
|
userPhone: '请输入手机号',
|
||||||
|
userEmail: '请输入邮箱',
|
||||||
|
userStatus: '请选择用户状态',
|
||||||
|
userRole: '请选择用户角色'
|
||||||
|
},
|
||||||
|
addUser: '新增用户',
|
||||||
|
editUser: '编辑用户',
|
||||||
|
gender: {
|
||||||
|
male: '男',
|
||||||
|
female: '女'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
home: '首页',
|
||||||
|
title: '菜单列表',
|
||||||
|
id: 'ID',
|
||||||
|
parentId: '父级菜单ID',
|
||||||
|
menuType: '菜单类型',
|
||||||
|
menuName: '菜单名称',
|
||||||
|
routeName: '路由名称',
|
||||||
|
routePath: '路由路径',
|
||||||
|
pathParam: '路径参数',
|
||||||
|
layout: '布局',
|
||||||
|
page: '页面组件',
|
||||||
|
i18nKey: '国际化key',
|
||||||
|
icon: '图标',
|
||||||
|
localIcon: '本地图标',
|
||||||
|
iconTypeTitle: '图标类型',
|
||||||
|
order: '排序',
|
||||||
|
constant: '常量路由',
|
||||||
|
keepAlive: '缓存路由',
|
||||||
|
href: '外链',
|
||||||
|
hideInMenu: '隐藏菜单',
|
||||||
|
activeMenu: '高亮的菜单',
|
||||||
|
multiTab: '支持多页签',
|
||||||
|
fixedIndexInTab: '固定在页签中的序号',
|
||||||
|
query: '路由参数',
|
||||||
|
button: '按钮',
|
||||||
|
buttonCode: '按钮编码',
|
||||||
|
buttonDesc: '按钮描述',
|
||||||
|
menuStatus: '菜单状态',
|
||||||
|
form: {
|
||||||
|
home: '请选择首页',
|
||||||
|
menuType: '请选择菜单类型',
|
||||||
|
menuName: '请输入菜单名称',
|
||||||
|
routeName: '请输入路由名称',
|
||||||
|
routePath: '请输入路由路径',
|
||||||
|
pathParam: '请输入路径参数',
|
||||||
|
page: '请选择页面组件',
|
||||||
|
layout: '请选择布局组件',
|
||||||
|
i18nKey: '请输入国际化key',
|
||||||
|
icon: '请输入图标',
|
||||||
|
localIcon: '请选择本地图标',
|
||||||
|
order: '请输入排序',
|
||||||
|
keepAlive: '请选择是否缓存路由',
|
||||||
|
href: '请输入外链',
|
||||||
|
hideInMenu: '请选择是否隐藏菜单',
|
||||||
|
activeMenu: '请选择高亮的菜单的路由名称',
|
||||||
|
multiTab: '请选择是否支持多标签',
|
||||||
|
fixedInTab: '请选择是否固定在页签中',
|
||||||
|
fixedIndexInTab: '请输入固定在页签中的序号',
|
||||||
|
queryKey: '请输入路由参数Key',
|
||||||
|
queryValue: '请输入路由参数Value',
|
||||||
|
button: '请选择是否按钮',
|
||||||
|
buttonCode: '请输入按钮编码',
|
||||||
|
buttonDesc: '请输入按钮描述',
|
||||||
|
menuStatus: '请选择菜单状态'
|
||||||
|
},
|
||||||
|
addMenu: '新增菜单',
|
||||||
|
editMenu: '编辑菜单',
|
||||||
|
addChildMenu: '新增子菜单',
|
||||||
|
type: {
|
||||||
|
directory: '目录',
|
||||||
|
menu: '菜单'
|
||||||
|
},
|
||||||
|
iconType: {
|
||||||
|
iconify: 'iconify图标',
|
||||||
|
local: '本地图标'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
|
|||||||
11
src/main.ts
11
src/main.ts
@@ -1,7 +1,14 @@
|
|||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import './plugins/assets';
|
import './plugins/assets';
|
||||||
|
import {
|
||||||
|
setupAppVersionNotification,
|
||||||
|
setupDayjs,
|
||||||
|
setupIconifyOffline,
|
||||||
|
setupLoading,
|
||||||
|
setupNProgress,
|
||||||
|
setupProNaiveComponents
|
||||||
|
} from './plugins';
|
||||||
import { setupVueRootValidator } from 'vite-plugin-vue-transition-root-validator/client';
|
import { setupVueRootValidator } from 'vite-plugin-vue-transition-root-validator/client';
|
||||||
import { setupAppVersionNotification, setupDayjs, setupIconifyOffline, setupLoading, setupNProgress } from './plugins';
|
|
||||||
import { setupStore } from './store';
|
import { setupStore } from './store';
|
||||||
import { setupRouter } from './router';
|
import { setupRouter } from './router';
|
||||||
import { getLocale, setupI18n } from './locales';
|
import { getLocale, setupI18n } from './locales';
|
||||||
@@ -22,6 +29,8 @@ async function setupApp() {
|
|||||||
|
|
||||||
await setupRouter(app);
|
await setupRouter(app);
|
||||||
|
|
||||||
|
setupProNaiveComponents(app);
|
||||||
|
|
||||||
setupI18n(app);
|
setupI18n(app);
|
||||||
|
|
||||||
setupAppVersionNotification();
|
setupAppVersionNotification();
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
import 'virtual:svg-icons-register';
|
import 'virtual:svg-icons-register';
|
||||||
import 'uno.css';
|
import 'uno.css';
|
||||||
import '../styles/css/global.css';
|
import '../styles/css/global.css';
|
||||||
|
import 'swiper/css';
|
||||||
|
import 'swiper/css/navigation';
|
||||||
|
import 'swiper/css/pagination';
|
||||||
|
|||||||
10
src/plugins/components.ts
Normal file
10
src/plugins/components.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
import { ProDate, ProDateTime, ProInput, ProRate, create } from 'pro-naive-ui';
|
||||||
|
|
||||||
|
/** pro-naive-ui 支持配置表单的按需加载,所以需要注册 */
|
||||||
|
export function setupProNaiveComponents(app: App) {
|
||||||
|
const proNaive = create({
|
||||||
|
components: [ProInput, ProDate, ProDateTime, ProRate]
|
||||||
|
});
|
||||||
|
app.use(proNaive);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from './loading';
|
export * from './loading';
|
||||||
export * from './nprogress';
|
export * from './nprogress';
|
||||||
|
export * from './components';
|
||||||
export * from './iconify';
|
export * from './iconify';
|
||||||
export * from './dayjs';
|
export * from './dayjs';
|
||||||
export * from './app';
|
export * from './app';
|
||||||
|
|||||||
@@ -20,5 +20,47 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
500: () => import("@/views/_builtin/500/index.vue"),
|
500: () => import("@/views/_builtin/500/index.vue"),
|
||||||
"iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
|
"iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
|
||||||
login: () => import("@/views/_builtin/login/index.vue"),
|
login: () => import("@/views/_builtin/login/index.vue"),
|
||||||
|
about: () => import("@/views/about/index.vue"),
|
||||||
|
alova_request: () => import("@/views/alova/request/index.vue"),
|
||||||
|
alova_scenes: () => import("@/views/alova/scenes/index.vue"),
|
||||||
|
"function_hide-child_one": () => import("@/views/function/hide-child/one/index.vue"),
|
||||||
|
"function_hide-child_three": () => import("@/views/function/hide-child/three/index.vue"),
|
||||||
|
"function_hide-child_two": () => import("@/views/function/hide-child/two/index.vue"),
|
||||||
|
"function_multi-tab": () => import("@/views/function/multi-tab/index.vue"),
|
||||||
|
function_request: () => import("@/views/function/request/index.vue"),
|
||||||
|
"function_super-page": () => import("@/views/function/super-page/index.vue"),
|
||||||
|
function_tab: () => import("@/views/function/tab/index.vue"),
|
||||||
|
"function_toggle-auth": () => import("@/views/function/toggle-auth/index.vue"),
|
||||||
home: () => import("@/views/home/index.vue"),
|
home: () => import("@/views/home/index.vue"),
|
||||||
|
manage_menu: () => import("@/views/manage/menu/index.vue"),
|
||||||
|
manage_role: () => import("@/views/manage/role/index.vue"),
|
||||||
|
"manage_user-detail": () => import("@/views/manage/user-detail/[id].vue"),
|
||||||
|
manage_user: () => import("@/views/manage/user/index.vue"),
|
||||||
|
"multi-menu_first_child": () => import("@/views/multi-menu/first_child/index.vue"),
|
||||||
|
"multi-menu_second_child_home": () => import("@/views/multi-menu/second_child_home/index.vue"),
|
||||||
|
plugin_barcode: () => import("@/views/plugin/barcode/index.vue"),
|
||||||
|
plugin_charts_antv: () => import("@/views/plugin/charts/antv/index.vue"),
|
||||||
|
plugin_charts_echarts: () => import("@/views/plugin/charts/echarts/index.vue"),
|
||||||
|
plugin_charts_vchart: () => import("@/views/plugin/charts/vchart/index.vue"),
|
||||||
|
plugin_copy: () => import("@/views/plugin/copy/index.vue"),
|
||||||
|
plugin_editor_markdown: () => import("@/views/plugin/editor/markdown/index.vue"),
|
||||||
|
plugin_editor_quill: () => import("@/views/plugin/editor/quill/index.vue"),
|
||||||
|
plugin_excel: () => import("@/views/plugin/excel/index.vue"),
|
||||||
|
plugin_gantt_dhtmlx: () => import("@/views/plugin/gantt/dhtmlx/index.vue"),
|
||||||
|
plugin_gantt_vtable: () => import("@/views/plugin/gantt/vtable/index.vue"),
|
||||||
|
plugin_icon: () => import("@/views/plugin/icon/index.vue"),
|
||||||
|
plugin_map: () => import("@/views/plugin/map/index.vue"),
|
||||||
|
plugin_pdf: () => import("@/views/plugin/pdf/index.vue"),
|
||||||
|
plugin_pinyin: () => import("@/views/plugin/pinyin/index.vue"),
|
||||||
|
plugin_print: () => import("@/views/plugin/print/index.vue"),
|
||||||
|
plugin_swiper: () => import("@/views/plugin/swiper/index.vue"),
|
||||||
|
plugin_tables_vtable: () => import("@/views/plugin/tables/vtable/index.vue"),
|
||||||
|
plugin_typeit: () => import("@/views/plugin/typeit/index.vue"),
|
||||||
|
plugin_video: () => import("@/views/plugin/video/index.vue"),
|
||||||
|
"pro-naive_form_basic": () => import("@/views/pro-naive/form/basic/index.vue"),
|
||||||
|
"pro-naive_form_query": () => import("@/views/pro-naive/form/query/index.vue"),
|
||||||
|
"pro-naive_form_step": () => import("@/views/pro-naive/form/step/index.vue"),
|
||||||
|
"pro-naive_table_remote": () => import("@/views/pro-naive/table/remote/index.vue"),
|
||||||
|
"pro-naive_table_row-edit": () => import("@/views/pro-naive/table/row-edit/index.vue"),
|
||||||
|
"user-center": () => import("@/views/user-center/index.vue"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,169 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
hideInMenu: true
|
hideInMenu: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'about',
|
||||||
|
path: '/about',
|
||||||
|
component: 'layout.base$view.about',
|
||||||
|
meta: {
|
||||||
|
title: 'about',
|
||||||
|
i18nKey: 'route.about',
|
||||||
|
icon: 'fluent:book-information-24-regular',
|
||||||
|
order: 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'alova',
|
||||||
|
path: '/alova',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: 'alova',
|
||||||
|
i18nKey: 'route.alova',
|
||||||
|
icon: 'carbon:http',
|
||||||
|
order: 7
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'alova_request',
|
||||||
|
path: '/alova/request',
|
||||||
|
component: 'view.alova_request',
|
||||||
|
meta: {
|
||||||
|
title: 'alova_request',
|
||||||
|
i18nKey: 'route.alova_request',
|
||||||
|
order: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'alova_scenes',
|
||||||
|
path: '/alova/scenes',
|
||||||
|
component: 'view.alova_scenes',
|
||||||
|
meta: {
|
||||||
|
title: 'alova_scenes',
|
||||||
|
i18nKey: 'route.alova_scenes',
|
||||||
|
icon: 'cbi:scene-dynamic',
|
||||||
|
order: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'function',
|
||||||
|
path: '/function',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: 'function',
|
||||||
|
i18nKey: 'route.function',
|
||||||
|
icon: 'icon-park-outline:all-application',
|
||||||
|
order: 6
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'function_hide-child',
|
||||||
|
path: '/function/hide-child',
|
||||||
|
meta: {
|
||||||
|
title: 'function_hide-child',
|
||||||
|
i18nKey: 'route.function_hide-child',
|
||||||
|
icon: 'material-symbols:filter-list-off',
|
||||||
|
order: 2
|
||||||
|
},
|
||||||
|
redirect: '/function/hide-child/one',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'function_hide-child_one',
|
||||||
|
path: '/function/hide-child/one',
|
||||||
|
component: 'view.function_hide-child_one',
|
||||||
|
meta: {
|
||||||
|
title: 'function_hide-child_one',
|
||||||
|
i18nKey: 'route.function_hide-child_one',
|
||||||
|
icon: 'material-symbols:filter-list-off',
|
||||||
|
hideInMenu: true,
|
||||||
|
activeMenu: 'function_hide-child'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'function_hide-child_three',
|
||||||
|
path: '/function/hide-child/three',
|
||||||
|
component: 'view.function_hide-child_three',
|
||||||
|
meta: {
|
||||||
|
title: 'function_hide-child_three',
|
||||||
|
i18nKey: 'route.function_hide-child_three',
|
||||||
|
hideInMenu: true,
|
||||||
|
activeMenu: 'function_hide-child'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'function_hide-child_two',
|
||||||
|
path: '/function/hide-child/two',
|
||||||
|
component: 'view.function_hide-child_two',
|
||||||
|
meta: {
|
||||||
|
title: 'function_hide-child_two',
|
||||||
|
i18nKey: 'route.function_hide-child_two',
|
||||||
|
hideInMenu: true,
|
||||||
|
activeMenu: 'function_hide-child'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'function_multi-tab',
|
||||||
|
path: '/function/multi-tab',
|
||||||
|
component: 'view.function_multi-tab',
|
||||||
|
meta: {
|
||||||
|
title: 'function_multi-tab',
|
||||||
|
i18nKey: 'route.function_multi-tab',
|
||||||
|
icon: 'ic:round-tab',
|
||||||
|
multiTab: true,
|
||||||
|
hideInMenu: true,
|
||||||
|
activeMenu: 'function_tab'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'function_request',
|
||||||
|
path: '/function/request',
|
||||||
|
component: 'view.function_request',
|
||||||
|
meta: {
|
||||||
|
title: 'function_request',
|
||||||
|
i18nKey: 'route.function_request',
|
||||||
|
icon: 'carbon:network-overlay',
|
||||||
|
order: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'function_super-page',
|
||||||
|
path: '/function/super-page',
|
||||||
|
component: 'view.function_super-page',
|
||||||
|
meta: {
|
||||||
|
title: 'function_super-page',
|
||||||
|
i18nKey: 'route.function_super-page',
|
||||||
|
icon: 'ic:round-supervisor-account',
|
||||||
|
order: 5,
|
||||||
|
roles: ['R_SUPER']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'function_tab',
|
||||||
|
path: '/function/tab',
|
||||||
|
component: 'view.function_tab',
|
||||||
|
meta: {
|
||||||
|
title: 'function_tab',
|
||||||
|
i18nKey: 'route.function_tab',
|
||||||
|
icon: 'ic:round-tab',
|
||||||
|
order: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'function_toggle-auth',
|
||||||
|
path: '/function/toggle-auth',
|
||||||
|
component: 'view.function_toggle-auth',
|
||||||
|
meta: {
|
||||||
|
title: 'function_toggle-auth',
|
||||||
|
i18nKey: 'route.function_toggle-auth',
|
||||||
|
icon: 'ic:round-construction',
|
||||||
|
order: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'home',
|
name: 'home',
|
||||||
path: '/home',
|
path: '/home',
|
||||||
@@ -74,5 +237,468 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
constant: true,
|
constant: true,
|
||||||
hideInMenu: true
|
hideInMenu: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'manage',
|
||||||
|
path: '/manage',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: 'manage',
|
||||||
|
i18nKey: 'route.manage',
|
||||||
|
icon: 'carbon:cloud-service-management',
|
||||||
|
order: 9,
|
||||||
|
roles: ['R_ADMIN']
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'manage_menu',
|
||||||
|
path: '/manage/menu',
|
||||||
|
component: 'view.manage_menu',
|
||||||
|
meta: {
|
||||||
|
title: 'manage_menu',
|
||||||
|
i18nKey: 'route.manage_menu',
|
||||||
|
icon: 'material-symbols:route',
|
||||||
|
order: 3,
|
||||||
|
roles: ['R_ADMIN'],
|
||||||
|
keepAlive: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'manage_role',
|
||||||
|
path: '/manage/role',
|
||||||
|
component: 'view.manage_role',
|
||||||
|
meta: {
|
||||||
|
title: 'manage_role',
|
||||||
|
i18nKey: 'route.manage_role',
|
||||||
|
icon: 'carbon:user-role',
|
||||||
|
order: 2,
|
||||||
|
roles: ['R_SUPER']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'manage_user',
|
||||||
|
path: '/manage/user',
|
||||||
|
component: 'view.manage_user',
|
||||||
|
meta: {
|
||||||
|
title: 'manage_user',
|
||||||
|
i18nKey: 'route.manage_user',
|
||||||
|
icon: 'ic:round-manage-accounts',
|
||||||
|
order: 1,
|
||||||
|
roles: ['R_ADMIN']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'manage_user-detail',
|
||||||
|
path: '/manage/user-detail/:id',
|
||||||
|
component: 'view.manage_user-detail',
|
||||||
|
props: true,
|
||||||
|
meta: {
|
||||||
|
title: 'manage_user-detail',
|
||||||
|
i18nKey: 'route.manage_user-detail',
|
||||||
|
hideInMenu: true,
|
||||||
|
roles: ['R_ADMIN'],
|
||||||
|
activeMenu: 'manage_user'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'multi-menu',
|
||||||
|
path: '/multi-menu',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: 'multi-menu',
|
||||||
|
i18nKey: 'route.multi-menu',
|
||||||
|
order: 8
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'multi-menu_first',
|
||||||
|
path: '/multi-menu/first',
|
||||||
|
meta: {
|
||||||
|
title: 'multi-menu_first',
|
||||||
|
i18nKey: 'route.multi-menu_first',
|
||||||
|
order: 1
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'multi-menu_first_child',
|
||||||
|
path: '/multi-menu/first/child',
|
||||||
|
component: 'view.multi-menu_first_child',
|
||||||
|
meta: {
|
||||||
|
title: 'multi-menu_first_child',
|
||||||
|
i18nKey: 'route.multi-menu_first_child'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'multi-menu_second',
|
||||||
|
path: '/multi-menu/second',
|
||||||
|
meta: {
|
||||||
|
title: 'multi-menu_second',
|
||||||
|
i18nKey: 'route.multi-menu_second',
|
||||||
|
order: 2
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'multi-menu_second_child',
|
||||||
|
path: '/multi-menu/second/child',
|
||||||
|
meta: {
|
||||||
|
title: 'multi-menu_second_child',
|
||||||
|
i18nKey: 'route.multi-menu_second_child'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'multi-menu_second_child_home',
|
||||||
|
path: '/multi-menu/second/child/home',
|
||||||
|
component: 'view.multi-menu_second_child_home',
|
||||||
|
meta: {
|
||||||
|
title: 'multi-menu_second_child_home',
|
||||||
|
i18nKey: 'route.multi-menu_second_child_home'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin',
|
||||||
|
path: '/plugin',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: '插件示例',
|
||||||
|
i18nKey: 'route.plugin',
|
||||||
|
order: 7,
|
||||||
|
icon: 'clarity:plugin-line'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'plugin_barcode',
|
||||||
|
path: '/plugin/barcode',
|
||||||
|
component: 'view.plugin_barcode',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_barcode',
|
||||||
|
i18nKey: 'route.plugin_barcode',
|
||||||
|
icon: 'ic:round-barcode'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_charts',
|
||||||
|
path: '/plugin/charts',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_charts',
|
||||||
|
i18nKey: 'route.plugin_charts',
|
||||||
|
icon: 'mdi:chart-areaspline'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'plugin_charts_antv',
|
||||||
|
path: '/plugin/charts/antv',
|
||||||
|
component: 'view.plugin_charts_antv',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_charts_antv',
|
||||||
|
i18nKey: 'route.plugin_charts_antv',
|
||||||
|
icon: 'hugeicons:flow-square'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_charts_echarts',
|
||||||
|
path: '/plugin/charts/echarts',
|
||||||
|
component: 'view.plugin_charts_echarts',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_charts_echarts',
|
||||||
|
i18nKey: 'route.plugin_charts_echarts',
|
||||||
|
icon: 'simple-icons:apacheecharts'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_charts_vchart',
|
||||||
|
path: '/plugin/charts/vchart',
|
||||||
|
component: 'view.plugin_charts_vchart',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_charts_vchart',
|
||||||
|
i18nKey: 'route.plugin_charts_vchart',
|
||||||
|
localIcon: 'visactor'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_copy',
|
||||||
|
path: '/plugin/copy',
|
||||||
|
component: 'view.plugin_copy',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_copy',
|
||||||
|
i18nKey: 'route.plugin_copy',
|
||||||
|
icon: 'mdi:clipboard-outline'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_editor',
|
||||||
|
path: '/plugin/editor',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_editor',
|
||||||
|
i18nKey: 'route.plugin_editor',
|
||||||
|
icon: 'icon-park-outline:editor'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'plugin_editor_markdown',
|
||||||
|
path: '/plugin/editor/markdown',
|
||||||
|
component: 'view.plugin_editor_markdown',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_editor_markdown',
|
||||||
|
i18nKey: 'route.plugin_editor_markdown',
|
||||||
|
icon: 'ri:markdown-line'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_editor_quill',
|
||||||
|
path: '/plugin/editor/quill',
|
||||||
|
component: 'view.plugin_editor_quill',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_editor_quill',
|
||||||
|
i18nKey: 'route.plugin_editor_quill',
|
||||||
|
icon: 'mdi:file-document-edit-outline'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_excel',
|
||||||
|
path: '/plugin/excel',
|
||||||
|
component: 'view.plugin_excel',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_excel',
|
||||||
|
i18nKey: 'route.plugin_excel',
|
||||||
|
icon: 'ri:file-excel-2-line',
|
||||||
|
keepAlive: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_gantt',
|
||||||
|
path: '/plugin/gantt',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_gantt',
|
||||||
|
i18nKey: 'route.plugin_gantt',
|
||||||
|
icon: 'ant-design:bar-chart-outlined'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'plugin_gantt_dhtmlx',
|
||||||
|
path: '/plugin/gantt/dhtmlx',
|
||||||
|
component: 'view.plugin_gantt_dhtmlx',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_gantt_dhtmlx',
|
||||||
|
i18nKey: 'route.plugin_gantt_dhtmlx'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_gantt_vtable',
|
||||||
|
path: '/plugin/gantt/vtable',
|
||||||
|
component: 'view.plugin_gantt_vtable',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_gantt_vtable',
|
||||||
|
i18nKey: 'route.plugin_gantt_vtable',
|
||||||
|
localIcon: 'visactor'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_icon',
|
||||||
|
path: '/plugin/icon',
|
||||||
|
component: 'view.plugin_icon',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_icon',
|
||||||
|
i18nKey: 'route.plugin_icon',
|
||||||
|
localIcon: 'custom-icon'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_map',
|
||||||
|
path: '/plugin/map',
|
||||||
|
component: 'view.plugin_map',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_map',
|
||||||
|
i18nKey: 'route.plugin_map',
|
||||||
|
icon: 'mdi:map'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_pdf',
|
||||||
|
path: '/plugin/pdf',
|
||||||
|
component: 'view.plugin_pdf',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_pdf',
|
||||||
|
i18nKey: 'route.plugin_pdf',
|
||||||
|
icon: 'uiw:file-pdf'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_pinyin',
|
||||||
|
path: '/plugin/pinyin',
|
||||||
|
component: 'view.plugin_pinyin',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_pinyin',
|
||||||
|
i18nKey: 'route.plugin_pinyin',
|
||||||
|
icon: 'entypo-social:google-hangouts'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_print',
|
||||||
|
path: '/plugin/print',
|
||||||
|
component: 'view.plugin_print',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_print',
|
||||||
|
i18nKey: 'route.plugin_print',
|
||||||
|
icon: 'mdi:printer'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_swiper',
|
||||||
|
path: '/plugin/swiper',
|
||||||
|
component: 'view.plugin_swiper',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_swiper',
|
||||||
|
i18nKey: 'route.plugin_swiper',
|
||||||
|
icon: 'simple-icons:swiper'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_tables',
|
||||||
|
path: '/plugin/tables',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_tables',
|
||||||
|
i18nKey: 'route.plugin_tables',
|
||||||
|
icon: 'icon-park-outline:table'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'plugin_tables_vtable',
|
||||||
|
path: '/plugin/tables/vtable',
|
||||||
|
component: 'view.plugin_tables_vtable',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_tables_vtable',
|
||||||
|
i18nKey: 'route.plugin_tables_vtable',
|
||||||
|
localIcon: 'visactor'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_typeit',
|
||||||
|
path: '/plugin/typeit',
|
||||||
|
component: 'view.plugin_typeit',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_typeit',
|
||||||
|
i18nKey: 'route.plugin_typeit',
|
||||||
|
icon: 'mdi:typewriter'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_video',
|
||||||
|
path: '/plugin/video',
|
||||||
|
component: 'view.plugin_video',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_video',
|
||||||
|
i18nKey: 'route.plugin_video',
|
||||||
|
icon: 'mdi:video'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pro-naive',
|
||||||
|
path: '/pro-naive',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: 'pro-naive',
|
||||||
|
i18nKey: 'route.pro-naive',
|
||||||
|
order: 7,
|
||||||
|
icon: 'material-symbols-light:demography-outline-rounded'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'pro-naive_form',
|
||||||
|
path: '/pro-naive/form',
|
||||||
|
meta: {
|
||||||
|
title: 'pro-naive_form',
|
||||||
|
i18nKey: 'route.pro-naive_form',
|
||||||
|
icon: 'fluent:form-28-regular'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'pro-naive_form_basic',
|
||||||
|
path: '/pro-naive/form/basic',
|
||||||
|
component: 'view.pro-naive_form_basic',
|
||||||
|
meta: {
|
||||||
|
title: 'pro-naive_form_basic',
|
||||||
|
i18nKey: 'route.pro-naive_form_basic'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pro-naive_form_query',
|
||||||
|
path: '/pro-naive/form/query',
|
||||||
|
component: 'view.pro-naive_form_query',
|
||||||
|
meta: {
|
||||||
|
title: 'pro-naive_form_query',
|
||||||
|
i18nKey: 'route.pro-naive_form_query'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pro-naive_form_step',
|
||||||
|
path: '/pro-naive/form/step',
|
||||||
|
component: 'view.pro-naive_form_step',
|
||||||
|
meta: {
|
||||||
|
title: 'pro-naive_form_step',
|
||||||
|
i18nKey: 'route.pro-naive_form_step'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pro-naive_table',
|
||||||
|
path: '/pro-naive/table',
|
||||||
|
meta: {
|
||||||
|
title: 'pro-naive_table',
|
||||||
|
i18nKey: 'route.pro-naive_table',
|
||||||
|
icon: 'mynaui:table'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'pro-naive_table_remote',
|
||||||
|
path: '/pro-naive/table/remote',
|
||||||
|
component: 'view.pro-naive_table_remote',
|
||||||
|
meta: {
|
||||||
|
title: 'pro-naive_table_remote',
|
||||||
|
i18nKey: 'route.pro-naive_table_remote'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pro-naive_table_row-edit',
|
||||||
|
path: '/pro-naive/table/row-edit',
|
||||||
|
component: 'view.pro-naive_table_row-edit',
|
||||||
|
meta: {
|
||||||
|
title: 'pro-naive_table_row-edit',
|
||||||
|
i18nKey: 'route.pro-naive_table_row-edit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user-center',
|
||||||
|
path: '/user-center',
|
||||||
|
component: 'layout.base$view.user-center',
|
||||||
|
meta: {
|
||||||
|
title: 'user-center',
|
||||||
|
i18nKey: 'route.user-center',
|
||||||
|
hideInMenu: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -163,12 +163,85 @@ function transformElegantRouteToVueRoute(
|
|||||||
const routeMap: RouteMap = {
|
const routeMap: RouteMap = {
|
||||||
"root": "/",
|
"root": "/",
|
||||||
"not-found": "/:pathMatch(.*)*",
|
"not-found": "/:pathMatch(.*)*",
|
||||||
|
"exception": "/exception",
|
||||||
|
"exception_403": "/exception/403",
|
||||||
|
"exception_404": "/exception/404",
|
||||||
|
"exception_500": "/exception/500",
|
||||||
|
"document": "/document",
|
||||||
|
"document_project": "/document/project",
|
||||||
|
"document_project-link": "/document/project-link",
|
||||||
|
"document_video": "/document/video",
|
||||||
|
"document_vue": "/document/vue",
|
||||||
|
"document_vite": "/document/vite",
|
||||||
|
"document_unocss": "/document/unocss",
|
||||||
|
"document_naive": "/document/naive",
|
||||||
|
"document_pro-naive": "/document/pro-naive",
|
||||||
|
"document_antd": "/document/antd",
|
||||||
|
"document_alova": "/document/alova",
|
||||||
"403": "/403",
|
"403": "/403",
|
||||||
"404": "/404",
|
"404": "/404",
|
||||||
"500": "/500",
|
"500": "/500",
|
||||||
|
"about": "/about",
|
||||||
|
"alova": "/alova",
|
||||||
|
"alova_request": "/alova/request",
|
||||||
|
"alova_scenes": "/alova/scenes",
|
||||||
|
"function": "/function",
|
||||||
|
"function_hide-child": "/function/hide-child",
|
||||||
|
"function_hide-child_one": "/function/hide-child/one",
|
||||||
|
"function_hide-child_three": "/function/hide-child/three",
|
||||||
|
"function_hide-child_two": "/function/hide-child/two",
|
||||||
|
"function_multi-tab": "/function/multi-tab",
|
||||||
|
"function_request": "/function/request",
|
||||||
|
"function_super-page": "/function/super-page",
|
||||||
|
"function_tab": "/function/tab",
|
||||||
|
"function_toggle-auth": "/function/toggle-auth",
|
||||||
"home": "/home",
|
"home": "/home",
|
||||||
"iframe-page": "/iframe-page/:url",
|
"iframe-page": "/iframe-page/:url",
|
||||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"
|
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
|
||||||
|
"manage": "/manage",
|
||||||
|
"manage_menu": "/manage/menu",
|
||||||
|
"manage_role": "/manage/role",
|
||||||
|
"manage_user": "/manage/user",
|
||||||
|
"manage_user-detail": "/manage/user-detail/:id",
|
||||||
|
"multi-menu": "/multi-menu",
|
||||||
|
"multi-menu_first": "/multi-menu/first",
|
||||||
|
"multi-menu_first_child": "/multi-menu/first/child",
|
||||||
|
"multi-menu_second": "/multi-menu/second",
|
||||||
|
"multi-menu_second_child": "/multi-menu/second/child",
|
||||||
|
"multi-menu_second_child_home": "/multi-menu/second/child/home",
|
||||||
|
"plugin": "/plugin",
|
||||||
|
"plugin_barcode": "/plugin/barcode",
|
||||||
|
"plugin_charts": "/plugin/charts",
|
||||||
|
"plugin_charts_antv": "/plugin/charts/antv",
|
||||||
|
"plugin_charts_echarts": "/plugin/charts/echarts",
|
||||||
|
"plugin_charts_vchart": "/plugin/charts/vchart",
|
||||||
|
"plugin_copy": "/plugin/copy",
|
||||||
|
"plugin_editor": "/plugin/editor",
|
||||||
|
"plugin_editor_markdown": "/plugin/editor/markdown",
|
||||||
|
"plugin_editor_quill": "/plugin/editor/quill",
|
||||||
|
"plugin_excel": "/plugin/excel",
|
||||||
|
"plugin_gantt": "/plugin/gantt",
|
||||||
|
"plugin_gantt_dhtmlx": "/plugin/gantt/dhtmlx",
|
||||||
|
"plugin_gantt_vtable": "/plugin/gantt/vtable",
|
||||||
|
"plugin_icon": "/plugin/icon",
|
||||||
|
"plugin_map": "/plugin/map",
|
||||||
|
"plugin_pdf": "/plugin/pdf",
|
||||||
|
"plugin_pinyin": "/plugin/pinyin",
|
||||||
|
"plugin_print": "/plugin/print",
|
||||||
|
"plugin_swiper": "/plugin/swiper",
|
||||||
|
"plugin_tables": "/plugin/tables",
|
||||||
|
"plugin_tables_vtable": "/plugin/tables/vtable",
|
||||||
|
"plugin_typeit": "/plugin/typeit",
|
||||||
|
"plugin_video": "/plugin/video",
|
||||||
|
"pro-naive": "/pro-naive",
|
||||||
|
"pro-naive_form": "/pro-naive/form",
|
||||||
|
"pro-naive_form_basic": "/pro-naive/form/basic",
|
||||||
|
"pro-naive_form_query": "/pro-naive/form/query",
|
||||||
|
"pro-naive_form_step": "/pro-naive/form/step",
|
||||||
|
"pro-naive_table": "/pro-naive/table",
|
||||||
|
"pro-naive_table_remote": "/pro-naive/table/remote",
|
||||||
|
"pro-naive_table_row-edit": "/pro-naive/table/row-edit",
|
||||||
|
"user-center": "/user-center"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { Router } from 'vue-router';
|
import type { Router } from 'vue-router';
|
||||||
|
|
||||||
export function createProgressGuard(router: Router) {
|
export function createProgressGuard(router: Router) {
|
||||||
router.beforeEach((_to, _from, next) => {
|
router.beforeEach(() => {
|
||||||
window.NProgress?.start?.();
|
window.NProgress?.start?.();
|
||||||
next();
|
return;
|
||||||
});
|
});
|
||||||
router.afterEach(_to => {
|
router.afterEach(() => {
|
||||||
window.NProgress?.done?.();
|
window.NProgress?.done?.();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import type {
|
import type { LocationQueryRaw, RouteLocationNormalized, RouteLocationRaw, Router } from 'vue-router';
|
||||||
LocationQueryRaw,
|
|
||||||
NavigationGuardNext,
|
|
||||||
RouteLocationNormalized,
|
|
||||||
RouteLocationRaw,
|
|
||||||
Router
|
|
||||||
} from 'vue-router';
|
|
||||||
import type { RouteKey, RoutePath } from '@elegant-router/types';
|
import type { RouteKey, RoutePath } from '@elegant-router/types';
|
||||||
import { useAuthStore } from '@/store/modules/auth';
|
import { useAuthStore } from '@/store/modules/auth';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
@@ -17,12 +11,11 @@ import { getRouteName } from '@/router/elegant/transform';
|
|||||||
* @param router router instance
|
* @param router router instance
|
||||||
*/
|
*/
|
||||||
export function createRouteGuard(router: Router) {
|
export function createRouteGuard(router: Router) {
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from) => {
|
||||||
const location = await initRoute(to);
|
const location = await initRoute(to);
|
||||||
|
|
||||||
if (location) {
|
if (location) {
|
||||||
next(location);
|
return location;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
@@ -40,30 +33,27 @@ export function createRouteGuard(router: Router) {
|
|||||||
|
|
||||||
// if it is login route when logged in, then switch to the root page
|
// if it is login route when logged in, then switch to the root page
|
||||||
if (to.name === loginRoute && isLogin) {
|
if (to.name === loginRoute && isLogin) {
|
||||||
next({ name: rootRoute });
|
return { name: rootRoute };
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the route does not need login, then it is allowed to access directly
|
// if the route does not need login, then it is allowed to access directly
|
||||||
if (!needLogin) {
|
if (!needLogin) {
|
||||||
handleRouteSwitch(to, from, next);
|
handleRouteSwitch(to, from);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the route need login but the user is not logged in, then switch to the login page
|
// the route need login but the user is not logged in, then switch to the login page
|
||||||
if (!isLogin) {
|
if (!isLogin) {
|
||||||
next({ name: loginRoute, query: { redirect: to.fullPath } });
|
return { name: loginRoute, query: { redirect: to.fullPath } };
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the user is logged in but does not have authorization, then switch to the 403 page
|
// if the user is logged in but does not have authorization, then switch to the 403 page
|
||||||
if (!hasAuth) {
|
if (!hasAuth) {
|
||||||
next({ name: noAuthorizationRoute });
|
return { name: noAuthorizationRoute };
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch route normally
|
// switch route normally
|
||||||
handleRouteSwitch(to, from, next);
|
handleRouteSwitch(to, from);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,17 +151,13 @@ async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
|
function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNormalized) {
|
||||||
// route with href
|
// route with href
|
||||||
if (to.meta.href) {
|
if (to.meta.href) {
|
||||||
window.open(to.meta.href, '_blank');
|
window.open(to.meta.href, '_blank');
|
||||||
|
|
||||||
next({ path: from.fullPath, replace: true, query: from.query, hash: to.hash });
|
return { path: from.fullPath, replace: true, query: from.query, hash: to.hash };
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRouteQueryOfLoginRoute(to: RouteLocationNormalized, routeHome: RouteKey) {
|
function getRouteQueryOfLoginRoute(to: RouteLocationNormalized, routeHome: RouteKey) {
|
||||||
|
|||||||
@@ -8,7 +8,200 @@ import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
|
|||||||
*
|
*
|
||||||
* @link https://github.com/soybeanjs/elegant-router?tab=readme-ov-file#custom-route
|
* @link https://github.com/soybeanjs/elegant-router?tab=readme-ov-file#custom-route
|
||||||
*/
|
*/
|
||||||
const customRoutes: CustomRoute[] = [];
|
const customRoutes: CustomRoute[] = [
|
||||||
|
{
|
||||||
|
name: 'exception',
|
||||||
|
path: '/exception',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: 'exception',
|
||||||
|
i18nKey: 'route.exception',
|
||||||
|
icon: 'ant-design:exception-outlined',
|
||||||
|
order: 7
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'exception_403',
|
||||||
|
path: '/exception/403',
|
||||||
|
component: 'view.403',
|
||||||
|
meta: {
|
||||||
|
title: 'exception_403',
|
||||||
|
i18nKey: 'route.exception_403',
|
||||||
|
icon: 'ic:baseline-block'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'exception_404',
|
||||||
|
path: '/exception/404',
|
||||||
|
component: 'view.404',
|
||||||
|
meta: {
|
||||||
|
title: 'exception_404',
|
||||||
|
i18nKey: 'route.exception_404',
|
||||||
|
icon: 'ic:baseline-web-asset-off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'exception_500',
|
||||||
|
path: '/exception/500',
|
||||||
|
component: 'view.500',
|
||||||
|
meta: {
|
||||||
|
title: 'exception_500',
|
||||||
|
i18nKey: 'route.exception_500',
|
||||||
|
icon: 'ic:baseline-wifi-off'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document',
|
||||||
|
path: '/document',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: 'document',
|
||||||
|
i18nKey: 'route.document',
|
||||||
|
order: 2,
|
||||||
|
icon: 'mdi:file-document-multiple-outline'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'document_antd',
|
||||||
|
path: '/document/antd',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
props: {
|
||||||
|
url: 'https://antdv.com/components/overview-cn'
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'document_antd',
|
||||||
|
i18nKey: 'route.document_antd',
|
||||||
|
order: 7,
|
||||||
|
icon: 'logos:ant-design'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_naive',
|
||||||
|
path: '/document/naive',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
props: {
|
||||||
|
url: 'https://www.naiveui.com/zh-CN/os-theme/docs/introduction'
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'document_naive',
|
||||||
|
i18nKey: 'route.document_naive',
|
||||||
|
order: 6,
|
||||||
|
icon: 'logos:naiveui'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_pro-naive',
|
||||||
|
path: '/document/pro-naive',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
props: {
|
||||||
|
url: 'https://naive-ui.pro-components.cn/'
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'document_pro-naive',
|
||||||
|
i18nKey: 'route.document_pro-naive',
|
||||||
|
order: 6,
|
||||||
|
icon: 'logos:naiveui'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_alova',
|
||||||
|
path: '/document/alova',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
props: {
|
||||||
|
url: 'https://alova.js.org'
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'document_alova',
|
||||||
|
i18nKey: 'route.document_alova',
|
||||||
|
order: 7,
|
||||||
|
localIcon: 'alova'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_project',
|
||||||
|
path: '/document/project',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
props: {
|
||||||
|
url: 'https://docs.soybeanjs.cn/zh'
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'document_project',
|
||||||
|
i18nKey: 'route.document_project',
|
||||||
|
order: 1,
|
||||||
|
localIcon: 'logo'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_project-link',
|
||||||
|
path: '/document/project-link',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
meta: {
|
||||||
|
title: 'document_project-link',
|
||||||
|
i18nKey: 'route.document_project-link',
|
||||||
|
order: 2,
|
||||||
|
localIcon: 'logo',
|
||||||
|
href: 'https://docs.soybeanjs.cn/zh'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_video',
|
||||||
|
path: '/document/video',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
meta: {
|
||||||
|
title: 'document_video',
|
||||||
|
i18nKey: 'route.document_video',
|
||||||
|
order: 2,
|
||||||
|
localIcon: 'logo',
|
||||||
|
href: 'https://www.bilibili.com/video/BV1YKdRYXELC'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_unocss',
|
||||||
|
path: '/document/unocss',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
props: {
|
||||||
|
url: 'https://unocss.dev/'
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'document_unocss',
|
||||||
|
i18nKey: 'route.document_unocss',
|
||||||
|
order: 5,
|
||||||
|
icon: 'logos:unocss'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_vite',
|
||||||
|
path: '/document/vite',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
props: {
|
||||||
|
url: 'https://cn.vitejs.dev/'
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'document_vite',
|
||||||
|
i18nKey: 'route.document_vite',
|
||||||
|
order: 4,
|
||||||
|
icon: 'logos:vitejs'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_vue',
|
||||||
|
path: '/document/vue',
|
||||||
|
component: 'view.iframe-page',
|
||||||
|
props: {
|
||||||
|
url: 'https://cn.vuejs.org/'
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
title: 'document_vue',
|
||||||
|
i18nKey: 'route.document_vue',
|
||||||
|
order: 3,
|
||||||
|
icon: 'logos:vue'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
/** create routes when the auth route mode is static */
|
/** create routes when the auth route mode is static */
|
||||||
export function createStaticRoutes() {
|
export function createStaticRoutes() {
|
||||||
|
|||||||
56
src/service-alova/api/auth.ts
Normal file
56
src/service-alova/api/auth.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { alova } from '../request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login
|
||||||
|
*
|
||||||
|
* @param userName User name
|
||||||
|
* @param password Password
|
||||||
|
*/
|
||||||
|
export function fetchLogin(userName: string, password: string) {
|
||||||
|
return alova.Post<Api.Auth.LoginToken>('/auth/login', { userName, password });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get user info */
|
||||||
|
export function fetchGetUserInfo() {
|
||||||
|
return alova.Get<Api.Auth.UserInfo>('/auth/getUserInfo');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send captcha to target phone */
|
||||||
|
export function sendCaptcha(phone: string) {
|
||||||
|
return alova.Post<null>('/auth/sendCaptcha', { phone });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verify captcha */
|
||||||
|
export function verifyCaptcha(phone: string, code: string) {
|
||||||
|
return alova.Post<null>('/auth/verifyCaptcha', { phone, code });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh token
|
||||||
|
*
|
||||||
|
* @param refreshToken Refresh token
|
||||||
|
*/
|
||||||
|
export function fetchRefreshToken(refreshToken: string) {
|
||||||
|
return alova.Post<Api.Auth.LoginToken>(
|
||||||
|
'/auth/refreshToken',
|
||||||
|
{ refreshToken },
|
||||||
|
{
|
||||||
|
meta: {
|
||||||
|
authRole: 'refreshToken'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return custom backend error
|
||||||
|
*
|
||||||
|
* @param code error code
|
||||||
|
* @param msg error message
|
||||||
|
*/
|
||||||
|
export function fetchCustomBackendError(code: string, msg: string) {
|
||||||
|
return alova.Get('/auth/error', {
|
||||||
|
params: { code, msg },
|
||||||
|
shareRequest: false
|
||||||
|
});
|
||||||
|
}
|
||||||
3
src/service-alova/api/index.ts
Normal file
3
src/service-alova/api/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './auth';
|
||||||
|
export * from './route';
|
||||||
|
export * from './system-manage';
|
||||||
20
src/service-alova/api/route.ts
Normal file
20
src/service-alova/api/route.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { alova } from '../request';
|
||||||
|
|
||||||
|
/** get constant routes */
|
||||||
|
export function fetchGetConstantRoutes() {
|
||||||
|
return alova.Get<Api.Route.MenuRoute[]>('/route/getConstantRoutes');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get user routes */
|
||||||
|
export function fetchGetUserRoutes() {
|
||||||
|
return alova.Get<Api.Route.UserRoute>('/route/getUserRoutes');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* whether the route is exist
|
||||||
|
*
|
||||||
|
* @param routeName route name
|
||||||
|
*/
|
||||||
|
export function fetchIsRouteExist(routeName: string) {
|
||||||
|
return alova.Get<boolean>('/route/isRouteExist', { params: { routeName } });
|
||||||
|
}
|
||||||
59
src/service-alova/api/system-manage.ts
Normal file
59
src/service-alova/api/system-manage.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { alova } from '../request';
|
||||||
|
|
||||||
|
/** get role list */
|
||||||
|
export function fetchGetRoleList(params?: Api.SystemManage.RoleSearchParams) {
|
||||||
|
return alova.Get<Api.SystemManage.RoleList>('/systemManage/getRoleList', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all roles
|
||||||
|
*
|
||||||
|
* these roles are all enabled
|
||||||
|
*/
|
||||||
|
export function fetchGetAllRoles() {
|
||||||
|
return alova.Get<Api.SystemManage.AllRole[]>('/systemManage/getAllRoles');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get user list */
|
||||||
|
export function fetchGetUserList(params?: Api.SystemManage.UserSearchParams) {
|
||||||
|
return alova.Get<Api.SystemManage.UserList>('/systemManage/getUserList', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserModel = Pick<
|
||||||
|
Api.SystemManage.User,
|
||||||
|
'userName' | 'userGender' | 'nickName' | 'userPhone' | 'userEmail' | 'userRoles' | 'status'
|
||||||
|
>;
|
||||||
|
/** add user */
|
||||||
|
export function addUser(data: UserModel) {
|
||||||
|
return alova.Post<null>('/systemManage/addUser', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** update user */
|
||||||
|
export function updateUser(data: UserModel) {
|
||||||
|
return alova.Post<null>('/systemManage/updateUser', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** delete user */
|
||||||
|
export function deleteUser(id: number) {
|
||||||
|
return alova.Delete<null>('/systemManage/deleteUser', { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** batch delete user */
|
||||||
|
export function batchDeleteUser(ids: number[]) {
|
||||||
|
return alova.Delete<null>('/systemManage/batchDeleteUser', { ids });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get menu list */
|
||||||
|
export function fetchGetMenuList() {
|
||||||
|
return alova.Get<Api.SystemManage.MenuList>('/systemManage/getMenuList/v2');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get all pages */
|
||||||
|
export function fetchGetAllPages() {
|
||||||
|
return alova.Get<string[]>('/systemManage/getAllPages');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get menu tree */
|
||||||
|
export function fetchGetMenuTree() {
|
||||||
|
return alova.Get<Api.SystemManage.MenuTree[]>('/systemManage/getMenuTree');
|
||||||
|
}
|
||||||
56
src/service-alova/mocks/feature-users-20241014.ts
Normal file
56
src/service-alova/mocks/feature-users-20241014.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { defineMock } from '@sa/alova/mock';
|
||||||
|
|
||||||
|
// you can separate the mock data into multiple files dependent on your project versions
|
||||||
|
export default defineMock({
|
||||||
|
'[POST]/systemManage/addUser': () => {
|
||||||
|
return {
|
||||||
|
code: '0000',
|
||||||
|
msg: 'success',
|
||||||
|
data: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'[POST]/systemManage/updateUser': () => {
|
||||||
|
return {
|
||||||
|
code: '0000',
|
||||||
|
msg: 'success',
|
||||||
|
data: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'[DELETE]/systemManage/deleteUser': () => {
|
||||||
|
return {
|
||||||
|
code: '0000',
|
||||||
|
msg: 'success',
|
||||||
|
data: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'[DELETE]/systemManage/batchDeleteUser': () => {
|
||||||
|
return {
|
||||||
|
code: '0000',
|
||||||
|
msg: 'success',
|
||||||
|
data: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'[POST]/auth/sendCaptcha': () => {
|
||||||
|
return {
|
||||||
|
code: '0000',
|
||||||
|
msg: 'success',
|
||||||
|
data: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'[POST]/auth/verifyCaptcha': () => {
|
||||||
|
return {
|
||||||
|
code: '0000',
|
||||||
|
msg: 'success',
|
||||||
|
data: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'/mock/getLastTime': () => {
|
||||||
|
return {
|
||||||
|
code: '0000',
|
||||||
|
msg: 'success',
|
||||||
|
data: {
|
||||||
|
time: new Date().toLocaleTimeString()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
115
src/service-alova/request/index.ts
Normal file
115
src/service-alova/request/index.ts
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { createAlovaRequest } from '@sa/alova';
|
||||||
|
import { createAlovaMockAdapter } from '@sa/alova/mock';
|
||||||
|
import adapterFetch from '@sa/alova/fetch';
|
||||||
|
import { useAuthStore } from '@/store/modules/auth';
|
||||||
|
import { getServiceBaseURL } from '@/utils/service';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import featureUsers20241014 from '../mocks/feature-users-20241014';
|
||||||
|
import { getAuthorization, handleRefreshToken, showErrorMsg } from './shared';
|
||||||
|
import type { RequestInstanceState } from './type';
|
||||||
|
|
||||||
|
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
|
||||||
|
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
|
||||||
|
|
||||||
|
const state: RequestInstanceState = {
|
||||||
|
errMsgStack: []
|
||||||
|
};
|
||||||
|
const mockAdapter = createAlovaMockAdapter([featureUsers20241014], {
|
||||||
|
// using requestAdapter if not match mock request
|
||||||
|
httpAdapter: adapterFetch(),
|
||||||
|
|
||||||
|
// response delay time
|
||||||
|
delay: 1000,
|
||||||
|
|
||||||
|
// global mock toggle
|
||||||
|
enable: true,
|
||||||
|
matchMode: 'methodurl'
|
||||||
|
});
|
||||||
|
export const alova = createAlovaRequest(
|
||||||
|
{
|
||||||
|
baseURL,
|
||||||
|
requestAdapter: import.meta.env.DEV ? mockAdapter : adapterFetch()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onRequest({ config }) {
|
||||||
|
const Authorization = getAuthorization();
|
||||||
|
config.headers.Authorization = Authorization;
|
||||||
|
config.headers.apifoxToken = 'XL299LiMEDZ0H5h3A29PxwQXdMJqWyY2';
|
||||||
|
},
|
||||||
|
tokenRefresher: {
|
||||||
|
async isExpired(response) {
|
||||||
|
const expiredTokenCodes = import.meta.env.VITE_SERVICE_EXPIRED_TOKEN_CODES?.split(',') || [];
|
||||||
|
const { code } = await response.clone().json();
|
||||||
|
return expiredTokenCodes.includes(String(code));
|
||||||
|
},
|
||||||
|
async handler() {
|
||||||
|
await handleRefreshToken();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async isBackendSuccess(response) {
|
||||||
|
// when the backend response code is "0000"(default), it means the request is success
|
||||||
|
// to change this logic by yourself, you can modify the `VITE_SERVICE_SUCCESS_CODE` in `.env` file
|
||||||
|
const resp = response.clone();
|
||||||
|
const data = await resp.json();
|
||||||
|
return String(data.code) === import.meta.env.VITE_SERVICE_SUCCESS_CODE;
|
||||||
|
},
|
||||||
|
async transformBackendResponse(response) {
|
||||||
|
return (await response.clone().json()).data;
|
||||||
|
},
|
||||||
|
async onError(error, response) {
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
let message = error.message;
|
||||||
|
let responseCode = '';
|
||||||
|
if (response) {
|
||||||
|
const data = await response?.clone().json();
|
||||||
|
message = data.msg;
|
||||||
|
responseCode = String(data.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLogout() {
|
||||||
|
showErrorMsg(state, message);
|
||||||
|
authStore.resetStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function logoutAndCleanup() {
|
||||||
|
handleLogout();
|
||||||
|
window.removeEventListener('beforeunload', handleLogout);
|
||||||
|
state.errMsgStack = state.errMsgStack.filter(msg => msg !== message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// when the backend response code is in `logoutCodes`, it means the user will be logged out and redirected to login page
|
||||||
|
const logoutCodes = import.meta.env.VITE_SERVICE_LOGOUT_CODES?.split(',') || [];
|
||||||
|
if (logoutCodes.includes(responseCode)) {
|
||||||
|
handleLogout();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when the backend response code is in `modalLogoutCodes`, it means the user will be logged out by displaying a modal
|
||||||
|
const modalLogoutCodes = import.meta.env.VITE_SERVICE_MODAL_LOGOUT_CODES?.split(',') || [];
|
||||||
|
if (modalLogoutCodes.includes(responseCode) && !state.errMsgStack?.includes(message)) {
|
||||||
|
state.errMsgStack = [...(state.errMsgStack || []), message];
|
||||||
|
|
||||||
|
// prevent the user from refreshing the page
|
||||||
|
window.addEventListener('beforeunload', handleLogout);
|
||||||
|
|
||||||
|
window.$dialog?.error({
|
||||||
|
title: $t('common.error'),
|
||||||
|
content: message,
|
||||||
|
positiveText: $t('common.confirm'),
|
||||||
|
maskClosable: false,
|
||||||
|
closeOnEsc: false,
|
||||||
|
onPositiveClick() {
|
||||||
|
logoutAndCleanup();
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
logoutAndCleanup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
showErrorMsg(state, message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
53
src/service-alova/request/shared.ts
Normal file
53
src/service-alova/request/shared.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { useAuthStore } from '@/store/modules/auth';
|
||||||
|
import { localStg } from '@/utils/storage';
|
||||||
|
import { fetchRefreshToken } from '../api';
|
||||||
|
import type { RequestInstanceState } from './type';
|
||||||
|
|
||||||
|
export function getAuthorization() {
|
||||||
|
const token = localStg.get('token');
|
||||||
|
const Authorization = token ? `Bearer ${token}` : null;
|
||||||
|
|
||||||
|
return Authorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** refresh token */
|
||||||
|
export async function handleRefreshToken() {
|
||||||
|
const { resetStore } = useAuthStore();
|
||||||
|
|
||||||
|
const rToken = localStg.get('refreshToken') || '';
|
||||||
|
const refreshTokenMethod = fetchRefreshToken(rToken);
|
||||||
|
|
||||||
|
// set the refreshToken role, so that the request will not be intercepted
|
||||||
|
refreshTokenMethod.meta.authRole = 'refreshToken';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await refreshTokenMethod;
|
||||||
|
localStg.set('token', data.token);
|
||||||
|
localStg.set('refreshToken', data.refreshToken);
|
||||||
|
} catch (error) {
|
||||||
|
resetStore();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showErrorMsg(state: RequestInstanceState, message: string) {
|
||||||
|
if (!state.errMsgStack?.length) {
|
||||||
|
state.errMsgStack = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const isExist = state.errMsgStack.includes(message);
|
||||||
|
|
||||||
|
if (!isExist) {
|
||||||
|
state.errMsgStack.push(message);
|
||||||
|
|
||||||
|
window.$message?.error(message, {
|
||||||
|
onLeave: () => {
|
||||||
|
state.errMsgStack = state.errMsgStack.filter(msg => msg !== message);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
state.errMsgStack = [];
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/service-alova/request/type.ts
Normal file
4
src/service-alova/request/type.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface RequestInstanceState {
|
||||||
|
/** the request error message stack */
|
||||||
|
errMsgStack: string[];
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './auth';
|
export * from './auth';
|
||||||
export * from './route';
|
export * from './route';
|
||||||
|
export * from './system-manage';
|
||||||
|
|||||||
55
src/service/api/system-manage.ts
Normal file
55
src/service/api/system-manage.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { request } from '../request';
|
||||||
|
|
||||||
|
/** get role list */
|
||||||
|
export function fetchGetRoleList(params?: Api.SystemManage.RoleSearchParams) {
|
||||||
|
return request<Api.SystemManage.RoleList>({
|
||||||
|
url: '/systemManage/getRoleList',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all roles
|
||||||
|
*
|
||||||
|
* these roles are all enabled
|
||||||
|
*/
|
||||||
|
export function fetchGetAllRoles() {
|
||||||
|
return request<Api.SystemManage.AllRole[]>({
|
||||||
|
url: '/systemManage/getAllRoles',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get user list */
|
||||||
|
export function fetchGetUserList(params?: Api.SystemManage.UserSearchParams) {
|
||||||
|
return request<Api.SystemManage.UserList>({
|
||||||
|
url: '/systemManage/getUserList',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get menu list */
|
||||||
|
export function fetchGetMenuList() {
|
||||||
|
return request<Api.SystemManage.MenuList>({
|
||||||
|
url: '/systemManage/getMenuList/v2',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get all pages */
|
||||||
|
export function fetchGetAllPages() {
|
||||||
|
return request<string[]>({
|
||||||
|
url: '/systemManage/getAllPages',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get menu tree */
|
||||||
|
export function fetchGetMenuTree() {
|
||||||
|
return request<Api.SystemManage.MenuTree[]>({
|
||||||
|
url: '/systemManage/getMenuTree',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
const { toLogin, redirectFromLogin } = useRouterPush(false);
|
const { toLogin, redirectFromLogin } = useRouterPush(false);
|
||||||
const { loading: loginLoading, startLoading, endLoading } = useLoading();
|
const { loading: loginLoading, startLoading, endLoading } = useLoading();
|
||||||
|
|
||||||
const token = ref(getToken());
|
const token = ref('');
|
||||||
|
|
||||||
const userInfo: Api.Auth.UserInfo = reactive({
|
const userInfo: Api.Auth.UserInfo = reactive({
|
||||||
userId: '',
|
userId: '',
|
||||||
@@ -159,9 +159,10 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function initUserInfo() {
|
async function initUserInfo() {
|
||||||
const hasToken = getToken();
|
const maybeToken = getToken();
|
||||||
|
|
||||||
if (hasToken) {
|
if (maybeToken) {
|
||||||
|
token.value = maybeToken;
|
||||||
const pass = await getUserInfo();
|
const pass = await getUserInfo();
|
||||||
|
|
||||||
if (!pass) {
|
if (!pass) {
|
||||||
|
|||||||
139
src/typings/api/system-manage.d.ts
vendored
Normal file
139
src/typings/api/system-manage.d.ts
vendored
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
declare namespace Api {
|
||||||
|
/**
|
||||||
|
* namespace SystemManage
|
||||||
|
*
|
||||||
|
* backend api module: "systemManage"
|
||||||
|
*/
|
||||||
|
namespace SystemManage {
|
||||||
|
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'current' | 'size'>;
|
||||||
|
|
||||||
|
/** role */
|
||||||
|
type Role = Common.CommonRecord<{
|
||||||
|
/** role name */
|
||||||
|
roleName: string;
|
||||||
|
/** role code */
|
||||||
|
roleCode: string;
|
||||||
|
/** role description */
|
||||||
|
roleDesc: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
/** role search params */
|
||||||
|
type RoleSearchParams = CommonType.RecordNullable<
|
||||||
|
Pick<Api.SystemManage.Role, 'roleName' | 'roleCode' | 'status'> & CommonSearchParams
|
||||||
|
>;
|
||||||
|
|
||||||
|
/** role list */
|
||||||
|
type RoleList = Common.PaginatingQueryRecord<Role>;
|
||||||
|
|
||||||
|
/** all role */
|
||||||
|
type AllRole = Pick<Role, 'id' | 'roleName' | 'roleCode'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* user gender
|
||||||
|
*
|
||||||
|
* - "1": "male"
|
||||||
|
* - "2": "female"
|
||||||
|
*/
|
||||||
|
type UserGender = '1' | '2';
|
||||||
|
|
||||||
|
/** user */
|
||||||
|
type User = Common.CommonRecord<{
|
||||||
|
/** user name */
|
||||||
|
userName: string;
|
||||||
|
/** user gender */
|
||||||
|
userGender: UserGender | null;
|
||||||
|
/** user nick name */
|
||||||
|
nickName: string;
|
||||||
|
/** user phone */
|
||||||
|
userPhone: string;
|
||||||
|
/** user email */
|
||||||
|
userEmail: string;
|
||||||
|
/** user role code collection */
|
||||||
|
userRoles: string[];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
/** user search params */
|
||||||
|
type UserSearchParams = CommonType.RecordNullable<
|
||||||
|
Pick<Api.SystemManage.User, 'userName' | 'userGender' | 'nickName' | 'userPhone' | 'userEmail' | 'status'> &
|
||||||
|
CommonSearchParams
|
||||||
|
>;
|
||||||
|
|
||||||
|
/** user list */
|
||||||
|
type UserList = Common.PaginatingQueryRecord<User>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* menu type
|
||||||
|
*
|
||||||
|
* - "1": directory
|
||||||
|
* - "2": menu
|
||||||
|
*/
|
||||||
|
type MenuType = '1' | '2';
|
||||||
|
|
||||||
|
type MenuButton = {
|
||||||
|
/**
|
||||||
|
* button code
|
||||||
|
*
|
||||||
|
* it can be used to control the button permission
|
||||||
|
*/
|
||||||
|
code: string;
|
||||||
|
/** button description */
|
||||||
|
desc: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* icon type
|
||||||
|
*
|
||||||
|
* - "1": iconify icon
|
||||||
|
* - "2": local icon
|
||||||
|
*/
|
||||||
|
type IconType = '1' | '2';
|
||||||
|
|
||||||
|
type MenuPropsOfRoute = Pick<
|
||||||
|
import('vue-router').RouteMeta,
|
||||||
|
| 'i18nKey'
|
||||||
|
| 'keepAlive'
|
||||||
|
| 'constant'
|
||||||
|
| 'order'
|
||||||
|
| 'href'
|
||||||
|
| 'hideInMenu'
|
||||||
|
| 'activeMenu'
|
||||||
|
| 'multiTab'
|
||||||
|
| 'fixedIndexInTab'
|
||||||
|
| 'query'
|
||||||
|
>;
|
||||||
|
|
||||||
|
type Menu = Common.CommonRecord<{
|
||||||
|
/** parent menu id */
|
||||||
|
parentId: number;
|
||||||
|
/** menu type */
|
||||||
|
menuType: MenuType;
|
||||||
|
/** menu name */
|
||||||
|
menuName: string;
|
||||||
|
/** route name */
|
||||||
|
routeName: string;
|
||||||
|
/** route path */
|
||||||
|
routePath: string;
|
||||||
|
/** component */
|
||||||
|
component?: string;
|
||||||
|
/** iconify icon name or local icon name */
|
||||||
|
icon: string;
|
||||||
|
/** icon type */
|
||||||
|
iconType: IconType;
|
||||||
|
/** buttons */
|
||||||
|
buttons?: MenuButton[] | null;
|
||||||
|
/** children menu */
|
||||||
|
children?: Menu[] | null;
|
||||||
|
}> &
|
||||||
|
MenuPropsOfRoute;
|
||||||
|
|
||||||
|
/** menu list */
|
||||||
|
type MenuList = Common.PaginatingQueryRecord<Menu>;
|
||||||
|
|
||||||
|
type MenuTree = {
|
||||||
|
id: number;
|
||||||
|
label: string;
|
||||||
|
pId: number;
|
||||||
|
children?: MenuTree[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
262
src/typings/app.d.ts
vendored
262
src/typings/app.d.ts
vendored
@@ -196,6 +196,7 @@ declare namespace App {
|
|||||||
type RouterPushOptions = {
|
type RouterPushOptions = {
|
||||||
query?: Record<string, string>;
|
query?: Record<string, string>;
|
||||||
params?: Record<string, string>;
|
params?: Record<string, string>;
|
||||||
|
force?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The global header props */
|
/** The global header props */
|
||||||
@@ -326,6 +327,7 @@ declare namespace App {
|
|||||||
cancel: string;
|
cancel: string;
|
||||||
close: string;
|
close: string;
|
||||||
check: string;
|
check: string;
|
||||||
|
selectAll: string;
|
||||||
expandColumn: string;
|
expandColumn: string;
|
||||||
columnSetting: string;
|
columnSetting: string;
|
||||||
config: string;
|
config: string;
|
||||||
@@ -525,6 +527,19 @@ declare namespace App {
|
|||||||
title: string;
|
title: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
about: {
|
||||||
|
title: string;
|
||||||
|
introduction: string;
|
||||||
|
projectInfo: {
|
||||||
|
title: string;
|
||||||
|
version: string;
|
||||||
|
latestBuildTime: string;
|
||||||
|
githubLink: string;
|
||||||
|
previewLink: string;
|
||||||
|
};
|
||||||
|
prdDep: string;
|
||||||
|
devDep: string;
|
||||||
|
};
|
||||||
home: {
|
home: {
|
||||||
branchDesc: string;
|
branchDesc: string;
|
||||||
greeting: string;
|
greeting: string;
|
||||||
@@ -553,6 +568,253 @@ declare namespace App {
|
|||||||
};
|
};
|
||||||
creativity: string;
|
creativity: string;
|
||||||
};
|
};
|
||||||
|
function: {
|
||||||
|
tab: {
|
||||||
|
tabOperate: {
|
||||||
|
title: string;
|
||||||
|
addTab: string;
|
||||||
|
addTabDesc: string;
|
||||||
|
closeTab: string;
|
||||||
|
closeCurrentTab: string;
|
||||||
|
closeAboutTab: string;
|
||||||
|
addMultiTab: string;
|
||||||
|
addMultiTabDesc1: string;
|
||||||
|
addMultiTabDesc2: string;
|
||||||
|
};
|
||||||
|
tabTitle: {
|
||||||
|
title: string;
|
||||||
|
changeTitle: string;
|
||||||
|
change: string;
|
||||||
|
resetTitle: string;
|
||||||
|
reset: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
multiTab: {
|
||||||
|
routeParam: string;
|
||||||
|
backTab: string;
|
||||||
|
};
|
||||||
|
toggleAuth: {
|
||||||
|
toggleAccount: string;
|
||||||
|
authHook: string;
|
||||||
|
superAdminVisible: string;
|
||||||
|
adminVisible: string;
|
||||||
|
adminOrUserVisible: string;
|
||||||
|
};
|
||||||
|
request: {
|
||||||
|
repeatedErrorOccurOnce: string;
|
||||||
|
repeatedError: string;
|
||||||
|
repeatedErrorMsg1: string;
|
||||||
|
repeatedErrorMsg2: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
alova: {
|
||||||
|
scenes: {
|
||||||
|
captchaSend: string;
|
||||||
|
autoRequest: string;
|
||||||
|
visibilityRequestTips: string;
|
||||||
|
pollingRequestTips: string;
|
||||||
|
networkRequestTips: string;
|
||||||
|
refreshTime: string;
|
||||||
|
startRequest: string;
|
||||||
|
stopRequest: string;
|
||||||
|
requestCrossComponent: string;
|
||||||
|
triggerAllRequest: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
proNaive: {
|
||||||
|
form: {
|
||||||
|
basic: {
|
||||||
|
title: string;
|
||||||
|
appName: string;
|
||||||
|
appStatus: string;
|
||||||
|
createTime: string;
|
||||||
|
responseDate: string;
|
||||||
|
specificationInfo: string;
|
||||||
|
specificate: string;
|
||||||
|
specificationName: string;
|
||||||
|
specificationValue: string;
|
||||||
|
specificationColorRed: string;
|
||||||
|
specificationColorOrange: string;
|
||||||
|
addSpecificateItem: string;
|
||||||
|
fillValue: string;
|
||||||
|
reset: string;
|
||||||
|
submit: string;
|
||||||
|
add: string;
|
||||||
|
delete: string;
|
||||||
|
color: string;
|
||||||
|
normal: string;
|
||||||
|
anomaly: string;
|
||||||
|
};
|
||||||
|
query: {
|
||||||
|
title1: string;
|
||||||
|
title2: string;
|
||||||
|
appName: string;
|
||||||
|
appStatus: string;
|
||||||
|
createTime: string;
|
||||||
|
responseDate: string;
|
||||||
|
endDate: string;
|
||||||
|
field: string;
|
||||||
|
};
|
||||||
|
step: {
|
||||||
|
title: string;
|
||||||
|
step1: {
|
||||||
|
title: string;
|
||||||
|
field: string;
|
||||||
|
nextStep: string;
|
||||||
|
};
|
||||||
|
step2: {
|
||||||
|
title: string;
|
||||||
|
field: string;
|
||||||
|
prevStep: string;
|
||||||
|
submit: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
table: {
|
||||||
|
remote: {
|
||||||
|
filterCondition: string;
|
||||||
|
name: string;
|
||||||
|
createTime: string;
|
||||||
|
responseTime: string;
|
||||||
|
title: string;
|
||||||
|
replicableText: string;
|
||||||
|
tags: string;
|
||||||
|
dateFormatting: string;
|
||||||
|
image: string;
|
||||||
|
};
|
||||||
|
rowEdit: {
|
||||||
|
title: string;
|
||||||
|
reset: string;
|
||||||
|
submit: string;
|
||||||
|
edit: string;
|
||||||
|
delete: string;
|
||||||
|
save: string;
|
||||||
|
task: string;
|
||||||
|
score: string;
|
||||||
|
time: string;
|
||||||
|
name: string;
|
||||||
|
action: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
manage: {
|
||||||
|
common: {
|
||||||
|
status: {
|
||||||
|
enable: string;
|
||||||
|
disable: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
role: {
|
||||||
|
title: string;
|
||||||
|
roleName: string;
|
||||||
|
roleCode: string;
|
||||||
|
roleStatus: string;
|
||||||
|
roleDesc: string;
|
||||||
|
form: {
|
||||||
|
roleName: string;
|
||||||
|
roleCode: string;
|
||||||
|
roleStatus: string;
|
||||||
|
roleDesc: string;
|
||||||
|
};
|
||||||
|
addRole: string;
|
||||||
|
editRole: string;
|
||||||
|
menuAuth: string;
|
||||||
|
buttonAuth: string;
|
||||||
|
};
|
||||||
|
user: {
|
||||||
|
title: string;
|
||||||
|
userName: string;
|
||||||
|
userGender: string;
|
||||||
|
nickName: string;
|
||||||
|
userPhone: string;
|
||||||
|
userEmail: string;
|
||||||
|
userStatus: string;
|
||||||
|
userRole: string;
|
||||||
|
form: {
|
||||||
|
userName: string;
|
||||||
|
userGender: string;
|
||||||
|
nickName: string;
|
||||||
|
userPhone: string;
|
||||||
|
userEmail: string;
|
||||||
|
userStatus: string;
|
||||||
|
userRole: string;
|
||||||
|
};
|
||||||
|
addUser: string;
|
||||||
|
editUser: string;
|
||||||
|
gender: {
|
||||||
|
male: string;
|
||||||
|
female: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
menu: {
|
||||||
|
home: string;
|
||||||
|
title: string;
|
||||||
|
id: string;
|
||||||
|
parentId: string;
|
||||||
|
menuType: string;
|
||||||
|
menuName: string;
|
||||||
|
routeName: string;
|
||||||
|
routePath: string;
|
||||||
|
pathParam: string;
|
||||||
|
layout: string;
|
||||||
|
page: string;
|
||||||
|
i18nKey: string;
|
||||||
|
icon: string;
|
||||||
|
localIcon: string;
|
||||||
|
iconTypeTitle: string;
|
||||||
|
order: string;
|
||||||
|
constant: string;
|
||||||
|
keepAlive: string;
|
||||||
|
href: string;
|
||||||
|
hideInMenu: string;
|
||||||
|
activeMenu: string;
|
||||||
|
multiTab: string;
|
||||||
|
fixedIndexInTab: string;
|
||||||
|
query: string;
|
||||||
|
button: string;
|
||||||
|
buttonCode: string;
|
||||||
|
buttonDesc: string;
|
||||||
|
menuStatus: string;
|
||||||
|
form: {
|
||||||
|
home: string;
|
||||||
|
menuType: string;
|
||||||
|
menuName: string;
|
||||||
|
routeName: string;
|
||||||
|
routePath: string;
|
||||||
|
pathParam: string;
|
||||||
|
layout: string;
|
||||||
|
page: string;
|
||||||
|
i18nKey: string;
|
||||||
|
icon: string;
|
||||||
|
localIcon: string;
|
||||||
|
order: string;
|
||||||
|
keepAlive: string;
|
||||||
|
href: string;
|
||||||
|
hideInMenu: string;
|
||||||
|
activeMenu: string;
|
||||||
|
multiTab: string;
|
||||||
|
fixedInTab: string;
|
||||||
|
fixedIndexInTab: string;
|
||||||
|
queryKey: string;
|
||||||
|
queryValue: string;
|
||||||
|
button: string;
|
||||||
|
buttonCode: string;
|
||||||
|
buttonDesc: string;
|
||||||
|
menuStatus: string;
|
||||||
|
};
|
||||||
|
addMenu: string;
|
||||||
|
editMenu: string;
|
||||||
|
addChildMenu: string;
|
||||||
|
type: {
|
||||||
|
directory: string;
|
||||||
|
menu: string;
|
||||||
|
};
|
||||||
|
iconType: {
|
||||||
|
iconify: string;
|
||||||
|
local: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
form: {
|
form: {
|
||||||
required: string;
|
required: string;
|
||||||
|
|||||||
44
src/typings/components.d.ts
vendored
44
src/typings/components.d.ts
vendored
@@ -16,32 +16,45 @@ declare module 'vue' {
|
|||||||
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
||||||
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
||||||
CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
||||||
|
CustomIconSelect: typeof import('./../components/custom/custom-icon-select.vue')['default']
|
||||||
DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
||||||
ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
|
ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
|
||||||
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
||||||
|
GithubLink: typeof import('./../components/custom/github-link.vue')['default']
|
||||||
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
||||||
IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
||||||
|
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
|
||||||
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||||
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||||
|
IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
|
||||||
|
IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
|
||||||
|
IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
|
||||||
|
IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
||||||
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||||
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
||||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||||
|
IconMdiDrag: typeof import('~icons/mdi/drag')['default']
|
||||||
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
||||||
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
||||||
|
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||||
IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
||||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||||
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
||||||
NAlert: typeof import('naive-ui')['NAlert']
|
|
||||||
NBadge: typeof import('naive-ui')['NBadge']
|
NBadge: typeof import('naive-ui')['NBadge']
|
||||||
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
||||||
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
NCard: typeof import('naive-ui')['NCard']
|
NCard: typeof import('naive-ui')['NCard']
|
||||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
|
NCollapse: typeof import('naive-ui')['NCollapse']
|
||||||
|
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
||||||
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||||
|
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||||
|
NDescriptions: typeof import('naive-ui')['NDescriptions']
|
||||||
|
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
|
||||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||||
NDivider: typeof import('naive-ui')['NDivider']
|
NDivider: typeof import('naive-ui')['NDivider']
|
||||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||||
@@ -50,6 +63,7 @@ declare module 'vue' {
|
|||||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||||
NForm: typeof import('naive-ui')['NForm']
|
NForm: typeof import('naive-ui')['NForm']
|
||||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||||
|
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
||||||
NGi: typeof import('naive-ui')['NGi']
|
NGi: typeof import('naive-ui')['NGi']
|
||||||
NGrid: typeof import('naive-ui')['NGrid']
|
NGrid: typeof import('naive-ui')['NGrid']
|
||||||
NInput: typeof import('naive-ui')['NInput']
|
NInput: typeof import('naive-ui')['NInput']
|
||||||
@@ -62,7 +76,10 @@ declare module 'vue' {
|
|||||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||||
|
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
||||||
NPopover: typeof import('naive-ui')['NPopover']
|
NPopover: typeof import('naive-ui')['NPopover']
|
||||||
|
NRadio: typeof import('naive-ui')['NRadio']
|
||||||
|
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||||
NSelect: typeof import('naive-ui')['NSelect']
|
NSelect: typeof import('naive-ui')['NSelect']
|
||||||
NSpace: typeof import('naive-ui')['NSpace']
|
NSpace: typeof import('naive-ui')['NSpace']
|
||||||
@@ -70,6 +87,7 @@ declare module 'vue' {
|
|||||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||||
NTab: typeof import('naive-ui')['NTab']
|
NTab: typeof import('naive-ui')['NTab']
|
||||||
NTabs: typeof import('naive-ui')['NTabs']
|
NTabs: typeof import('naive-ui')['NTabs']
|
||||||
|
NTag: typeof import('naive-ui')['NTag']
|
||||||
NThing: typeof import('naive-ui')['NThing']
|
NThing: typeof import('naive-ui')['NThing']
|
||||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||||
NWatermark: typeof import('naive-ui')['NWatermark']
|
NWatermark: typeof import('naive-ui')['NWatermark']
|
||||||
@@ -84,6 +102,7 @@ declare module 'vue' {
|
|||||||
TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
|
TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
|
||||||
ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
||||||
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
||||||
|
WebSiteLink: typeof import('./../components/custom/web-site-link.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,32 +112,45 @@ declare global {
|
|||||||
const BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
const BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
||||||
const ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
const ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
||||||
const CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
const CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
||||||
|
const CustomIconSelect: typeof import('./../components/custom/custom-icon-select.vue')['default']
|
||||||
const DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
const DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
||||||
const ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
|
const ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
|
||||||
const FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
const FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
||||||
|
const GithubLink: typeof import('./../components/custom/github-link.vue')['default']
|
||||||
const IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
const IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
||||||
const IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
const IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
||||||
|
const IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
|
||||||
const IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
const IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||||
const IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
const IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||||
|
const IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
|
||||||
|
const IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
|
||||||
|
const IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
|
||||||
|
const IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
||||||
const IconLocalBanner: typeof import('~icons/local/banner')['default']
|
const IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||||
const IconLocalLogo: typeof import('~icons/local/logo')['default']
|
const IconLocalLogo: typeof import('~icons/local/logo')['default']
|
||||||
const IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
const IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||||
const IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
const IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||||
|
const IconMdiDrag: typeof import('~icons/mdi/drag')['default']
|
||||||
const IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
const IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
||||||
const IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
const IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
||||||
|
const IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||||
const IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
const IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
||||||
const IconUilSearch: typeof import('~icons/uil/search')['default']
|
const IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||||
const LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
const LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||||
const LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
const LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||||
const MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
const MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
||||||
const NAlert: typeof import('naive-ui')['NAlert']
|
|
||||||
const NBadge: typeof import('naive-ui')['NBadge']
|
const NBadge: typeof import('naive-ui')['NBadge']
|
||||||
const NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
const NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
||||||
const NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
const NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
||||||
const NButton: typeof import('naive-ui')['NButton']
|
const NButton: typeof import('naive-ui')['NButton']
|
||||||
const NCard: typeof import('naive-ui')['NCard']
|
const NCard: typeof import('naive-ui')['NCard']
|
||||||
const NCheckbox: typeof import('naive-ui')['NCheckbox']
|
const NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
|
const NCollapse: typeof import('naive-ui')['NCollapse']
|
||||||
|
const NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
||||||
const NColorPicker: typeof import('naive-ui')['NColorPicker']
|
const NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||||
|
const NDataTable: typeof import('naive-ui')['NDataTable']
|
||||||
|
const NDescriptions: typeof import('naive-ui')['NDescriptions']
|
||||||
|
const NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
|
||||||
const NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
const NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||||
const NDivider: typeof import('naive-ui')['NDivider']
|
const NDivider: typeof import('naive-ui')['NDivider']
|
||||||
const NDrawer: typeof import('naive-ui')['NDrawer']
|
const NDrawer: typeof import('naive-ui')['NDrawer']
|
||||||
@@ -127,6 +159,7 @@ declare global {
|
|||||||
const NEmpty: typeof import('naive-ui')['NEmpty']
|
const NEmpty: typeof import('naive-ui')['NEmpty']
|
||||||
const NForm: typeof import('naive-ui')['NForm']
|
const NForm: typeof import('naive-ui')['NForm']
|
||||||
const NFormItem: typeof import('naive-ui')['NFormItem']
|
const NFormItem: typeof import('naive-ui')['NFormItem']
|
||||||
|
const NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
||||||
const NGi: typeof import('naive-ui')['NGi']
|
const NGi: typeof import('naive-ui')['NGi']
|
||||||
const NGrid: typeof import('naive-ui')['NGrid']
|
const NGrid: typeof import('naive-ui')['NGrid']
|
||||||
const NInput: typeof import('naive-ui')['NInput']
|
const NInput: typeof import('naive-ui')['NInput']
|
||||||
@@ -139,7 +172,10 @@ declare global {
|
|||||||
const NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
const NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
const NModal: typeof import('naive-ui')['NModal']
|
const NModal: typeof import('naive-ui')['NModal']
|
||||||
const NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
const NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||||
|
const NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
||||||
const NPopover: typeof import('naive-ui')['NPopover']
|
const NPopover: typeof import('naive-ui')['NPopover']
|
||||||
|
const NRadio: typeof import('naive-ui')['NRadio']
|
||||||
|
const NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
||||||
const NScrollbar: typeof import('naive-ui')['NScrollbar']
|
const NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||||
const NSelect: typeof import('naive-ui')['NSelect']
|
const NSelect: typeof import('naive-ui')['NSelect']
|
||||||
const NSpace: typeof import('naive-ui')['NSpace']
|
const NSpace: typeof import('naive-ui')['NSpace']
|
||||||
@@ -147,6 +183,7 @@ declare global {
|
|||||||
const NSwitch: typeof import('naive-ui')['NSwitch']
|
const NSwitch: typeof import('naive-ui')['NSwitch']
|
||||||
const NTab: typeof import('naive-ui')['NTab']
|
const NTab: typeof import('naive-ui')['NTab']
|
||||||
const NTabs: typeof import('naive-ui')['NTabs']
|
const NTabs: typeof import('naive-ui')['NTabs']
|
||||||
|
const NTag: typeof import('naive-ui')['NTag']
|
||||||
const NThing: typeof import('naive-ui')['NThing']
|
const NThing: typeof import('naive-ui')['NThing']
|
||||||
const NTooltip: typeof import('naive-ui')['NTooltip']
|
const NTooltip: typeof import('naive-ui')['NTooltip']
|
||||||
const NWatermark: typeof import('naive-ui')['NWatermark']
|
const NWatermark: typeof import('naive-ui')['NWatermark']
|
||||||
@@ -161,4 +198,5 @@ declare global {
|
|||||||
const TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
|
const TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
|
||||||
const ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
const ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
||||||
const WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
const WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
||||||
}
|
const WebSiteLink: typeof import('./../components/custom/web-site-link.vue')['default']
|
||||||
|
}
|
||||||
|
|||||||
153
src/typings/elegant-router.d.ts
vendored
153
src/typings/elegant-router.d.ts
vendored
@@ -17,12 +17,85 @@ declare module "@elegant-router/types" {
|
|||||||
export type RouteMap = {
|
export type RouteMap = {
|
||||||
"root": "/";
|
"root": "/";
|
||||||
"not-found": "/:pathMatch(.*)*";
|
"not-found": "/:pathMatch(.*)*";
|
||||||
|
"exception": "/exception";
|
||||||
|
"exception_403": "/exception/403";
|
||||||
|
"exception_404": "/exception/404";
|
||||||
|
"exception_500": "/exception/500";
|
||||||
|
"document": "/document";
|
||||||
|
"document_project": "/document/project";
|
||||||
|
"document_project-link": "/document/project-link";
|
||||||
|
"document_video": "/document/video";
|
||||||
|
"document_vue": "/document/vue";
|
||||||
|
"document_vite": "/document/vite";
|
||||||
|
"document_unocss": "/document/unocss";
|
||||||
|
"document_naive": "/document/naive";
|
||||||
|
"document_pro-naive": "/document/pro-naive";
|
||||||
|
"document_antd": "/document/antd";
|
||||||
|
"document_alova": "/document/alova";
|
||||||
"403": "/403";
|
"403": "/403";
|
||||||
"404": "/404";
|
"404": "/404";
|
||||||
"500": "/500";
|
"500": "/500";
|
||||||
|
"about": "/about";
|
||||||
|
"alova": "/alova";
|
||||||
|
"alova_request": "/alova/request";
|
||||||
|
"alova_scenes": "/alova/scenes";
|
||||||
|
"function": "/function";
|
||||||
|
"function_hide-child": "/function/hide-child";
|
||||||
|
"function_hide-child_one": "/function/hide-child/one";
|
||||||
|
"function_hide-child_three": "/function/hide-child/three";
|
||||||
|
"function_hide-child_two": "/function/hide-child/two";
|
||||||
|
"function_multi-tab": "/function/multi-tab";
|
||||||
|
"function_request": "/function/request";
|
||||||
|
"function_super-page": "/function/super-page";
|
||||||
|
"function_tab": "/function/tab";
|
||||||
|
"function_toggle-auth": "/function/toggle-auth";
|
||||||
"home": "/home";
|
"home": "/home";
|
||||||
"iframe-page": "/iframe-page/:url";
|
"iframe-page": "/iframe-page/:url";
|
||||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
|
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
|
||||||
|
"manage": "/manage";
|
||||||
|
"manage_menu": "/manage/menu";
|
||||||
|
"manage_role": "/manage/role";
|
||||||
|
"manage_user": "/manage/user";
|
||||||
|
"manage_user-detail": "/manage/user-detail/:id";
|
||||||
|
"multi-menu": "/multi-menu";
|
||||||
|
"multi-menu_first": "/multi-menu/first";
|
||||||
|
"multi-menu_first_child": "/multi-menu/first/child";
|
||||||
|
"multi-menu_second": "/multi-menu/second";
|
||||||
|
"multi-menu_second_child": "/multi-menu/second/child";
|
||||||
|
"multi-menu_second_child_home": "/multi-menu/second/child/home";
|
||||||
|
"plugin": "/plugin";
|
||||||
|
"plugin_barcode": "/plugin/barcode";
|
||||||
|
"plugin_charts": "/plugin/charts";
|
||||||
|
"plugin_charts_antv": "/plugin/charts/antv";
|
||||||
|
"plugin_charts_echarts": "/plugin/charts/echarts";
|
||||||
|
"plugin_charts_vchart": "/plugin/charts/vchart";
|
||||||
|
"plugin_copy": "/plugin/copy";
|
||||||
|
"plugin_editor": "/plugin/editor";
|
||||||
|
"plugin_editor_markdown": "/plugin/editor/markdown";
|
||||||
|
"plugin_editor_quill": "/plugin/editor/quill";
|
||||||
|
"plugin_excel": "/plugin/excel";
|
||||||
|
"plugin_gantt": "/plugin/gantt";
|
||||||
|
"plugin_gantt_dhtmlx": "/plugin/gantt/dhtmlx";
|
||||||
|
"plugin_gantt_vtable": "/plugin/gantt/vtable";
|
||||||
|
"plugin_icon": "/plugin/icon";
|
||||||
|
"plugin_map": "/plugin/map";
|
||||||
|
"plugin_pdf": "/plugin/pdf";
|
||||||
|
"plugin_pinyin": "/plugin/pinyin";
|
||||||
|
"plugin_print": "/plugin/print";
|
||||||
|
"plugin_swiper": "/plugin/swiper";
|
||||||
|
"plugin_tables": "/plugin/tables";
|
||||||
|
"plugin_tables_vtable": "/plugin/tables/vtable";
|
||||||
|
"plugin_typeit": "/plugin/typeit";
|
||||||
|
"plugin_video": "/plugin/video";
|
||||||
|
"pro-naive": "/pro-naive";
|
||||||
|
"pro-naive_form": "/pro-naive/form";
|
||||||
|
"pro-naive_form_basic": "/pro-naive/form/basic";
|
||||||
|
"pro-naive_form_query": "/pro-naive/form/query";
|
||||||
|
"pro-naive_form_step": "/pro-naive/form/step";
|
||||||
|
"pro-naive_table": "/pro-naive/table";
|
||||||
|
"pro-naive_table_remote": "/pro-naive/table/remote";
|
||||||
|
"pro-naive_table_row-edit": "/pro-naive/table/row-edit";
|
||||||
|
"user-center": "/user-center";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,6 +115,21 @@ declare module "@elegant-router/types" {
|
|||||||
RouteKey,
|
RouteKey,
|
||||||
| "root"
|
| "root"
|
||||||
| "not-found"
|
| "not-found"
|
||||||
|
| "exception"
|
||||||
|
| "exception_403"
|
||||||
|
| "exception_404"
|
||||||
|
| "exception_500"
|
||||||
|
| "document"
|
||||||
|
| "document_project"
|
||||||
|
| "document_project-link"
|
||||||
|
| "document_video"
|
||||||
|
| "document_vue"
|
||||||
|
| "document_vite"
|
||||||
|
| "document_unocss"
|
||||||
|
| "document_naive"
|
||||||
|
| "document_pro-naive"
|
||||||
|
| "document_antd"
|
||||||
|
| "document_alova"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,9 +145,17 @@ declare module "@elegant-router/types" {
|
|||||||
| "403"
|
| "403"
|
||||||
| "404"
|
| "404"
|
||||||
| "500"
|
| "500"
|
||||||
|
| "about"
|
||||||
|
| "alova"
|
||||||
|
| "function"
|
||||||
| "home"
|
| "home"
|
||||||
| "iframe-page"
|
| "iframe-page"
|
||||||
| "login"
|
| "login"
|
||||||
|
| "manage"
|
||||||
|
| "multi-menu"
|
||||||
|
| "plugin"
|
||||||
|
| "pro-naive"
|
||||||
|
| "user-center"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,6 +165,8 @@ declare module "@elegant-router/types" {
|
|||||||
CustomRouteKey,
|
CustomRouteKey,
|
||||||
| "root"
|
| "root"
|
||||||
| "not-found"
|
| "not-found"
|
||||||
|
| "exception"
|
||||||
|
| "document"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,7 +179,49 @@ declare module "@elegant-router/types" {
|
|||||||
| "500"
|
| "500"
|
||||||
| "iframe-page"
|
| "iframe-page"
|
||||||
| "login"
|
| "login"
|
||||||
|
| "about"
|
||||||
|
| "alova_request"
|
||||||
|
| "alova_scenes"
|
||||||
|
| "function_hide-child_one"
|
||||||
|
| "function_hide-child_three"
|
||||||
|
| "function_hide-child_two"
|
||||||
|
| "function_multi-tab"
|
||||||
|
| "function_request"
|
||||||
|
| "function_super-page"
|
||||||
|
| "function_tab"
|
||||||
|
| "function_toggle-auth"
|
||||||
| "home"
|
| "home"
|
||||||
|
| "manage_menu"
|
||||||
|
| "manage_role"
|
||||||
|
| "manage_user-detail"
|
||||||
|
| "manage_user"
|
||||||
|
| "multi-menu_first_child"
|
||||||
|
| "multi-menu_second_child_home"
|
||||||
|
| "plugin_barcode"
|
||||||
|
| "plugin_charts_antv"
|
||||||
|
| "plugin_charts_echarts"
|
||||||
|
| "plugin_charts_vchart"
|
||||||
|
| "plugin_copy"
|
||||||
|
| "plugin_editor_markdown"
|
||||||
|
| "plugin_editor_quill"
|
||||||
|
| "plugin_excel"
|
||||||
|
| "plugin_gantt_dhtmlx"
|
||||||
|
| "plugin_gantt_vtable"
|
||||||
|
| "plugin_icon"
|
||||||
|
| "plugin_map"
|
||||||
|
| "plugin_pdf"
|
||||||
|
| "plugin_pinyin"
|
||||||
|
| "plugin_print"
|
||||||
|
| "plugin_swiper"
|
||||||
|
| "plugin_tables_vtable"
|
||||||
|
| "plugin_typeit"
|
||||||
|
| "plugin_video"
|
||||||
|
| "pro-naive_form_basic"
|
||||||
|
| "pro-naive_form_query"
|
||||||
|
| "pro-naive_form_step"
|
||||||
|
| "pro-naive_table_remote"
|
||||||
|
| "pro-naive_table_row-edit"
|
||||||
|
| "user-center"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,6 +231,19 @@ declare module "@elegant-router/types" {
|
|||||||
CustomRouteKey,
|
CustomRouteKey,
|
||||||
| "root"
|
| "root"
|
||||||
| "not-found"
|
| "not-found"
|
||||||
|
| "exception_403"
|
||||||
|
| "exception_404"
|
||||||
|
| "exception_500"
|
||||||
|
| "document_project"
|
||||||
|
| "document_project-link"
|
||||||
|
| "document_video"
|
||||||
|
| "document_vue"
|
||||||
|
| "document_vite"
|
||||||
|
| "document_unocss"
|
||||||
|
| "document_naive"
|
||||||
|
| "document_pro-naive"
|
||||||
|
| "document_antd"
|
||||||
|
| "document_alova"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
20
src/typings/package.d.ts
vendored
Normal file
20
src/typings/package.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/// <reference types="@amap/amap-jsapi-types" />
|
||||||
|
/// <reference types="bmapgl" />
|
||||||
|
|
||||||
|
declare namespace BMap {
|
||||||
|
class Map extends BMapGL.Map {}
|
||||||
|
class Point extends BMapGL.Point {}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const TMap: any;
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
/**
|
||||||
|
* make baidu map request under https protocol
|
||||||
|
*
|
||||||
|
* - 0: http
|
||||||
|
* - 1: https
|
||||||
|
* - 2: https
|
||||||
|
*/
|
||||||
|
HOST_TYPE: '0' | '1' | '2';
|
||||||
|
}
|
||||||
85
src/views/about/index.vue
Normal file
85
src/views/about/index.vue
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import pkg from '~/package.json';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const column = computed(() => (appStore.isMobile ? 1 : 2));
|
||||||
|
|
||||||
|
interface PkgJson {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
dependencies: PkgVersionInfo[];
|
||||||
|
devDependencies: PkgVersionInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PkgVersionInfo {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, version, dependencies, devDependencies } = pkg;
|
||||||
|
|
||||||
|
function transformVersionData(tuple: [string, string]): PkgVersionInfo {
|
||||||
|
const [$name, $version] = tuple;
|
||||||
|
return {
|
||||||
|
name: $name,
|
||||||
|
version: $version
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkgJson: PkgJson = {
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
dependencies: Object.entries(dependencies).map(item => transformVersionData(item)),
|
||||||
|
devDependencies: Object.entries(devDependencies).map(item => transformVersionData(item))
|
||||||
|
};
|
||||||
|
|
||||||
|
const latestBuildTime = BUILD_TIME;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical :size="16">
|
||||||
|
<NCard :title="$t('page.about.title')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<p>{{ $t('page.about.introduction') }}</p>
|
||||||
|
</NCard>
|
||||||
|
<NCard :title="$t('page.about.projectInfo.title')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NDescriptions label-placement="left" bordered size="small" :column="column">
|
||||||
|
<NDescriptionsItem :label="$t('page.about.projectInfo.version')">
|
||||||
|
<NTag type="primary">{{ pkgJson.version }}</NTag>
|
||||||
|
</NDescriptionsItem>
|
||||||
|
<NDescriptionsItem :label="$t('page.about.projectInfo.latestBuildTime')">
|
||||||
|
<NTag type="primary">{{ latestBuildTime }}</NTag>
|
||||||
|
</NDescriptionsItem>
|
||||||
|
<NDescriptionsItem :label="$t('page.about.projectInfo.githubLink')">
|
||||||
|
<a class="text-primary" :href="pkg.homepage" target="_blank" rel="noopener noreferrer">
|
||||||
|
{{ $t('page.about.projectInfo.githubLink') }}
|
||||||
|
</a>
|
||||||
|
</NDescriptionsItem>
|
||||||
|
<NDescriptionsItem :label="$t('page.about.projectInfo.previewLink')">
|
||||||
|
<a class="text-primary" :href="pkg.website" target="_blank" rel="noopener noreferrer">
|
||||||
|
{{ $t('page.about.projectInfo.previewLink') }}
|
||||||
|
</a>
|
||||||
|
</NDescriptionsItem>
|
||||||
|
</NDescriptions>
|
||||||
|
</NCard>
|
||||||
|
<NCard :title="$t('page.about.prdDep')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NDescriptions label-placement="left" bordered size="small" :column="column">
|
||||||
|
<NDescriptionsItem v-for="item in pkgJson.dependencies" :key="item.name" :label="item.name">
|
||||||
|
{{ item.version }}
|
||||||
|
</NDescriptionsItem>
|
||||||
|
</NDescriptions>
|
||||||
|
</NCard>
|
||||||
|
<NCard :title="$t('page.about.devDep')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NDescriptions label-placement="left" bordered size="small" :column="column">
|
||||||
|
<NDescriptionsItem v-for="item in pkgJson.devDependencies" :key="item.name" :label="item.name">
|
||||||
|
{{ item.version }}
|
||||||
|
</NDescriptionsItem>
|
||||||
|
</NDescriptions>
|
||||||
|
</NCard>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
63
src/views/alova/request/index.vue
Normal file
63
src/views/alova/request/index.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import { fetchCustomBackendError } from '@/service-alova/api';
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
await fetchCustomBackendError('8888', $t('request.logoutMsg'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logoutWithModal() {
|
||||||
|
await fetchCustomBackendError('7777', $t('request.logoutWithModalMsg'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshToken() {
|
||||||
|
await fetchCustomBackendError('9999', $t('request.tokenExpired'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRepeatedMessageError() {
|
||||||
|
await Promise.all([
|
||||||
|
fetchCustomBackendError('2222', $t('page.function.request.repeatedErrorMsg1')),
|
||||||
|
fetchCustomBackendError('2222', $t('page.function.request.repeatedErrorMsg1')),
|
||||||
|
fetchCustomBackendError('2222', $t('page.function.request.repeatedErrorMsg1')),
|
||||||
|
fetchCustomBackendError('3333', $t('page.function.request.repeatedErrorMsg2')),
|
||||||
|
fetchCustomBackendError('3333', $t('page.function.request.repeatedErrorMsg2')),
|
||||||
|
fetchCustomBackendError('3333', $t('page.function.request.repeatedErrorMsg2'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRepeatedModalError() {
|
||||||
|
await Promise.all([
|
||||||
|
fetchCustomBackendError('7777', $t('request.logoutWithModalMsg')),
|
||||||
|
fetchCustomBackendError('7777', $t('request.logoutWithModalMsg')),
|
||||||
|
fetchCustomBackendError('7777', $t('request.logoutWithModalMsg'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical :size="16">
|
||||||
|
<NCard :title="$t('request.logout')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NButton @click="logout">{{ $t('common.trigger') }}</NButton>
|
||||||
|
</NCard>
|
||||||
|
<NCard :title="$t('request.logoutWithModal')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NButton @click="logoutWithModal">{{ $t('common.trigger') }}</NButton>
|
||||||
|
</NCard>
|
||||||
|
<NCard :title="$t('request.refreshToken')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NButton @click="refreshToken">{{ $t('common.trigger') }}</NButton>
|
||||||
|
</NCard>
|
||||||
|
<NCard
|
||||||
|
:title="$t('page.function.request.repeatedErrorOccurOnce')"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
segmented
|
||||||
|
class="card-wrapper"
|
||||||
|
>
|
||||||
|
<NButton @click="handleRepeatedMessageError">{{ $t('page.function.request.repeatedError') }}(Message)</NButton>
|
||||||
|
<NButton class="ml-12px" @click="handleRepeatedModalError">
|
||||||
|
{{ $t('page.function.request.repeatedError') }}(Modal)
|
||||||
|
</NButton>
|
||||||
|
</NCard>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
34
src/views/alova/scenes/index.vue
Normal file
34
src/views/alova/scenes/index.vue
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import CaptchaVerification from './modules/captcha-verification.vue';
|
||||||
|
import BrowserVisibilityRequest from './modules/browser-visibility-request.vue';
|
||||||
|
import PollingRequest from './modules/polling-request.vue';
|
||||||
|
import NetworkToggleRequest from './modules/network-toggle-request.vue';
|
||||||
|
import CrossComponentRequest from './modules/cross-component-request.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical :size="16">
|
||||||
|
<NCard :title="$t('page.alova.scenes.captchaSend')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<CaptchaVerification class="w-1/3" />
|
||||||
|
</NCard>
|
||||||
|
<NCard :title="$t('page.alova.scenes.autoRequest')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NSpace :wrap="false">
|
||||||
|
<BrowserVisibilityRequest />
|
||||||
|
<PollingRequest />
|
||||||
|
<NetworkToggleRequest />
|
||||||
|
</NSpace>
|
||||||
|
</NCard>
|
||||||
|
<NCard
|
||||||
|
:title="$t('page.alova.scenes.requestCrossComponent')"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
segmented
|
||||||
|
class="card-wrapper"
|
||||||
|
>
|
||||||
|
<CrossComponentRequest />
|
||||||
|
</NCard>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { actionDelegationMiddleware, useAutoRequest } from '@sa/alova/client';
|
||||||
|
import { alova } from '@/service-alova/request';
|
||||||
|
|
||||||
|
const getLastTime = alova.Get<{ time: string }>('/mock/getLastTime', { cacheFor: null });
|
||||||
|
const isStop = ref(false);
|
||||||
|
const { loading, data } = useAutoRequest(getLastTime, {
|
||||||
|
enableVisibility: true,
|
||||||
|
enableNetwork: false,
|
||||||
|
enableFocus: false,
|
||||||
|
initialData: {
|
||||||
|
time: ''
|
||||||
|
},
|
||||||
|
async middleware(_, next) {
|
||||||
|
await actionDelegationMiddleware('autoRequest:1')(_, () => Promise.resolve());
|
||||||
|
if (!isStop.value) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleStop = () => {
|
||||||
|
isStop.value = !isStop.value;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical>
|
||||||
|
<NAlert type="info">
|
||||||
|
{{ $t('page.alova.scenes.visibilityRequestTips') }}
|
||||||
|
</NAlert>
|
||||||
|
<NButton type="primary" @click="toggleStop">
|
||||||
|
<icon-carbon-play v-if="isStop" class="mr-2" />
|
||||||
|
<icon-carbon-stop v-else class="mr-2" />
|
||||||
|
{{ isStop ? $t('page.alova.scenes.startRequest') : $t('page.alova.scenes.stopRequest') }}
|
||||||
|
</NButton>
|
||||||
|
<NSpace align="center">
|
||||||
|
<span>{{ $t('page.alova.scenes.refreshTime') }}: {{ data.time || '--' }}</span>
|
||||||
|
<NSpin v-if="loading" :size="12" />
|
||||||
|
</NSpace>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
71
src/views/alova/scenes/modules/captcha-verification.vue
Normal file
71
src/views/alova/scenes/modules/captcha-verification.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { actionDelegationMiddleware, useCaptcha, useForm } from '@sa/alova/client';
|
||||||
|
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import { sendCaptcha, verifyCaptcha } from '@/service-alova/api';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'CaptchaVerification'
|
||||||
|
});
|
||||||
|
|
||||||
|
const { loading, send, countdown } = useCaptcha(sendCaptcha, {
|
||||||
|
middleware: actionDelegationMiddleware('captcha:send')
|
||||||
|
});
|
||||||
|
const label = computed(() => {
|
||||||
|
return countdown.value > 0
|
||||||
|
? $t('page.login.codeLogin.reGetCode', { time: countdown.value })
|
||||||
|
: $t('page.login.codeLogin.getCode');
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
form,
|
||||||
|
loading: submiting,
|
||||||
|
send: submit
|
||||||
|
} = useForm(formData => verifyCaptcha(formData.phone, formData.code), {
|
||||||
|
initialForm: {
|
||||||
|
phone: '',
|
||||||
|
code: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { formRef, validate } = useNaiveForm();
|
||||||
|
|
||||||
|
const rules = computed<Record<keyof typeof form.value, App.Global.FormRule[]>>(() => {
|
||||||
|
const { formRules } = useFormRules();
|
||||||
|
|
||||||
|
return {
|
||||||
|
phone: formRules.phone,
|
||||||
|
code: formRules.code
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await validate();
|
||||||
|
await submit();
|
||||||
|
// request
|
||||||
|
window.$message?.success($t('page.login.common.validateSuccess'));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NForm ref="formRef" :model="form" :rules="rules" size="large" :show-label="false" @keyup.enter="handleSubmit">
|
||||||
|
<NFormItem path="phone">
|
||||||
|
<NInput v-model:value="form.phone" :placeholder="$t('page.login.common.phonePlaceholder')" :maxlength="11" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem path="code">
|
||||||
|
<div class="w-full flex-y-center gap-16px">
|
||||||
|
<NInput v-model:value="form.code" :placeholder="$t('page.login.common.codePlaceholder')" />
|
||||||
|
<NButton size="large" :disabled="countdown > 0" :loading="loading" @click="send(form.phone)">
|
||||||
|
{{ label }}
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
</NFormItem>
|
||||||
|
<NSpace vertical :size="18" class="w-full">
|
||||||
|
<NButton type="primary" size="large" round block :loading="submiting" @click="handleSubmit">
|
||||||
|
{{ $t('common.confirm') }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</NForm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
13
src/views/alova/scenes/modules/cross-component-request.vue
Normal file
13
src/views/alova/scenes/modules/cross-component-request.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { accessAction } from '@sa/alova/client';
|
||||||
|
|
||||||
|
const handleAutoRequestSend = async () => {
|
||||||
|
accessAction(/^autoRequest/, async ({ send }) => {
|
||||||
|
await send();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NButton @click="handleAutoRequestSend">{{ $t('page.alova.scenes.triggerAllRequest') }}</NButton>
|
||||||
|
</template>
|
||||||
43
src/views/alova/scenes/modules/network-toggle-request.vue
Normal file
43
src/views/alova/scenes/modules/network-toggle-request.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { actionDelegationMiddleware, useAutoRequest } from '@sa/alova/client';
|
||||||
|
import { alova } from '@/service-alova/request';
|
||||||
|
|
||||||
|
const getLastTime = alova.Get<{ time: string }>('/mock/getLastTime', { cacheFor: null });
|
||||||
|
const isStop = ref(false);
|
||||||
|
const { loading, data } = useAutoRequest(getLastTime, {
|
||||||
|
enableVisibility: false,
|
||||||
|
enableNetwork: true,
|
||||||
|
enableFocus: false,
|
||||||
|
initialData: {
|
||||||
|
time: ''
|
||||||
|
},
|
||||||
|
async middleware(_, next) {
|
||||||
|
await actionDelegationMiddleware('autoRequest:2')(_, () => Promise.resolve());
|
||||||
|
if (!isStop.value) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleStop = () => {
|
||||||
|
isStop.value = !isStop.value;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical>
|
||||||
|
<NAlert type="info">
|
||||||
|
{{ $t('page.alova.scenes.networkRequestTips') }}
|
||||||
|
</NAlert>
|
||||||
|
<NButton type="primary" @click="toggleStop">
|
||||||
|
<icon-carbon-play v-if="isStop" class="mr-2" />
|
||||||
|
<icon-carbon-stop v-else class="mr-2" />
|
||||||
|
{{ isStop ? $t('page.alova.scenes.startRequest') : $t('page.alova.scenes.stopRequest') }}
|
||||||
|
</NButton>
|
||||||
|
<NSpace align="center">
|
||||||
|
<span>{{ $t('page.alova.scenes.refreshTime') }}: {{ data.time || '--' }}</span>
|
||||||
|
<NSpin v-if="loading" :size="12" />
|
||||||
|
</NSpace>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
41
src/views/alova/scenes/modules/polling-request.vue
Normal file
41
src/views/alova/scenes/modules/polling-request.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { actionDelegationMiddleware, useAutoRequest } from '@sa/alova/client';
|
||||||
|
import { alova } from '@/service-alova/request';
|
||||||
|
|
||||||
|
const getLastTime = alova.Get<{ time: string }>('/mock/getLastTime', { cacheFor: null });
|
||||||
|
const isStop = ref(false);
|
||||||
|
const { loading, data } = useAutoRequest(getLastTime, {
|
||||||
|
pollingTime: 3000,
|
||||||
|
initialData: {
|
||||||
|
time: ''
|
||||||
|
},
|
||||||
|
async middleware(_, next) {
|
||||||
|
await actionDelegationMiddleware('autoRequest:3')(_, () => Promise.resolve());
|
||||||
|
if (!isStop.value) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleStop = () => {
|
||||||
|
isStop.value = !isStop.value;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical>
|
||||||
|
<NAlert type="info">
|
||||||
|
{{ $t('page.alova.scenes.pollingRequestTips') }}
|
||||||
|
</NAlert>
|
||||||
|
<NButton type="primary" @click="toggleStop">
|
||||||
|
<icon-carbon-play v-if="isStop" class="mr-2" />
|
||||||
|
<icon-carbon-stop v-else class="mr-2" />
|
||||||
|
{{ isStop ? $t('page.alova.scenes.startRequest') : $t('page.alova.scenes.stopRequest') }}
|
||||||
|
</NButton>
|
||||||
|
<NSpace align="center">
|
||||||
|
<span>{{ $t('page.alova.scenes.refreshTime') }}: {{ data.time || '--' }}</span>
|
||||||
|
<NSpin v-if="loading" :size="12" />
|
||||||
|
</NSpace>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
7
src/views/function/hide-child/one/index.vue
Normal file
7
src/views/function/hide-child/one/index.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<LookForward />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
7
src/views/function/hide-child/three/index.vue
Normal file
7
src/views/function/hide-child/three/index.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<LookForward />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
7
src/views/function/hide-child/two/index.vue
Normal file
7
src/views/function/hide-child/two/index.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<LookForward />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
24
src/views/function/multi-tab/index.vue
Normal file
24
src/views/function/multi-tab/index.vue
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { routerPushByKey } = useRouterPush();
|
||||||
|
|
||||||
|
const routeQuery = computed(() => JSON.stringify(route.query));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LookForward>
|
||||||
|
<div>
|
||||||
|
<NButton @click="routerPushByKey('function_tab')">{{ $t('page.function.multiTab.backTab') }}</NButton>
|
||||||
|
<div class="py-24px">{{ $t('page.function.multiTab.routeParam') }}: {{ routeQuery }}</div>
|
||||||
|
</div>
|
||||||
|
</LookForward>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
63
src/views/function/request/index.vue
Normal file
63
src/views/function/request/index.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { fetchCustomBackendError } from '@/service/api';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
await fetchCustomBackendError('8888', $t('request.logoutMsg'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logoutWithModal() {
|
||||||
|
await fetchCustomBackendError('7777', $t('request.logoutWithModalMsg'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshToken() {
|
||||||
|
await fetchCustomBackendError('9999', $t('request.tokenExpired'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRepeatedMessageError() {
|
||||||
|
await Promise.all([
|
||||||
|
fetchCustomBackendError('2222', $t('page.function.request.repeatedErrorMsg1')),
|
||||||
|
fetchCustomBackendError('2222', $t('page.function.request.repeatedErrorMsg1')),
|
||||||
|
fetchCustomBackendError('2222', $t('page.function.request.repeatedErrorMsg1')),
|
||||||
|
fetchCustomBackendError('3333', $t('page.function.request.repeatedErrorMsg2')),
|
||||||
|
fetchCustomBackendError('3333', $t('page.function.request.repeatedErrorMsg2')),
|
||||||
|
fetchCustomBackendError('3333', $t('page.function.request.repeatedErrorMsg2'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRepeatedModalError() {
|
||||||
|
await Promise.all([
|
||||||
|
fetchCustomBackendError('7777', $t('request.logoutWithModalMsg')),
|
||||||
|
fetchCustomBackendError('7777', $t('request.logoutWithModalMsg')),
|
||||||
|
fetchCustomBackendError('7777', $t('request.logoutWithModalMsg'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical :size="16">
|
||||||
|
<NCard :title="$t('request.logout')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NButton @click="logout">{{ $t('common.trigger') }}</NButton>
|
||||||
|
</NCard>
|
||||||
|
<NCard :title="$t('request.logoutWithModal')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NButton @click="logoutWithModal">{{ $t('common.trigger') }}</NButton>
|
||||||
|
</NCard>
|
||||||
|
<NCard :title="$t('request.refreshToken')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NButton @click="refreshToken">{{ $t('common.trigger') }}</NButton>
|
||||||
|
</NCard>
|
||||||
|
<NCard
|
||||||
|
:title="$t('page.function.request.repeatedErrorOccurOnce')"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
segmented
|
||||||
|
class="card-wrapper"
|
||||||
|
>
|
||||||
|
<NButton @click="handleRepeatedMessageError">{{ $t('page.function.request.repeatedError') }}(Message)</NButton>
|
||||||
|
<NButton class="ml-12px" @click="handleRepeatedModalError">
|
||||||
|
{{ $t('page.function.request.repeatedError') }}(Modal)
|
||||||
|
</NButton>
|
||||||
|
</NCard>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
7
src/views/function/super-page/index.vue
Normal file
7
src/views/function/super-page/index.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<LookForward />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
71
src/views/function/tab/index.vue
Normal file
71
src/views/function/tab/index.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useTabStore } from '@/store/modules/tab';
|
||||||
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
const tabStore = useTabStore();
|
||||||
|
const { routerPushByKey } = useRouterPush();
|
||||||
|
|
||||||
|
const tabLabel = ref('');
|
||||||
|
|
||||||
|
function changeTabLabel() {
|
||||||
|
tabStore.setTabLabel(tabLabel.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTabLabel() {
|
||||||
|
tabStore.resetTabLabel();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical :size="16">
|
||||||
|
<NCard
|
||||||
|
:title="$t('page.function.tab.tabOperate.title')"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
segmented
|
||||||
|
class="card-wrapper"
|
||||||
|
>
|
||||||
|
<NDivider title-placement="left">{{ $t('page.function.tab.tabOperate.addTab') }}</NDivider>
|
||||||
|
<NButton @click="routerPushByKey('about')">{{ $t('page.function.tab.tabOperate.addTabDesc') }}</NButton>
|
||||||
|
<NDivider title-placement="left">{{ $t('page.function.tab.tabOperate.closeTab') }}</NDivider>
|
||||||
|
<NSpace>
|
||||||
|
<NButton @click="tabStore.removeActiveTab">
|
||||||
|
{{ $t('page.function.tab.tabOperate.closeCurrentTab') }}
|
||||||
|
</NButton>
|
||||||
|
<NButton @click="tabStore.removeTabByRouteName('about')">
|
||||||
|
{{ $t('page.function.tab.tabOperate.closeAboutTab') }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
<NDivider title-placement="left">{{ $t('page.function.tab.tabOperate.addMultiTab') }}</NDivider>
|
||||||
|
<NSpace>
|
||||||
|
<NButton @click="routerPushByKey('function_multi-tab')">
|
||||||
|
{{ $t('page.function.tab.tabOperate.addMultiTabDesc1') }}
|
||||||
|
</NButton>
|
||||||
|
<NButton @click="routerPushByKey('function_multi-tab', { query: { a: '1' } })">
|
||||||
|
{{ $t('page.function.tab.tabOperate.addMultiTabDesc2') }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</NCard>
|
||||||
|
<NCard
|
||||||
|
:title="$t('page.function.tab.tabTitle.title')"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
segmented
|
||||||
|
class="card-wrapper"
|
||||||
|
>
|
||||||
|
<NDivider title-placement="left">{{ $t('page.function.tab.tabTitle.changeTitle') }}</NDivider>
|
||||||
|
<NInputGroup class="max-w-240px">
|
||||||
|
<NInput v-model:value="tabLabel" />
|
||||||
|
<NButton type="primary" @click="changeTabLabel">{{ $t('page.function.tab.tabTitle.change') }}</NButton>
|
||||||
|
</NInputGroup>
|
||||||
|
<NDivider title-placement="left">{{ $t('page.function.tab.tabTitle.resetTitle') }}</NDivider>
|
||||||
|
<NButton type="error" ghost class="w-80px" @click="resetTabLabel">
|
||||||
|
{{ $t('page.function.tab.tabTitle.reset') }}
|
||||||
|
</NButton>
|
||||||
|
</NCard>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
103
src/views/function/toggle-auth/index.vue
Normal file
103
src/views/function/toggle-auth/index.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useLoading } from '@sa/hooks';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
import { useAuthStore } from '@/store/modules/auth';
|
||||||
|
import { useTabStore } from '@/store/modules/tab';
|
||||||
|
import { useAuth } from '@/hooks/business/auth';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const tabStore = useTabStore();
|
||||||
|
const { hasAuth } = useAuth();
|
||||||
|
const { loading, startLoading, endLoading } = useLoading();
|
||||||
|
|
||||||
|
type AccountKey = 'super' | 'admin' | 'user';
|
||||||
|
|
||||||
|
interface Account {
|
||||||
|
key: AccountKey;
|
||||||
|
label: string;
|
||||||
|
userName: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accounts = computed<Account[]>(() => [
|
||||||
|
{
|
||||||
|
key: 'super',
|
||||||
|
label: $t('page.login.pwdLogin.superAdmin'),
|
||||||
|
userName: 'Super',
|
||||||
|
password: '123456'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'admin',
|
||||||
|
label: $t('page.login.pwdLogin.admin'),
|
||||||
|
userName: 'Admin',
|
||||||
|
password: '123456'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'user',
|
||||||
|
label: $t('page.login.pwdLogin.user'),
|
||||||
|
userName: 'User',
|
||||||
|
password: '123456'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const loginAccount = ref<AccountKey>('super');
|
||||||
|
|
||||||
|
async function handleToggleAccount(account: Account) {
|
||||||
|
loginAccount.value = account.key;
|
||||||
|
|
||||||
|
startLoading();
|
||||||
|
await authStore.login(account.userName, account.password, false);
|
||||||
|
tabStore.initTabStore(route);
|
||||||
|
endLoading();
|
||||||
|
appStore.reloadPage();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical :size="16">
|
||||||
|
<NCard :title="$t('route.function_toggle-auth')" :bordered="false" size="small" segmented class="card-wrapper">
|
||||||
|
<NDescriptions bordered :column="1">
|
||||||
|
<NDescriptionsItem :label="$t('page.manage.user.userRole')">
|
||||||
|
<NSpace>
|
||||||
|
<NTag v-for="role in authStore.userInfo.roles" :key="role">{{ role }}</NTag>
|
||||||
|
</NSpace>
|
||||||
|
</NDescriptionsItem>
|
||||||
|
<NDescriptionsItem ions-item :label="$t('page.function.toggleAuth.toggleAccount')">
|
||||||
|
<NSpace>
|
||||||
|
<NButton
|
||||||
|
v-for="account in accounts"
|
||||||
|
:key="account.key"
|
||||||
|
:loading="loading && loginAccount === account.key"
|
||||||
|
:disabled="loading && loginAccount !== account.key"
|
||||||
|
@click="handleToggleAccount(account)"
|
||||||
|
>
|
||||||
|
{{ account.label }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</NDescriptionsItem>
|
||||||
|
</NDescriptions>
|
||||||
|
</NCard>
|
||||||
|
<NCard
|
||||||
|
:title="$t('page.function.toggleAuth.authHook')"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
segmented
|
||||||
|
class="card-wrapper"
|
||||||
|
>
|
||||||
|
<NSpace>
|
||||||
|
<NButton v-if="hasAuth('B_CODE1')">{{ $t('page.function.toggleAuth.superAdminVisible') }}</NButton>
|
||||||
|
<NButton v-if="hasAuth('B_CODE2')">{{ $t('page.function.toggleAuth.adminVisible') }}</NButton>
|
||||||
|
<NButton v-if="hasAuth('B_CODE3')">
|
||||||
|
{{ $t('page.function.toggleAuth.adminOrUserVisible') }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</NCard>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -15,9 +15,6 @@ const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NSpace vertical :size="16">
|
<NSpace vertical :size="16">
|
||||||
<NAlert :title="$t('common.tip')" type="warning">
|
|
||||||
{{ $t('page.home.branchDesc') }}
|
|
||||||
</NAlert>
|
|
||||||
<HeaderBanner />
|
<HeaderBanner />
|
||||||
<CardData />
|
<CardData />
|
||||||
<NGrid :x-gap="gap" :y-gap="16" responsive="screen" item-responsive>
|
<NGrid :x-gap="gap" :y-gap="16" responsive="screen" item-responsive>
|
||||||
|
|||||||
267
src/views/manage/menu/index.vue
Normal file
267
src/views/manage/menu/index.vue
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
||||||
|
import { useBoolean } from '@sa/hooks';
|
||||||
|
import { yesOrNoRecord } from '@/constants/common';
|
||||||
|
import { enableStatusRecord, menuTypeRecord } from '@/constants/business';
|
||||||
|
import { fetchGetAllPages, fetchGetMenuList } from '@/service/api';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SvgIcon from '@/components/custom/svg-icon.vue';
|
||||||
|
import MenuOperateModal, { type OperateType } from './modules/menu-operate-modal.vue';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const { bool: visible, setTrue: openModal } = useBoolean();
|
||||||
|
|
||||||
|
const wrapperRef = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
const { columns, columnChecks, data, loading, pagination, getData, getDataByPage } = useNaivePaginatedTable({
|
||||||
|
api: () => fetchGetMenuList(),
|
||||||
|
transform: response => defaultTransform(response),
|
||||||
|
columns: () => [
|
||||||
|
{
|
||||||
|
type: 'selection',
|
||||||
|
align: 'center',
|
||||||
|
width: 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'id',
|
||||||
|
title: $t('page.manage.menu.id'),
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'menuType',
|
||||||
|
title: $t('page.manage.menu.menuType'),
|
||||||
|
align: 'center',
|
||||||
|
width: 80,
|
||||||
|
render: row => {
|
||||||
|
const tagMap: Record<Api.SystemManage.MenuType, NaiveUI.ThemeColor> = {
|
||||||
|
1: 'default',
|
||||||
|
2: 'primary'
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = $t(menuTypeRecord[row.menuType]);
|
||||||
|
|
||||||
|
return <NTag type={tagMap[row.menuType]}>{label}</NTag>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'menuName',
|
||||||
|
title: $t('page.manage.menu.menuName'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120,
|
||||||
|
render: row => {
|
||||||
|
const { i18nKey, menuName } = row;
|
||||||
|
|
||||||
|
const label = i18nKey ? $t(i18nKey) : menuName;
|
||||||
|
|
||||||
|
return <span>{label}</span>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'icon',
|
||||||
|
title: $t('page.manage.menu.icon'),
|
||||||
|
align: 'center',
|
||||||
|
width: 60,
|
||||||
|
render: row => {
|
||||||
|
const icon = row.iconType === '1' ? row.icon : undefined;
|
||||||
|
|
||||||
|
const localIcon = row.iconType === '2' ? row.icon : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="flex-center">
|
||||||
|
<SvgIcon icon={icon} localIcon={localIcon} class="text-icon" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'routeName',
|
||||||
|
title: $t('page.manage.menu.routeName'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'routePath',
|
||||||
|
title: $t('page.manage.menu.routePath'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'status',
|
||||||
|
title: $t('page.manage.menu.menuStatus'),
|
||||||
|
align: 'center',
|
||||||
|
width: 80,
|
||||||
|
render: row => {
|
||||||
|
if (row.status === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagMap: Record<Api.Common.EnableStatus, NaiveUI.ThemeColor> = {
|
||||||
|
1: 'success',
|
||||||
|
2: 'warning'
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = $t(enableStatusRecord[row.status]);
|
||||||
|
|
||||||
|
return <NTag type={tagMap[row.status]}>{label}</NTag>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'hideInMenu',
|
||||||
|
title: $t('page.manage.menu.hideInMenu'),
|
||||||
|
align: 'center',
|
||||||
|
width: 80,
|
||||||
|
render: row => {
|
||||||
|
const hide: CommonType.YesOrNo = row.hideInMenu ? 'Y' : 'N';
|
||||||
|
|
||||||
|
const tagMap: Record<CommonType.YesOrNo, NaiveUI.ThemeColor> = {
|
||||||
|
Y: 'error',
|
||||||
|
N: 'default'
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = $t(yesOrNoRecord[hide]);
|
||||||
|
|
||||||
|
return <NTag type={tagMap[hide]}>{label}</NTag>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'parentId',
|
||||||
|
title: $t('page.manage.menu.parentId'),
|
||||||
|
width: 90,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'order',
|
||||||
|
title: $t('page.manage.menu.order'),
|
||||||
|
align: 'center',
|
||||||
|
width: 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'operate',
|
||||||
|
title: $t('common.operate'),
|
||||||
|
align: 'center',
|
||||||
|
width: 230,
|
||||||
|
render: row => (
|
||||||
|
<div class="flex-center justify-end gap-8px">
|
||||||
|
{row.menuType === '1' && (
|
||||||
|
<NButton type="primary" ghost size="small" onClick={() => handleAddChildMenu(row)}>
|
||||||
|
{$t('page.manage.menu.addChildMenu')}
|
||||||
|
</NButton>
|
||||||
|
)}
|
||||||
|
<NButton type="primary" ghost size="small" onClick={() => handleEdit(row)}>
|
||||||
|
{$t('common.edit')}
|
||||||
|
</NButton>
|
||||||
|
<NPopconfirm onPositiveClick={() => handleDelete(row.id)}>
|
||||||
|
{{
|
||||||
|
default: () => $t('common.confirmDelete'),
|
||||||
|
trigger: () => (
|
||||||
|
<NButton type="error" ghost size="small">
|
||||||
|
{$t('common.delete')}
|
||||||
|
</NButton>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NPopconfirm>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, 'id', getData);
|
||||||
|
|
||||||
|
const operateType = ref<OperateType>('add');
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
operateType.value = 'add';
|
||||||
|
openModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleBatchDelete() {
|
||||||
|
// request
|
||||||
|
console.log(checkedRowKeys.value);
|
||||||
|
|
||||||
|
onBatchDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(id: number) {
|
||||||
|
// request
|
||||||
|
console.log(id);
|
||||||
|
|
||||||
|
onDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** the edit menu data or the parent menu data when adding a child menu */
|
||||||
|
const editingData: Ref<Api.SystemManage.Menu | null> = ref(null);
|
||||||
|
|
||||||
|
function handleEdit(item: Api.SystemManage.Menu) {
|
||||||
|
operateType.value = 'edit';
|
||||||
|
editingData.value = { ...item };
|
||||||
|
|
||||||
|
openModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAddChildMenu(item: Api.SystemManage.Menu) {
|
||||||
|
operateType.value = 'addChild';
|
||||||
|
|
||||||
|
editingData.value = { ...item };
|
||||||
|
|
||||||
|
openModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
const allPages = ref<string[]>([]);
|
||||||
|
|
||||||
|
async function getAllPages() {
|
||||||
|
const { data: pages } = await fetchGetAllPages();
|
||||||
|
allPages.value = pages || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
getAllPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
init();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="wrapperRef" class="flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||||
|
<NCard :title="$t('page.manage.menu.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||||
|
<template #header-extra>
|
||||||
|
<TableHeaderOperation
|
||||||
|
v-model:columns="columnChecks"
|
||||||
|
:disabled-delete="checkedRowKeys.length === 0"
|
||||||
|
:loading="loading"
|
||||||
|
@add="handleAdd"
|
||||||
|
@delete="handleBatchDelete"
|
||||||
|
@refresh="getData"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<NDataTable
|
||||||
|
v-model:checked-row-keys="checkedRowKeys"
|
||||||
|
:columns="columns"
|
||||||
|
:data="data"
|
||||||
|
size="small"
|
||||||
|
:flex-height="!appStore.isMobile"
|
||||||
|
:scroll-x="1088"
|
||||||
|
:loading="loading"
|
||||||
|
:row-key="row => row.id"
|
||||||
|
remote
|
||||||
|
:pagination="pagination"
|
||||||
|
class="sm:h-full"
|
||||||
|
/>
|
||||||
|
<MenuOperateModal
|
||||||
|
v-model:visible="visible"
|
||||||
|
:operate-type="operateType"
|
||||||
|
:row-data="editingData"
|
||||||
|
:all-pages="allPages"
|
||||||
|
@submitted="getDataByPage"
|
||||||
|
/>
|
||||||
|
</NCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
467
src/views/manage/menu/modules/menu-operate-modal.vue
Normal file
467
src/views/manage/menu/modules/menu-operate-modal.vue
Normal file
@@ -0,0 +1,467 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import type { SelectOption } from 'naive-ui';
|
||||||
|
import { enableStatusOptions, menuIconTypeOptions, menuTypeOptions } from '@/constants/business';
|
||||||
|
import { fetchGetAllRoles } from '@/service/api';
|
||||||
|
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||||
|
import { getLocalIcons } from '@/utils/icon';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SvgIcon from '@/components/custom/svg-icon.vue';
|
||||||
|
import {
|
||||||
|
getLayoutAndPage,
|
||||||
|
getPathParamFromRoutePath,
|
||||||
|
getRoutePathByRouteName,
|
||||||
|
getRoutePathWithParam,
|
||||||
|
transformLayoutAndPageToComponent
|
||||||
|
} from './shared';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MenuOperateModal'
|
||||||
|
});
|
||||||
|
|
||||||
|
export type OperateType = NaiveUI.TableOperateType | 'addChild';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** the type of operation */
|
||||||
|
operateType: OperateType;
|
||||||
|
/** the edit menu data or the parent menu data when adding a child menu */
|
||||||
|
rowData?: Api.SystemManage.Menu | null;
|
||||||
|
/** all pages */
|
||||||
|
allPages: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'submitted'): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||||
|
const { defaultRequiredRule } = useFormRules();
|
||||||
|
|
||||||
|
const title = computed(() => {
|
||||||
|
const titles: Record<OperateType, string> = {
|
||||||
|
add: $t('page.manage.menu.addMenu'),
|
||||||
|
addChild: $t('page.manage.menu.addChildMenu'),
|
||||||
|
edit: $t('page.manage.menu.editMenu')
|
||||||
|
};
|
||||||
|
return titles[props.operateType];
|
||||||
|
});
|
||||||
|
|
||||||
|
type Model = Pick<
|
||||||
|
Api.SystemManage.Menu,
|
||||||
|
| 'menuType'
|
||||||
|
| 'menuName'
|
||||||
|
| 'routeName'
|
||||||
|
| 'routePath'
|
||||||
|
| 'component'
|
||||||
|
| 'order'
|
||||||
|
| 'i18nKey'
|
||||||
|
| 'icon'
|
||||||
|
| 'iconType'
|
||||||
|
| 'status'
|
||||||
|
| 'parentId'
|
||||||
|
| 'keepAlive'
|
||||||
|
| 'constant'
|
||||||
|
| 'href'
|
||||||
|
| 'hideInMenu'
|
||||||
|
| 'activeMenu'
|
||||||
|
| 'multiTab'
|
||||||
|
| 'fixedIndexInTab'
|
||||||
|
> & {
|
||||||
|
query: NonNullable<Api.SystemManage.Menu['query']>;
|
||||||
|
buttons: NonNullable<Api.SystemManage.Menu['buttons']>;
|
||||||
|
layout: string;
|
||||||
|
page: string;
|
||||||
|
pathParam: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const model = ref(createDefaultModel());
|
||||||
|
|
||||||
|
function createDefaultModel(): Model {
|
||||||
|
return {
|
||||||
|
menuType: '1',
|
||||||
|
menuName: '',
|
||||||
|
routeName: '',
|
||||||
|
routePath: '',
|
||||||
|
pathParam: '',
|
||||||
|
component: '',
|
||||||
|
layout: '',
|
||||||
|
page: '',
|
||||||
|
i18nKey: null,
|
||||||
|
icon: '',
|
||||||
|
iconType: '1',
|
||||||
|
parentId: 0,
|
||||||
|
status: '1',
|
||||||
|
keepAlive: false,
|
||||||
|
constant: false,
|
||||||
|
order: 0,
|
||||||
|
href: null,
|
||||||
|
hideInMenu: false,
|
||||||
|
activeMenu: null,
|
||||||
|
multiTab: false,
|
||||||
|
fixedIndexInTab: null,
|
||||||
|
query: [],
|
||||||
|
buttons: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleKey = Extract<keyof Model, 'menuName' | 'status' | 'routeName' | 'routePath'>;
|
||||||
|
|
||||||
|
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||||
|
menuName: defaultRequiredRule,
|
||||||
|
status: defaultRequiredRule,
|
||||||
|
routeName: defaultRequiredRule,
|
||||||
|
routePath: defaultRequiredRule
|
||||||
|
};
|
||||||
|
|
||||||
|
const disabledMenuType = computed(() => props.operateType === 'edit');
|
||||||
|
|
||||||
|
const localIcons = getLocalIcons();
|
||||||
|
const localIconOptions = localIcons.map<SelectOption>(item => ({
|
||||||
|
label: () => (
|
||||||
|
<div class="flex-y-center gap-16px">
|
||||||
|
<SvgIcon localIcon={item} class="text-icon" />
|
||||||
|
<span>{item}</span>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
value: item
|
||||||
|
}));
|
||||||
|
|
||||||
|
const showLayout = computed(() => model.value.parentId === 0);
|
||||||
|
|
||||||
|
const showPage = computed(() => model.value.menuType === '2');
|
||||||
|
|
||||||
|
const pageOptions = computed(() => {
|
||||||
|
const allPages = [...props.allPages];
|
||||||
|
|
||||||
|
if (model.value.routeName && !allPages.includes(model.value.routeName)) {
|
||||||
|
allPages.unshift(model.value.routeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const opts: CommonType.Option[] = allPages.map(page => ({
|
||||||
|
label: page,
|
||||||
|
value: page
|
||||||
|
}));
|
||||||
|
|
||||||
|
return opts;
|
||||||
|
});
|
||||||
|
|
||||||
|
const layoutOptions: CommonType.Option[] = [
|
||||||
|
{
|
||||||
|
label: 'base',
|
||||||
|
value: 'base'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'blank',
|
||||||
|
value: 'blank'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/** the enabled role options */
|
||||||
|
const roleOptions = ref<CommonType.Option<string>[]>([]);
|
||||||
|
|
||||||
|
async function getRoleOptions() {
|
||||||
|
const { error, data } = await fetchGetAllRoles();
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
const options = data.map(item => ({
|
||||||
|
label: item.roleName,
|
||||||
|
value: item.roleCode
|
||||||
|
}));
|
||||||
|
|
||||||
|
roleOptions.value = [...options];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInitModel() {
|
||||||
|
model.value = createDefaultModel();
|
||||||
|
|
||||||
|
if (!props.rowData) return;
|
||||||
|
|
||||||
|
if (props.operateType === 'addChild') {
|
||||||
|
const { id } = props.rowData;
|
||||||
|
|
||||||
|
Object.assign(model.value, { parentId: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.operateType === 'edit') {
|
||||||
|
const { component, ...rest } = props.rowData;
|
||||||
|
|
||||||
|
const { layout, page } = getLayoutAndPage(component);
|
||||||
|
const { path, param } = getPathParamFromRoutePath(rest.routePath);
|
||||||
|
|
||||||
|
Object.assign(model.value, rest, { layout, page, routePath: path, pathParam: param });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!model.value.query) {
|
||||||
|
model.value.query = [];
|
||||||
|
}
|
||||||
|
if (!model.value.buttons) {
|
||||||
|
model.value.buttons = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDrawer() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateRoutePathByRouteName() {
|
||||||
|
if (model.value.routeName) {
|
||||||
|
model.value.routePath = getRoutePathByRouteName(model.value.routeName);
|
||||||
|
} else {
|
||||||
|
model.value.routePath = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateI18nKeyByRouteName() {
|
||||||
|
if (model.value.routeName) {
|
||||||
|
model.value.i18nKey = `route.${model.value.routeName}` as App.I18n.I18nKey;
|
||||||
|
} else {
|
||||||
|
model.value.i18nKey = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCreateButton() {
|
||||||
|
const buttonItem: Api.SystemManage.MenuButton = {
|
||||||
|
code: '',
|
||||||
|
desc: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
return buttonItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubmitParams() {
|
||||||
|
const { layout, page, pathParam, ...params } = model.value;
|
||||||
|
|
||||||
|
const component = transformLayoutAndPageToComponent(layout, page);
|
||||||
|
const routePath = getRoutePathWithParam(model.value.routePath, pathParam);
|
||||||
|
|
||||||
|
params.component = component;
|
||||||
|
params.routePath = routePath;
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await validate();
|
||||||
|
|
||||||
|
const params = getSubmitParams();
|
||||||
|
|
||||||
|
console.log('params: ', params);
|
||||||
|
|
||||||
|
// request
|
||||||
|
window.$message?.success($t('common.updateSuccess'));
|
||||||
|
closeDrawer();
|
||||||
|
emit('submitted');
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(visible, () => {
|
||||||
|
if (visible.value) {
|
||||||
|
handleInitModel();
|
||||||
|
restoreValidation();
|
||||||
|
getRoleOptions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => model.value.routeName,
|
||||||
|
() => {
|
||||||
|
handleUpdateRoutePathByRouteName();
|
||||||
|
handleUpdateI18nKeyByRouteName();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NModal v-model:show="visible" :title="title" preset="card" class="w-800px">
|
||||||
|
<NScrollbar class="h-480px pr-20px">
|
||||||
|
<NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="100">
|
||||||
|
<NGrid responsive="screen" item-responsive>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.menuType')" path="menuType">
|
||||||
|
<NRadioGroup v-model:value="model.menuType" :disabled="disabledMenuType">
|
||||||
|
<NRadio v-for="item in menuTypeOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.menuName')" path="menuName">
|
||||||
|
<NInput v-model:value="model.menuName" :placeholder="$t('page.manage.menu.form.menuName')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.routeName')" path="routeName">
|
||||||
|
<NInput v-model:value="model.routeName" :placeholder="$t('page.manage.menu.form.routeName')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.routePath')" path="routePath">
|
||||||
|
<NInput v-model:value="model.routePath" disabled :placeholder="$t('page.manage.menu.form.routePath')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.pathParam')" path="pathParam">
|
||||||
|
<NInput v-model:value="model.pathParam" :placeholder="$t('page.manage.menu.form.pathParam')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi v-if="showLayout" span="24 m:12" :label="$t('page.manage.menu.layout')" path="layout">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="model.layout"
|
||||||
|
:options="layoutOptions"
|
||||||
|
:placeholder="$t('page.manage.menu.form.layout')"
|
||||||
|
/>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi v-if="showPage" span="24 m:12" :label="$t('page.manage.menu.page')" path="page">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="model.page"
|
||||||
|
:options="pageOptions"
|
||||||
|
:placeholder="$t('page.manage.menu.form.page')"
|
||||||
|
/>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.i18nKey')" path="i18nKey">
|
||||||
|
<NInput v-model:value="model.i18nKey" :placeholder="$t('page.manage.menu.form.i18nKey')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.order')" path="order">
|
||||||
|
<NInputNumber v-model:value="model.order" class="w-full" :placeholder="$t('page.manage.menu.form.order')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.iconTypeTitle')" path="iconType">
|
||||||
|
<NRadioGroup v-model:value="model.iconType">
|
||||||
|
<NRadio
|
||||||
|
v-for="item in menuIconTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="$t(item.label)"
|
||||||
|
/>
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.icon')" path="icon">
|
||||||
|
<template v-if="model.iconType === '1'">
|
||||||
|
<NInput v-model:value="model.icon" :placeholder="$t('page.manage.menu.form.icon')" class="flex-1">
|
||||||
|
<template #suffix>
|
||||||
|
<SvgIcon v-if="model.icon" :icon="model.icon" class="text-icon" />
|
||||||
|
</template>
|
||||||
|
</NInput>
|
||||||
|
</template>
|
||||||
|
<template v-if="model.iconType === '2'">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="model.icon"
|
||||||
|
:placeholder="$t('page.manage.menu.form.localIcon')"
|
||||||
|
:options="localIconOptions"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.menuStatus')" path="status">
|
||||||
|
<NRadioGroup v-model:value="model.status">
|
||||||
|
<NRadio
|
||||||
|
v-for="item in enableStatusOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="$t(item.label)"
|
||||||
|
/>
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.keepAlive')" path="keepAlive">
|
||||||
|
<NRadioGroup v-model:value="model.keepAlive">
|
||||||
|
<NRadio :value="true" :label="$t('common.yesOrNo.yes')" />
|
||||||
|
<NRadio :value="false" :label="$t('common.yesOrNo.no')" />
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.constant')" path="constant">
|
||||||
|
<NRadioGroup v-model:value="model.constant">
|
||||||
|
<NRadio :value="true" :label="$t('common.yesOrNo.yes')" />
|
||||||
|
<NRadio :value="false" :label="$t('common.yesOrNo.no')" />
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.href')" path="href">
|
||||||
|
<NInput v-model:value="model.href" :placeholder="$t('page.manage.menu.form.href')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.hideInMenu')" path="hideInMenu">
|
||||||
|
<NRadioGroup v-model:value="model.hideInMenu">
|
||||||
|
<NRadio :value="true" :label="$t('common.yesOrNo.yes')" />
|
||||||
|
<NRadio :value="false" :label="$t('common.yesOrNo.no')" />
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi
|
||||||
|
v-if="model.hideInMenu"
|
||||||
|
span="24 m:12"
|
||||||
|
:label="$t('page.manage.menu.activeMenu')"
|
||||||
|
path="activeMenu"
|
||||||
|
>
|
||||||
|
<NSelect
|
||||||
|
v-model:value="model.activeMenu"
|
||||||
|
:options="pageOptions"
|
||||||
|
clearable
|
||||||
|
:placeholder="$t('page.manage.menu.form.activeMenu')"
|
||||||
|
/>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.multiTab')" path="multiTab">
|
||||||
|
<NRadioGroup v-model:value="model.multiTab">
|
||||||
|
<NRadio :value="true" :label="$t('common.yesOrNo.yes')" />
|
||||||
|
<NRadio :value="false" :label="$t('common.yesOrNo.no')" />
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" :label="$t('page.manage.menu.fixedIndexInTab')" path="fixedIndexInTab">
|
||||||
|
<NInputNumber
|
||||||
|
v-model:value="model.fixedIndexInTab"
|
||||||
|
class="w-full"
|
||||||
|
clearable
|
||||||
|
:placeholder="$t('page.manage.menu.form.fixedIndexInTab')"
|
||||||
|
/>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24" :label="$t('page.manage.menu.query')">
|
||||||
|
<NDynamicInput
|
||||||
|
v-model:value="model.query"
|
||||||
|
preset="pair"
|
||||||
|
:key-placeholder="$t('page.manage.menu.form.queryKey')"
|
||||||
|
:value-placeholder="$t('page.manage.menu.form.queryValue')"
|
||||||
|
>
|
||||||
|
<template #action="{ index, create, remove }">
|
||||||
|
<NSpace class="ml-12px">
|
||||||
|
<NButton size="medium" @click="() => create(index)">
|
||||||
|
<icon-ic:round-plus class="text-icon" />
|
||||||
|
</NButton>
|
||||||
|
<NButton size="medium" @click="() => remove(index)">
|
||||||
|
<icon-ic-round-remove class="text-icon" />
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NDynamicInput>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24" :label="$t('page.manage.menu.button')">
|
||||||
|
<NDynamicInput v-model:value="model.buttons" :on-create="handleCreateButton">
|
||||||
|
<template #default="{ value }">
|
||||||
|
<div class="flex-y-center flex-1 gap-12px">
|
||||||
|
<NInput
|
||||||
|
v-model:value="value.code"
|
||||||
|
:placeholder="$t('page.manage.menu.form.buttonCode')"
|
||||||
|
class="flex-1"
|
||||||
|
/>
|
||||||
|
<NInput
|
||||||
|
v-model:value="value.desc"
|
||||||
|
:placeholder="$t('page.manage.menu.form.buttonDesc')"
|
||||||
|
class="flex-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #action="{ index, create, remove }">
|
||||||
|
<NSpace class="ml-12px">
|
||||||
|
<NButton size="medium" @click="() => create(index)">
|
||||||
|
<icon-ic:round-plus class="text-icon" />
|
||||||
|
</NButton>
|
||||||
|
<NButton size="medium" @click="() => remove(index)">
|
||||||
|
<icon-ic-round-remove class="text-icon" />
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NDynamicInput>
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
</NForm>
|
||||||
|
</NScrollbar>
|
||||||
|
<template #footer>
|
||||||
|
<NSpace justify="end" :size="16">
|
||||||
|
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||||
|
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
79
src/views/manage/menu/modules/shared.ts
Normal file
79
src/views/manage/menu/modules/shared.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
const LAYOUT_PREFIX = 'layout.';
|
||||||
|
const VIEW_PREFIX = 'view.';
|
||||||
|
const FIRST_LEVEL_ROUTE_COMPONENT_SPLIT = '$';
|
||||||
|
|
||||||
|
export function getLayoutAndPage(component?: string | null) {
|
||||||
|
let layout = '';
|
||||||
|
let page = '';
|
||||||
|
|
||||||
|
const [layoutOrPage = '', pageItem = ''] = component?.split(FIRST_LEVEL_ROUTE_COMPONENT_SPLIT) || [];
|
||||||
|
|
||||||
|
layout = getLayout(layoutOrPage);
|
||||||
|
page = getPage(pageItem || layoutOrPage);
|
||||||
|
|
||||||
|
return { layout, page };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLayout(layout: string) {
|
||||||
|
return layout.startsWith(LAYOUT_PREFIX) ? layout.replace(LAYOUT_PREFIX, '') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPage(page: string) {
|
||||||
|
return page.startsWith(VIEW_PREFIX) ? page.replace(VIEW_PREFIX, '') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformLayoutAndPageToComponent(layout: string, page: string) {
|
||||||
|
const hasLayout = Boolean(layout);
|
||||||
|
const hasPage = Boolean(page);
|
||||||
|
|
||||||
|
if (hasLayout && hasPage) {
|
||||||
|
return `${LAYOUT_PREFIX}${layout}${FIRST_LEVEL_ROUTE_COMPONENT_SPLIT}${VIEW_PREFIX}${page}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasLayout) {
|
||||||
|
return `${LAYOUT_PREFIX}${layout}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPage) {
|
||||||
|
return `${VIEW_PREFIX}${page}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get route name by route path
|
||||||
|
*
|
||||||
|
* @param routeName
|
||||||
|
*/
|
||||||
|
export function getRoutePathByRouteName(routeName: string) {
|
||||||
|
return `/${routeName.replace(/_/g, '/')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get path param from route path
|
||||||
|
*
|
||||||
|
* @param routePath route path
|
||||||
|
*/
|
||||||
|
export function getPathParamFromRoutePath(routePath: string) {
|
||||||
|
const [path, param = ''] = routePath.split('/:');
|
||||||
|
|
||||||
|
return {
|
||||||
|
path,
|
||||||
|
param
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get route path with param
|
||||||
|
*
|
||||||
|
* @param routePath route path
|
||||||
|
* @param param path param
|
||||||
|
*/
|
||||||
|
export function getRoutePathWithParam(routePath: string, param: string) {
|
||||||
|
if (param.trim()) {
|
||||||
|
return `${routePath}/:${param}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return routePath;
|
||||||
|
}
|
||||||
173
src/views/manage/role/index.vue
Normal file
173
src/views/manage/role/index.vue
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
||||||
|
import { enableStatusRecord } from '@/constants/business';
|
||||||
|
import { fetchGetRoleList } from '@/service/api';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import RoleOperateDrawer from './modules/role-operate-drawer.vue';
|
||||||
|
import RoleSearch from './modules/role-search.vue';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const searchParams: Api.SystemManage.RoleSearchParams = reactive({
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
roleName: null,
|
||||||
|
roleCode: null,
|
||||||
|
status: null
|
||||||
|
});
|
||||||
|
|
||||||
|
const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagination } = useNaivePaginatedTable({
|
||||||
|
api: () => fetchGetRoleList(searchParams),
|
||||||
|
transform: response => defaultTransform(response),
|
||||||
|
onPaginationParamsChange: params => {
|
||||||
|
searchParams.current = params.page;
|
||||||
|
searchParams.size = params.pageSize;
|
||||||
|
},
|
||||||
|
columns: () => [
|
||||||
|
{
|
||||||
|
type: 'selection',
|
||||||
|
align: 'center',
|
||||||
|
width: 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'index',
|
||||||
|
title: $t('common.index'),
|
||||||
|
width: 64,
|
||||||
|
align: 'center',
|
||||||
|
render: (_, index) => index + 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'roleName',
|
||||||
|
title: $t('page.manage.role.roleName'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'roleCode',
|
||||||
|
title: $t('page.manage.role.roleCode'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'roleDesc',
|
||||||
|
title: $t('page.manage.role.roleDesc'),
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'status',
|
||||||
|
title: $t('page.manage.role.roleStatus'),
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: row => {
|
||||||
|
if (row.status === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagMap: Record<Api.Common.EnableStatus, NaiveUI.ThemeColor> = {
|
||||||
|
1: 'success',
|
||||||
|
2: 'warning'
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = $t(enableStatusRecord[row.status]);
|
||||||
|
|
||||||
|
return <NTag type={tagMap[row.status]}>{label}</NTag>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'operate',
|
||||||
|
title: $t('common.operate'),
|
||||||
|
align: 'center',
|
||||||
|
width: 130,
|
||||||
|
render: row => (
|
||||||
|
<div class="flex-center gap-8px">
|
||||||
|
<NButton type="primary" ghost size="small" onClick={() => edit(row.id)}>
|
||||||
|
{$t('common.edit')}
|
||||||
|
</NButton>
|
||||||
|
<NPopconfirm onPositiveClick={() => handleDelete(row.id)}>
|
||||||
|
{{
|
||||||
|
default: () => $t('common.confirmDelete'),
|
||||||
|
trigger: () => (
|
||||||
|
<NButton type="error" ghost size="small">
|
||||||
|
{$t('common.delete')}
|
||||||
|
</NButton>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NPopconfirm>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
drawerVisible,
|
||||||
|
operateType,
|
||||||
|
editingData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
checkedRowKeys,
|
||||||
|
onBatchDeleted,
|
||||||
|
onDeleted
|
||||||
|
// closeDrawer
|
||||||
|
} = useTableOperate(data, 'id', getData);
|
||||||
|
|
||||||
|
async function handleBatchDelete() {
|
||||||
|
// request
|
||||||
|
console.log(checkedRowKeys.value);
|
||||||
|
|
||||||
|
onBatchDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(id: number) {
|
||||||
|
// request
|
||||||
|
console.log(id);
|
||||||
|
|
||||||
|
onDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(id: number) {
|
||||||
|
handleEdit(id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||||
|
<RoleSearch v-model:model="searchParams" @search="getDataByPage" />
|
||||||
|
<NCard :title="$t('page.manage.role.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||||
|
<template #header-extra>
|
||||||
|
<TableHeaderOperation
|
||||||
|
v-model:columns="columnChecks"
|
||||||
|
:disabled-delete="checkedRowKeys.length === 0"
|
||||||
|
:loading="loading"
|
||||||
|
@add="handleAdd"
|
||||||
|
@delete="handleBatchDelete"
|
||||||
|
@refresh="getData"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<NDataTable
|
||||||
|
v-model:checked-row-keys="checkedRowKeys"
|
||||||
|
:columns="columns"
|
||||||
|
:data="data"
|
||||||
|
size="small"
|
||||||
|
:flex-height="!appStore.isMobile"
|
||||||
|
:scroll-x="702"
|
||||||
|
:loading="loading"
|
||||||
|
remote
|
||||||
|
:row-key="row => row.id"
|
||||||
|
:pagination="mobilePagination"
|
||||||
|
class="sm:h-full"
|
||||||
|
/>
|
||||||
|
<RoleOperateDrawer
|
||||||
|
v-model:visible="drawerVisible"
|
||||||
|
:operate-type="operateType"
|
||||||
|
:row-data="editingData"
|
||||||
|
@submitted="getDataByPage"
|
||||||
|
/>
|
||||||
|
</NCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
101
src/views/manage/role/modules/button-auth-modal.vue
Normal file
101
src/views/manage/role/modules/button-auth-modal.vue
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, shallowRef } from 'vue';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ButtonAuthModal'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** the roleId */
|
||||||
|
roleId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = computed(() => $t('common.edit') + $t('page.manage.role.buttonAuth'));
|
||||||
|
|
||||||
|
type ButtonConfig = {
|
||||||
|
id: number;
|
||||||
|
label: string;
|
||||||
|
code: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tree = shallowRef<ButtonConfig[]>([]);
|
||||||
|
|
||||||
|
async function getAllButtons() {
|
||||||
|
// request
|
||||||
|
tree.value = [
|
||||||
|
{ id: 1, label: 'button1', code: 'code1' },
|
||||||
|
{ id: 2, label: 'button2', code: 'code2' },
|
||||||
|
{ id: 3, label: 'button3', code: 'code3' },
|
||||||
|
{ id: 4, label: 'button4', code: 'code4' },
|
||||||
|
{ id: 5, label: 'button5', code: 'code5' },
|
||||||
|
{ id: 6, label: 'button6', code: 'code6' },
|
||||||
|
{ id: 7, label: 'button7', code: 'code7' },
|
||||||
|
{ id: 8, label: 'button8', code: 'code8' },
|
||||||
|
{ id: 9, label: 'button9', code: 'code9' },
|
||||||
|
{ id: 10, label: 'button10', code: 'code10' }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const checks = shallowRef<number[]>([]);
|
||||||
|
|
||||||
|
async function getChecks() {
|
||||||
|
console.log(props.roleId);
|
||||||
|
// request
|
||||||
|
checks.value = [1, 2, 3, 4, 5];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
console.log(checks.value, props.roleId);
|
||||||
|
// request
|
||||||
|
|
||||||
|
window.$message?.success?.($t('common.modifySuccess'));
|
||||||
|
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
getAllButtons();
|
||||||
|
getChecks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
init();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NModal v-model:show="visible" :title="title" preset="card" class="w-480px">
|
||||||
|
<NTree
|
||||||
|
v-model:checked-keys="checks"
|
||||||
|
:data="tree"
|
||||||
|
key-field="id"
|
||||||
|
block-line
|
||||||
|
checkable
|
||||||
|
expand-on-click
|
||||||
|
virtual-scroll
|
||||||
|
class="h-280px"
|
||||||
|
/>
|
||||||
|
<template #footer>
|
||||||
|
<NSpace justify="end">
|
||||||
|
<NButton size="small" class="mt-16px" @click="closeModal">
|
||||||
|
{{ $t('common.cancel') }}
|
||||||
|
</NButton>
|
||||||
|
<NButton type="primary" size="small" class="mt-16px" @click="handleSubmit">
|
||||||
|
{{ $t('common.confirm') }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
130
src/views/manage/role/modules/menu-auth-modal.vue
Normal file
130
src/views/manage/role/modules/menu-auth-modal.vue
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, shallowRef, watch } from 'vue';
|
||||||
|
import { fetchGetAllPages, fetchGetMenuTree } from '@/service/api';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MenuAuthModal'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** the roleId */
|
||||||
|
roleId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = computed(() => $t('common.edit') + $t('page.manage.role.menuAuth'));
|
||||||
|
|
||||||
|
const home = shallowRef('');
|
||||||
|
|
||||||
|
async function getHome() {
|
||||||
|
console.log(props.roleId);
|
||||||
|
|
||||||
|
home.value = 'home';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateHome(val: string) {
|
||||||
|
// request
|
||||||
|
|
||||||
|
home.value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pages = shallowRef<string[]>([]);
|
||||||
|
|
||||||
|
async function getPages() {
|
||||||
|
const { error, data } = await fetchGetAllPages();
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
pages.value = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageSelectOptions = computed(() => {
|
||||||
|
const opts: CommonType.Option[] = pages.value.map(page => ({
|
||||||
|
label: page,
|
||||||
|
value: page
|
||||||
|
}));
|
||||||
|
|
||||||
|
return opts;
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = shallowRef<Api.SystemManage.MenuTree[]>([]);
|
||||||
|
|
||||||
|
async function getTree() {
|
||||||
|
const { error, data } = await fetchGetMenuTree();
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
tree.value = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checks = shallowRef<number[]>([]);
|
||||||
|
|
||||||
|
async function getChecks() {
|
||||||
|
console.log(props.roleId);
|
||||||
|
// request
|
||||||
|
checks.value = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
console.log(checks.value, props.roleId);
|
||||||
|
// request
|
||||||
|
|
||||||
|
window.$message?.success?.($t('common.modifySuccess'));
|
||||||
|
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
getHome();
|
||||||
|
getPages();
|
||||||
|
getTree();
|
||||||
|
getChecks();
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(visible, val => {
|
||||||
|
if (val) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NModal v-model:show="visible" :title="title" preset="card" class="w-480px">
|
||||||
|
<div class="flex-y-center gap-16px pb-12px">
|
||||||
|
<div>{{ $t('page.manage.menu.home') }}</div>
|
||||||
|
<NSelect :value="home" :options="pageSelectOptions" size="small" class="w-160px" @update:value="updateHome" />
|
||||||
|
</div>
|
||||||
|
<NTree
|
||||||
|
v-model:checked-keys="checks"
|
||||||
|
:data="tree"
|
||||||
|
key-field="id"
|
||||||
|
checkable
|
||||||
|
expand-on-click
|
||||||
|
virtual-scroll
|
||||||
|
block-line
|
||||||
|
class="h-280px"
|
||||||
|
/>
|
||||||
|
<template #footer>
|
||||||
|
<NSpace justify="end">
|
||||||
|
<NButton size="small" class="mt-16px" @click="closeModal">
|
||||||
|
{{ $t('common.cancel') }}
|
||||||
|
</NButton>
|
||||||
|
<NButton type="primary" size="small" class="mt-16px" @click="handleSubmit">
|
||||||
|
{{ $t('common.confirm') }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
135
src/views/manage/role/modules/role-operate-drawer.vue
Normal file
135
src/views/manage/role/modules/role-operate-drawer.vue
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { jsonClone } from '@sa/utils';
|
||||||
|
import { useBoolean } from '@sa/hooks';
|
||||||
|
import { enableStatusOptions } from '@/constants/business';
|
||||||
|
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import MenuAuthModal from './menu-auth-modal.vue';
|
||||||
|
import ButtonAuthModal from './button-auth-modal.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'RoleOperateDrawer'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** the type of operation */
|
||||||
|
operateType: NaiveUI.TableOperateType;
|
||||||
|
/** the edit row data */
|
||||||
|
rowData?: Api.SystemManage.Role | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'submitted'): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||||
|
const { defaultRequiredRule } = useFormRules();
|
||||||
|
const { bool: menuAuthVisible, setTrue: openMenuAuthModal } = useBoolean();
|
||||||
|
const { bool: buttonAuthVisible, setTrue: openButtonAuthModal } = useBoolean();
|
||||||
|
|
||||||
|
const title = computed(() => {
|
||||||
|
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||||
|
add: $t('page.manage.role.addRole'),
|
||||||
|
edit: $t('page.manage.role.editRole')
|
||||||
|
};
|
||||||
|
return titles[props.operateType];
|
||||||
|
});
|
||||||
|
|
||||||
|
type Model = Pick<Api.SystemManage.Role, 'roleName' | 'roleCode' | 'roleDesc' | 'status'>;
|
||||||
|
|
||||||
|
const model = ref(createDefaultModel());
|
||||||
|
|
||||||
|
function createDefaultModel(): Model {
|
||||||
|
return {
|
||||||
|
roleName: '',
|
||||||
|
roleCode: '',
|
||||||
|
roleDesc: '',
|
||||||
|
status: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleKey = Exclude<keyof Model, 'roleDesc'>;
|
||||||
|
|
||||||
|
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||||
|
roleName: defaultRequiredRule,
|
||||||
|
roleCode: defaultRequiredRule,
|
||||||
|
status: defaultRequiredRule
|
||||||
|
};
|
||||||
|
|
||||||
|
const roleId = computed(() => props.rowData?.id || -1);
|
||||||
|
|
||||||
|
const isEdit = computed(() => props.operateType === 'edit');
|
||||||
|
|
||||||
|
function handleInitModel() {
|
||||||
|
model.value = createDefaultModel();
|
||||||
|
|
||||||
|
if (props.operateType === 'edit' && props.rowData) {
|
||||||
|
Object.assign(model.value, jsonClone(props.rowData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDrawer() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await validate();
|
||||||
|
// request
|
||||||
|
window.$message?.success($t('common.updateSuccess'));
|
||||||
|
closeDrawer();
|
||||||
|
emit('submitted');
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(visible, () => {
|
||||||
|
if (visible.value) {
|
||||||
|
handleInitModel();
|
||||||
|
restoreValidation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDrawer v-model:show="visible" display-directive="show" :width="360">
|
||||||
|
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||||
|
<NForm ref="formRef" :model="model" :rules="rules">
|
||||||
|
<NFormItem :label="$t('page.manage.role.roleName')" path="roleName">
|
||||||
|
<NInput v-model:value="model.roleName" :placeholder="$t('page.manage.role.form.roleName')" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.manage.role.roleCode')" path="roleCode">
|
||||||
|
<NInput v-model:value="model.roleCode" :placeholder="$t('page.manage.role.form.roleCode')" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.manage.role.roleStatus')" path="status">
|
||||||
|
<NRadioGroup v-model:value="model.status">
|
||||||
|
<NRadio v-for="item in enableStatusOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.manage.role.roleDesc')" path="roleDesc">
|
||||||
|
<NInput v-model:value="model.roleDesc" :placeholder="$t('page.manage.role.form.roleDesc')" />
|
||||||
|
</NFormItem>
|
||||||
|
</NForm>
|
||||||
|
<NSpace v-if="isEdit">
|
||||||
|
<NButton @click="openMenuAuthModal">{{ $t('page.manage.role.menuAuth') }}</NButton>
|
||||||
|
<MenuAuthModal v-model:visible="menuAuthVisible" :role-id="roleId" />
|
||||||
|
<NButton @click="openButtonAuthModal">{{ $t('page.manage.role.buttonAuth') }}</NButton>
|
||||||
|
<ButtonAuthModal v-model:visible="buttonAuthVisible" :role-id="roleId" />
|
||||||
|
</NSpace>
|
||||||
|
<template #footer>
|
||||||
|
<NSpace :size="16">
|
||||||
|
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||||
|
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NDrawerContent>
|
||||||
|
</NDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
74
src/views/manage/role/modules/role-search.vue
Normal file
74
src/views/manage/role/modules/role-search.vue
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { toRaw } from 'vue';
|
||||||
|
import { jsonClone } from '@sa/utils';
|
||||||
|
import { enableStatusOptions } from '@/constants/business';
|
||||||
|
import { translateOptions } from '@/utils/common';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'RoleSearch'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'search'): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const model = defineModel<Api.SystemManage.RoleSearchParams>('model', { required: true });
|
||||||
|
|
||||||
|
const defaultModel = jsonClone(toRaw(model.value));
|
||||||
|
|
||||||
|
function resetModel() {
|
||||||
|
Object.assign(model.value, defaultModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function search() {
|
||||||
|
emit('search');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||||
|
<NCollapse :default-expanded-names="['role-search']">
|
||||||
|
<NCollapseItem :title="$t('common.search')" name="role-search">
|
||||||
|
<NForm :model="model" label-placement="left" :label-width="80">
|
||||||
|
<NGrid responsive="screen" item-responsive>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.role.roleName')" path="roleName" class="pr-24px">
|
||||||
|
<NInput v-model:value="model.roleName" :placeholder="$t('page.manage.role.form.roleName')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.role.roleCode')" path="roleCode" class="pr-24px">
|
||||||
|
<NInput v-model:value="model.roleCode" :placeholder="$t('page.manage.role.form.roleCode')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.role.roleStatus')" path="status" class="pr-24px">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="model.status"
|
||||||
|
:placeholder="$t('page.manage.role.form.roleStatus')"
|
||||||
|
:options="translateOptions(enableStatusOptions)"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6">
|
||||||
|
<NSpace class="w-full" justify="end">
|
||||||
|
<NButton @click="resetModel">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-refresh class="text-icon" />
|
||||||
|
</template>
|
||||||
|
{{ $t('common.reset') }}
|
||||||
|
</NButton>
|
||||||
|
<NButton type="primary" ghost @click="search">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-search class="text-icon" />
|
||||||
|
</template>
|
||||||
|
{{ $t('common.search') }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
</NForm>
|
||||||
|
</NCollapseItem>
|
||||||
|
</NCollapse>
|
||||||
|
</NCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
13
src/views/manage/user-detail/[id].vue
Normal file
13
src/views/manage/user-detail/[id].vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
interface Props {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<LookForward />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
203
src/views/manage/user/index.vue
Normal file
203
src/views/manage/user/index.vue
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
||||||
|
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
|
||||||
|
import { fetchGetUserList } from '@/service/api';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
||||||
|
import UserSearch from './modules/user-search.vue';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const searchParams: Api.SystemManage.UserSearchParams = reactive({
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
status: null,
|
||||||
|
userName: null,
|
||||||
|
userGender: null,
|
||||||
|
nickName: null,
|
||||||
|
userPhone: null,
|
||||||
|
userEmail: null
|
||||||
|
});
|
||||||
|
|
||||||
|
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination } = useNaivePaginatedTable({
|
||||||
|
api: () => fetchGetUserList(searchParams),
|
||||||
|
transform: response => defaultTransform(response),
|
||||||
|
onPaginationParamsChange: params => {
|
||||||
|
searchParams.current = params.page;
|
||||||
|
searchParams.size = params.pageSize;
|
||||||
|
},
|
||||||
|
columns: () => [
|
||||||
|
{
|
||||||
|
type: 'selection',
|
||||||
|
align: 'center',
|
||||||
|
width: 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'index',
|
||||||
|
title: $t('common.index'),
|
||||||
|
align: 'center',
|
||||||
|
width: 64,
|
||||||
|
render: (_, index) => index + 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'userName',
|
||||||
|
title: $t('page.manage.user.userName'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'userGender',
|
||||||
|
title: $t('page.manage.user.userGender'),
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: row => {
|
||||||
|
if (row.userGender === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagMap: Record<Api.SystemManage.UserGender, NaiveUI.ThemeColor> = {
|
||||||
|
1: 'primary',
|
||||||
|
2: 'error'
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = $t(userGenderRecord[row.userGender]);
|
||||||
|
|
||||||
|
return <NTag type={tagMap[row.userGender]}>{label}</NTag>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'nickName',
|
||||||
|
title: $t('page.manage.user.nickName'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'userPhone',
|
||||||
|
title: $t('page.manage.user.userPhone'),
|
||||||
|
align: 'center',
|
||||||
|
width: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'userEmail',
|
||||||
|
title: $t('page.manage.user.userEmail'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'status',
|
||||||
|
title: $t('page.manage.user.userStatus'),
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: row => {
|
||||||
|
if (row.status === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagMap: Record<Api.Common.EnableStatus, NaiveUI.ThemeColor> = {
|
||||||
|
1: 'success',
|
||||||
|
2: 'warning'
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = $t(enableStatusRecord[row.status]);
|
||||||
|
|
||||||
|
return <NTag type={tagMap[row.status]}>{label}</NTag>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'operate',
|
||||||
|
title: $t('common.operate'),
|
||||||
|
align: 'center',
|
||||||
|
width: 130,
|
||||||
|
render: row => (
|
||||||
|
<div class="flex-center gap-8px">
|
||||||
|
<NButton type="primary" ghost size="small" onClick={() => edit(row.id)}>
|
||||||
|
{$t('common.edit')}
|
||||||
|
</NButton>
|
||||||
|
<NPopconfirm onPositiveClick={() => handleDelete(row.id)}>
|
||||||
|
{{
|
||||||
|
default: () => $t('common.confirmDelete'),
|
||||||
|
trigger: () => (
|
||||||
|
<NButton type="error" ghost size="small">
|
||||||
|
{$t('common.delete')}
|
||||||
|
</NButton>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NPopconfirm>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
drawerVisible,
|
||||||
|
operateType,
|
||||||
|
editingData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
checkedRowKeys,
|
||||||
|
onBatchDeleted,
|
||||||
|
onDeleted
|
||||||
|
// closeDrawer
|
||||||
|
} = useTableOperate(data, 'id', getData);
|
||||||
|
|
||||||
|
async function handleBatchDelete() {
|
||||||
|
// request
|
||||||
|
console.log(checkedRowKeys.value);
|
||||||
|
|
||||||
|
onBatchDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(id: number) {
|
||||||
|
// request
|
||||||
|
console.log(id);
|
||||||
|
|
||||||
|
onDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(id: number) {
|
||||||
|
handleEdit(id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||||
|
<UserSearch v-model:model="searchParams" @search="getDataByPage" />
|
||||||
|
<NCard :title="$t('page.manage.user.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||||
|
<template #header-extra>
|
||||||
|
<TableHeaderOperation
|
||||||
|
v-model:columns="columnChecks"
|
||||||
|
:disabled-delete="checkedRowKeys.length === 0"
|
||||||
|
:loading="loading"
|
||||||
|
@add="handleAdd"
|
||||||
|
@delete="handleBatchDelete"
|
||||||
|
@refresh="getData"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<NDataTable
|
||||||
|
v-model:checked-row-keys="checkedRowKeys"
|
||||||
|
:columns="columns"
|
||||||
|
:data="data"
|
||||||
|
size="small"
|
||||||
|
:flex-height="!appStore.isMobile"
|
||||||
|
:scroll-x="962"
|
||||||
|
:loading="loading"
|
||||||
|
remote
|
||||||
|
:row-key="row => row.id"
|
||||||
|
:pagination="mobilePagination"
|
||||||
|
class="sm:h-full"
|
||||||
|
/>
|
||||||
|
<UserOperateDrawer
|
||||||
|
v-model:visible="drawerVisible"
|
||||||
|
:operate-type="operateType"
|
||||||
|
:row-data="editingData"
|
||||||
|
@submitted="getDataByPage"
|
||||||
|
/>
|
||||||
|
</NCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
167
src/views/manage/user/modules/user-operate-drawer.vue
Normal file
167
src/views/manage/user/modules/user-operate-drawer.vue
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { jsonClone } from '@sa/utils';
|
||||||
|
import { enableStatusOptions, userGenderOptions } from '@/constants/business';
|
||||||
|
import { fetchGetAllRoles } from '@/service/api';
|
||||||
|
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'UserOperateDrawer'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** the type of operation */
|
||||||
|
operateType: NaiveUI.TableOperateType;
|
||||||
|
/** the edit row data */
|
||||||
|
rowData?: Api.SystemManage.User | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'submitted'): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||||
|
const { defaultRequiredRule } = useFormRules();
|
||||||
|
|
||||||
|
const title = computed(() => {
|
||||||
|
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||||
|
add: $t('page.manage.user.addUser'),
|
||||||
|
edit: $t('page.manage.user.editUser')
|
||||||
|
};
|
||||||
|
return titles[props.operateType];
|
||||||
|
});
|
||||||
|
|
||||||
|
type Model = Pick<
|
||||||
|
Api.SystemManage.User,
|
||||||
|
'userName' | 'userGender' | 'nickName' | 'userPhone' | 'userEmail' | 'userRoles' | 'status'
|
||||||
|
>;
|
||||||
|
|
||||||
|
const model = ref(createDefaultModel());
|
||||||
|
|
||||||
|
function createDefaultModel(): Model {
|
||||||
|
return {
|
||||||
|
userName: '',
|
||||||
|
userGender: null,
|
||||||
|
nickName: '',
|
||||||
|
userPhone: '',
|
||||||
|
userEmail: '',
|
||||||
|
userRoles: [],
|
||||||
|
status: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleKey = Extract<keyof Model, 'userName' | 'status'>;
|
||||||
|
|
||||||
|
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||||
|
userName: defaultRequiredRule,
|
||||||
|
status: defaultRequiredRule
|
||||||
|
};
|
||||||
|
|
||||||
|
/** the enabled role options */
|
||||||
|
const roleOptions = ref<CommonType.Option<string>[]>([]);
|
||||||
|
|
||||||
|
async function getRoleOptions() {
|
||||||
|
const { error, data } = await fetchGetAllRoles();
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
const options = data.map(item => ({
|
||||||
|
label: item.roleName,
|
||||||
|
value: item.roleCode
|
||||||
|
}));
|
||||||
|
|
||||||
|
// the mock data does not have the roleCode, so fill it
|
||||||
|
// if the real request, remove the following code
|
||||||
|
const userRoleOptions = model.value.userRoles.map(item => ({
|
||||||
|
label: item,
|
||||||
|
value: item
|
||||||
|
}));
|
||||||
|
// end
|
||||||
|
|
||||||
|
roleOptions.value = [...userRoleOptions, ...options];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInitModel() {
|
||||||
|
model.value = createDefaultModel();
|
||||||
|
|
||||||
|
if (props.operateType === 'edit' && props.rowData) {
|
||||||
|
Object.assign(model.value, jsonClone(props.rowData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDrawer() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await validate();
|
||||||
|
// request
|
||||||
|
window.$message?.success($t('common.updateSuccess'));
|
||||||
|
closeDrawer();
|
||||||
|
emit('submitted');
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(visible, () => {
|
||||||
|
if (visible.value) {
|
||||||
|
handleInitModel();
|
||||||
|
restoreValidation();
|
||||||
|
getRoleOptions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDrawer v-model:show="visible" display-directive="show" :width="360">
|
||||||
|
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||||
|
<NForm ref="formRef" :model="model" :rules="rules">
|
||||||
|
<NFormItem :label="$t('page.manage.user.userName')" path="userName">
|
||||||
|
<NInput v-model:value="model.userName" :placeholder="$t('page.manage.user.form.userName')" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.manage.user.userGender')" path="userGender">
|
||||||
|
<NRadioGroup v-model:value="model.userGender">
|
||||||
|
<NRadio v-for="item in userGenderOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.manage.user.nickName')" path="nickName">
|
||||||
|
<NInput v-model:value="model.nickName" :placeholder="$t('page.manage.user.form.nickName')" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.manage.user.userPhone')" path="userPhone">
|
||||||
|
<NInput v-model:value="model.userPhone" :placeholder="$t('page.manage.user.form.userPhone')" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.manage.user.userEmail')" path="email">
|
||||||
|
<NInput v-model:value="model.userEmail" :placeholder="$t('page.manage.user.form.userEmail')" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.manage.user.userStatus')" path="status">
|
||||||
|
<NRadioGroup v-model:value="model.status">
|
||||||
|
<NRadio v-for="item in enableStatusOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
||||||
|
</NRadioGroup>
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.manage.user.userRole')" path="roles">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="model.userRoles"
|
||||||
|
multiple
|
||||||
|
:options="roleOptions"
|
||||||
|
:placeholder="$t('page.manage.user.form.userRole')"
|
||||||
|
/>
|
||||||
|
</NFormItem>
|
||||||
|
</NForm>
|
||||||
|
<template #footer>
|
||||||
|
<NSpace :size="16">
|
||||||
|
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||||
|
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NDrawerContent>
|
||||||
|
</NDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
118
src/views/manage/user/modules/user-search.vue
Normal file
118
src/views/manage/user/modules/user-search.vue
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, toRaw } from 'vue';
|
||||||
|
import { jsonClone } from '@sa/utils';
|
||||||
|
import { enableStatusOptions, userGenderOptions } from '@/constants/business';
|
||||||
|
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||||
|
import { translateOptions } from '@/utils/common';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'UserSearch'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'search'): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||||
|
|
||||||
|
const model = defineModel<Api.SystemManage.UserSearchParams>('model', { required: true });
|
||||||
|
|
||||||
|
type RuleKey = Extract<keyof Api.SystemManage.UserSearchParams, 'userEmail' | 'userPhone'>;
|
||||||
|
|
||||||
|
const rules = computed<Record<RuleKey, App.Global.FormRule>>(() => {
|
||||||
|
const { patternRules } = useFormRules(); // inside computed to make locale reactive
|
||||||
|
|
||||||
|
return {
|
||||||
|
userEmail: patternRules.email,
|
||||||
|
userPhone: patternRules.phone
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultModel = jsonClone(toRaw(model.value));
|
||||||
|
|
||||||
|
function resetModel() {
|
||||||
|
Object.assign(model.value, defaultModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reset() {
|
||||||
|
await restoreValidation();
|
||||||
|
resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function search() {
|
||||||
|
await validate();
|
||||||
|
emit('search');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||||
|
<NCollapse>
|
||||||
|
<NCollapseItem :title="$t('common.search')" name="user-search">
|
||||||
|
<NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="80">
|
||||||
|
<NGrid responsive="screen" item-responsive>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.user.userName')" path="userName" class="pr-24px">
|
||||||
|
<NInput v-model:value="model.userName" :placeholder="$t('page.manage.user.form.userName')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi
|
||||||
|
span="24 s:12 m:6"
|
||||||
|
:label="$t('page.manage.user.userGender')"
|
||||||
|
path="userGender"
|
||||||
|
class="pr-24px"
|
||||||
|
>
|
||||||
|
<NSelect
|
||||||
|
v-model:value="model.userGender"
|
||||||
|
:placeholder="$t('page.manage.user.form.userGender')"
|
||||||
|
:options="translateOptions(userGenderOptions)"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.user.nickName')" path="nickName" class="pr-24px">
|
||||||
|
<NInput v-model:value="model.nickName" :placeholder="$t('page.manage.user.form.nickName')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.user.userPhone')" path="userPhone" class="pr-24px">
|
||||||
|
<NInput v-model:value="model.userPhone" :placeholder="$t('page.manage.user.form.userPhone')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.user.userEmail')" path="userEmail" class="pr-24px">
|
||||||
|
<NInput v-model:value="model.userEmail" :placeholder="$t('page.manage.user.form.userEmail')" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi
|
||||||
|
span="24 s:12 m:6"
|
||||||
|
:label="$t('page.manage.user.userStatus')"
|
||||||
|
path="userStatus"
|
||||||
|
class="pr-24px"
|
||||||
|
>
|
||||||
|
<NSelect
|
||||||
|
v-model:value="model.status"
|
||||||
|
:placeholder="$t('page.manage.user.form.userStatus')"
|
||||||
|
:options="translateOptions(enableStatusOptions)"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 m:12" class="pr-24px">
|
||||||
|
<NSpace class="w-full" justify="end">
|
||||||
|
<NButton @click="reset">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-refresh class="text-icon" />
|
||||||
|
</template>
|
||||||
|
{{ $t('common.reset') }}
|
||||||
|
</NButton>
|
||||||
|
<NButton type="primary" ghost @click="search">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-search class="text-icon" />
|
||||||
|
</template>
|
||||||
|
{{ $t('common.search') }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
</NForm>
|
||||||
|
</NCollapseItem>
|
||||||
|
</NCollapse>
|
||||||
|
</NCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user