mirror of
https://gitee.com/dapppp/ruoyi-plus-vben5.git
synced 2026-04-02 18:03:22 +08:00
Compare commits
78 Commits
1.3.1-back
...
1.3.3-back
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a10a981fab | ||
|
|
20c15f352f | ||
|
|
8aa7dabeff | ||
|
|
78c7c1589a | ||
|
|
dd833ca56b | ||
|
|
681c1dc267 | ||
|
|
4545422ee0 | ||
|
|
5f26f5662e | ||
|
|
ca94ca906f | ||
|
|
76de450c71 | ||
|
|
dd2b1ed580 | ||
|
|
baec89f896 | ||
|
|
7c7051a11e | ||
|
|
aa27a2f7a1 | ||
|
|
9ee6d06d50 | ||
|
|
0cc1cb5a7b | ||
|
|
e662681ce2 | ||
|
|
0a9fc4e02d | ||
|
|
be840460d8 | ||
|
|
cb45987fe2 | ||
|
|
5ffd7db8e0 | ||
|
|
14377705e7 | ||
|
|
23503778d4 | ||
|
|
f54fab0bae | ||
|
|
b985ff0584 | ||
|
|
eff2f2a0b1 | ||
|
|
664fa800cd | ||
|
|
5dc4448c01 | ||
|
|
ccfe992779 | ||
|
|
583504495d | ||
|
|
7fb4bf3431 | ||
|
|
b148b8ec92 | ||
|
|
79de6bcbf7 | ||
|
|
14bd6dd25d | ||
|
|
9b577261e2 | ||
|
|
7f269e0d69 | ||
|
|
4baec83db5 | ||
|
|
7d8416890b | ||
|
|
2e2ffcd59e | ||
|
|
2046bfa846 | ||
|
|
0446adf778 | ||
|
|
f7a4d13a4c | ||
|
|
e587256425 | ||
|
|
0936861da1 | ||
|
|
3318d76bab | ||
|
|
8f3881eabf | ||
|
|
5252480b09 | ||
|
|
f096dfc6e6 | ||
|
|
d18f56177c | ||
|
|
333998b518 | ||
|
|
3fb4fba1cb | ||
|
|
c7e6210c8d | ||
|
|
d864085c13 | ||
|
|
fcdc1a1602 | ||
|
|
bf7496f0d5 | ||
|
|
9700150653 | ||
|
|
f0e9e55af2 | ||
|
|
ff88274554 | ||
|
|
afce9dc5c0 | ||
|
|
b5700bd0b1 | ||
|
|
e085083e42 | ||
|
|
a47910f650 | ||
|
|
a8c4786311 | ||
|
|
2971ccc0b7 | ||
|
|
4ead56eaf1 | ||
|
|
4fad8d77de | ||
|
|
9db1087d32 | ||
|
|
4a2c7b313f | ||
|
|
0f5fc5f54c | ||
|
|
76108e7b8f | ||
|
|
6018817906 | ||
|
|
7e4bdf7bd6 | ||
|
|
32117574f6 | ||
|
|
a48dfa1de2 | ||
|
|
36bf6fc149 | ||
|
|
f46ec30995 | ||
|
|
9bd5a190c2 | ||
|
|
86da3cedc2 |
@@ -2,5 +2,5 @@ ports:
|
|||||||
- port: 5555
|
- port: 5555
|
||||||
onOpen: open-preview
|
onOpen: open-preview
|
||||||
tasks:
|
tasks:
|
||||||
- init: corepack enable && pnpm install
|
- init: npm i -g corepack && pnpm install
|
||||||
command: pnpm run dev:play
|
command: pnpm run dev:play
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ export default {
|
|||||||
],
|
],
|
||||||
'package.json': ['prettier --cache --write'],
|
'package.json': ['prettier --cache --write'],
|
||||||
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
||||||
'prettier --cache --write--parser json',
|
'prettier --cache --write --parser json',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
20.14.0
|
22.1.0
|
||||||
|
|||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -14,7 +14,7 @@
|
|||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"editor.detectIndentation": false,
|
"editor.detectIndentation": false,
|
||||||
"editor.cursorBlinking": "expand",
|
"editor.cursorBlinking": "expand",
|
||||||
"editor.largeFileOptimizations": false,
|
"editor.largeFileOptimizations": true,
|
||||||
"editor.accessibilitySupport": "off",
|
"editor.accessibilitySupport": "off",
|
||||||
"editor.cursorSmoothCaretAnimation": "on",
|
"editor.cursorSmoothCaretAnimation": "on",
|
||||||
"editor.guides.bracketPairs": "active",
|
"editor.guides.bracketPairs": "active",
|
||||||
@@ -91,6 +91,7 @@
|
|||||||
"**/bower_components": true,
|
"**/bower_components": true,
|
||||||
"**/.turbo": true,
|
"**/.turbo": true,
|
||||||
"**/.idea": true,
|
"**/.idea": true,
|
||||||
|
"**/.vitepress": true,
|
||||||
"**/tmp": true,
|
"**/tmp": true,
|
||||||
"**/.git": true,
|
"**/.git": true,
|
||||||
"**/.svn": true,
|
"**/.svn": true,
|
||||||
@@ -113,6 +114,8 @@
|
|||||||
"**/yarn.lock": true
|
"**/yarn.lock": true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"typescript.tsserver.exclude": ["**/node_modules", "**/dist", "**/.turbo"],
|
||||||
|
|
||||||
// search
|
// search
|
||||||
"search.searchEditor.singleClickBehaviour": "peekDefinition",
|
"search.searchEditor.singleClickBehaviour": "peekDefinition",
|
||||||
"search.followSymlinks": false,
|
"search.followSymlinks": false,
|
||||||
|
|||||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,24 @@
|
|||||||
|
# 1.3.3
|
||||||
|
|
||||||
|
**BUG FIX**
|
||||||
|
|
||||||
|
- 工作流list展示在开启缩放会有误差导致触底逻辑不会触发
|
||||||
|
|
||||||
|
**OTHER**
|
||||||
|
|
||||||
|
- 代码生成预览对模板的提示...(下载都懒得点一下吗)
|
||||||
|
|
||||||
|
# 1.3.2
|
||||||
|
|
||||||
|
**REFACTOR**
|
||||||
|
|
||||||
|
- 所有表格操作列宽度调整为'auto', 这样会根据子元素宽度适配(比如没有分配权限的情况)
|
||||||
|
- 菜单图标更新了一部分 sql同步更新
|
||||||
|
|
||||||
|
**OTHER**
|
||||||
|
|
||||||
|
- 暂时锁死vite依赖 i18n会报错
|
||||||
|
|
||||||
# 1.3.1
|
# 1.3.1
|
||||||
|
|
||||||
**REFACTOR**
|
**REFACTOR**
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp"> </a> <br> <br>
|
<div align="center">
|
||||||
|
<a href="https://github.com/anncwb/vue-vben-admin">
|
||||||
|
<img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp">
|
||||||
|
</a>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
<h1>Vue Vben Admin</h1>
|
<h1>Vue Vben Admin</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
[](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin)    
|
[](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin)    
|
||||||
@@ -15,27 +20,27 @@ Vue Vben Adminは、最新の`vue3`、`vite`、`TypeScript`などの主流技術
|
|||||||
|
|
||||||
## アップグレード通知
|
## アップグレード通知
|
||||||
|
|
||||||
これは最新バージョン5.0であり、以前のバージョンとは互換性がありません。新しいプロジェクトを開始する場合は、最新バージョンを使用することをお勧めします。古いバージョンを表示したい場合は、[v2ブランチ](https://github.com/vbenjs/vue-vben-admin/tree/v2)を使用してください。
|
これは最新バージョン `5.0` であり、以前のバージョンとは互換性がありません。新しいプロジェクトを開始する場合は、最新バージョンを使用することをお勧めします。古いバージョンを表示したい場合は、[v2ブランチ](https://github.com/vbenjs/vue-vben-admin/tree/v2)を使用してください。
|
||||||
|
|
||||||
## 特徴
|
## 特徴
|
||||||
|
|
||||||
- **最新技術スタック**: Vue 3やViteなどの最先端フロントエンド技術で開発
|
- **最新技術スタック**:Vue 3やViteなどの最先端フロントエンド技術で開発
|
||||||
- **TypeScript**: アプリケーション規模のJavaScriptのための言語
|
- **TypeScript**:アプリケーション規模のJavaScriptのための言語
|
||||||
- **テーマ**: 複数のテーマカラーが利用可能で、カスタマイズオプションも豊富
|
- **テーマ**:複数のテーマカラーが利用可能で、カスタマイズオプションも豊富
|
||||||
- **国際化**: 完全な内蔵国際化サポート
|
- **国際化**:完全な内蔵国際化サポート
|
||||||
- **権限管理**: 動的ルートベースの権限生成ソリューションを内蔵
|
- **権限管理**:動的ルートベースの権限生成ソリューションを内蔵
|
||||||
|
|
||||||
## プレビュー
|
## プレビュー
|
||||||
|
|
||||||
- [Vben Admin](https://vben.pro/) - フルバージョンの中国語サイト
|
- [Vben Admin](https://vben.pro/) - フルバージョンの中国語サイト
|
||||||
|
|
||||||
テストアカウント: vben/123456
|
テストアカウント:vben/123456
|
||||||
|
|
||||||
<p align="center">
|
<div align="center">
|
||||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
|
||||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
|
||||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
|
||||||
</p>
|
</div>
|
||||||
|
|
||||||
### Gitpodを使用
|
### Gitpodを使用
|
||||||
|
|
||||||
@@ -49,30 +54,27 @@ Gitpod(GitHub用の無料オンライン開発環境)でプロジェクト
|
|||||||
|
|
||||||
## インストールと使用
|
## インストールと使用
|
||||||
|
|
||||||
- プロジェクトコードを取得
|
1. プロジェクトコードを取得
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/vbenjs/vue-vben-admin.git
|
git clone https://github.com/vbenjs/vue-vben-admin.git
|
||||||
```
|
```
|
||||||
|
|
||||||
- 依存関係のインストール
|
2. 依存関係のインストール
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd vue-vben-admin
|
cd vue-vben-admin
|
||||||
|
npm i -g corepack
|
||||||
corepack enable
|
|
||||||
|
|
||||||
pnpm install
|
pnpm install
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- 実行
|
3. 実行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
- ビルド
|
4. ビルド
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm build
|
pnpm build
|
||||||
@@ -86,40 +88,39 @@ pnpm build
|
|||||||
|
|
||||||
ご参加をお待ちしておりますするか、Pull Requestを送信してください。
|
ご参加をお待ちしておりますするか、Pull Requestを送信してください。
|
||||||
|
|
||||||
**Pull Request:**
|
**Pull Request プロセス:**
|
||||||
|
|
||||||
1. コードをフォーク!
|
1. コードをフォーク
|
||||||
2. 自分のブランチを作成: `git checkout -b feat/xxxx`
|
2. 自分のブランチを作成:`git checkout -b feat/xxxx`
|
||||||
3. 変更をコミット: `git commit -am 'feat(function): add xxxxx'`
|
3. 変更をコミット:`git commit -am 'feat(function): add xxxxx'`
|
||||||
4. ブランチをプッシュ: `git push origin feat/xxxx`
|
4. ブランチをプッシュ:`git push origin feat/xxxx`
|
||||||
5. `pull request`を送信
|
5. `pull request`を送信
|
||||||
|
|
||||||
## Git貢献提出規則
|
## Git貢献提出規則
|
||||||
|
|
||||||
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 規則 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 規則 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||||
|
|
||||||
- `feat` 新機能の追加
|
- `feat` 新機能の追加
|
||||||
- `fix` 問題/バグの修正
|
- `fix` 問題/バグの修正
|
||||||
- `style` コードスタイルに関連し、実行結果に影響しない
|
- `style` コードスタイルに関連し、実行結果に影響しない
|
||||||
- `perf` 最適化/パフォーマンス向上
|
- `perf` 最適化/パフォーマンス向上
|
||||||
- `refactor` リファクタリング
|
- `refactor` リファクタリング
|
||||||
- `revert` 変更の取り消し
|
- `revert` 変更の取り消し
|
||||||
- `test` テスト関連
|
- `test` テスト関連
|
||||||
- `docs` ドキュメント/注釈
|
- `docs` ドキュメント/注釈
|
||||||
- `chore` 依存関係の更新/スキャフォールディング設定の変更など
|
- `chore` 依存関係の更新/スキャフォールディング設定の変更など
|
||||||
- `ci` 継続的インテグレーション
|
- `ci` 継続的インテグレーション
|
||||||
- `types` 型定義ファイルの変更
|
- `types` 型定義ファイルの変更
|
||||||
- `wip` 開発中
|
|
||||||
|
|
||||||
## ブラウザサポート
|
## ブラウザサポート
|
||||||
|
|
||||||
ローカル開発には`Chrome 80+`ブラウザを推奨します
|
ローカル開発には `Chrome 80+` ブラウザを推奨します
|
||||||
|
|
||||||
モダンブラウザをサポートし、IEはサポートしません
|
モダンブラウザをサポートし、IEはサポートしません
|
||||||
|
|
||||||
| [<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/)</br>IE | [<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/)</br>Edge | [<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/)</br>Firefox | [<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/)</br>Chrome | [<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/)</br>Safari |
|
| [<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/)</br>Edge | [<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/)</br>Firefox | [<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/)</br>Chrome | [<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/)</br>Safari |
|
||||||
| :-: | :-: | :-: | :-: | :-: |
|
| :-: | :-: | :-: | :-: |
|
||||||
| サポートしない | 最新2バージョン | 最新2バージョン | 最新2バージョン | 最新2バージョン |
|
| 最新2バージョン | 最新2バージョン | 最新2バージョン | 最新2バージョン |
|
||||||
|
|
||||||
## メンテナー
|
## メンテナー
|
||||||
|
|
||||||
@@ -140,8 +141,7 @@ pnpm build
|
|||||||
## 貢献者
|
## 貢献者
|
||||||
|
|
||||||
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
||||||
<img alt="Contributors"
|
<img alt="Contributors" src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
|
||||||
src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
## Discord
|
## Discord
|
||||||
|
|||||||
87
README.md
87
README.md
@@ -1,8 +1,13 @@
|
|||||||
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp"> </a> <br> <br>
|
<div align="center">
|
||||||
|
<a href="https://github.com/anncwb/vue-vben-admin">
|
||||||
|
<img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp">
|
||||||
|
</a>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
<h1>Vue Vben Admin</h1>
|
<h1>Vue Vben Admin</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
[](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin)    
|
[](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin)    
|
||||||
@@ -17,7 +22,7 @@ Vue Vben Admin is a free and open source middle and back-end template. Using the
|
|||||||
|
|
||||||
This is the latest version, 5.0, and it is not compatible with previous versions. If you are starting a new project, it is recommended to use the latest version. If you wish to view the old version, please use the [v2 branch](https://github.com/vbenjs/vue-vben-admin/tree/v2).
|
This is the latest version, 5.0, and it is not compatible with previous versions. If you are starting a new project, it is recommended to use the latest version. If you wish to view the old version, please use the [v2 branch](https://github.com/vbenjs/vue-vben-admin/tree/v2).
|
||||||
|
|
||||||
## Feature
|
## Features
|
||||||
|
|
||||||
- **Latest Technology Stack**: Developed with cutting-edge front-end technologies like Vue 3 and Vite
|
- **Latest Technology Stack**: Developed with cutting-edge front-end technologies like Vue 3 and Vite
|
||||||
- **TypeScript**: A language for application-scale JavaScript
|
- **TypeScript**: A language for application-scale JavaScript
|
||||||
@@ -31,11 +36,11 @@ This is the latest version, 5.0, and it is not compatible with previous versions
|
|||||||
|
|
||||||
Test Account: vben/123456
|
Test Account: vben/123456
|
||||||
|
|
||||||
<p align="center">
|
<div align="center">
|
||||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
|
||||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
|
||||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
|
||||||
</p>
|
</div>
|
||||||
|
|
||||||
### Use Gitpod
|
### Use Gitpod
|
||||||
|
|
||||||
@@ -47,31 +52,29 @@ Open the project in Gitpod (free online dev environment for GitHub) and start co
|
|||||||
|
|
||||||
[Document](https://doc.vben.pro/)
|
[Document](https://doc.vben.pro/)
|
||||||
|
|
||||||
## Install and use
|
## Install and Use
|
||||||
|
|
||||||
- Get the project code
|
1. Get the project code
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/vbenjs/vue-vben-admin.git
|
git clone https://github.com/vbenjs/vue-vben-admin.git
|
||||||
```
|
```
|
||||||
|
|
||||||
- Installation dependencies
|
2. Install dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd vue-vben-admin
|
cd vue-vben-admin
|
||||||
|
npm i -g corepack
|
||||||
corepack enable
|
|
||||||
|
|
||||||
pnpm install
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
- run
|
3. Run
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
- build
|
4. Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm build
|
pnpm build
|
||||||
@@ -81,44 +84,43 @@ pnpm build
|
|||||||
|
|
||||||
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
|
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
|
||||||
|
|
||||||
## How to contribute
|
## How to Contribute
|
||||||
|
|
||||||
You are very welcome to join Or submit a Pull Request。
|
You are very welcome to join! [Raise an issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) or submit a Pull Request.
|
||||||
|
|
||||||
**Pull Request:**
|
**Pull Request Process:**
|
||||||
|
|
||||||
1. Fork code!
|
1. Fork the code
|
||||||
2. Create your own branch: `git checkout -b feat/xxxx`
|
2. Create your branch: `git checkout -b feat/xxxx`
|
||||||
3. Submit your changes: `git commit -am 'feat(function): add xxxxx'`
|
3. Submit your changes: `git commit -am 'feat(function): add xxxxx'`
|
||||||
4. Push your branch: `git push origin feat/xxxx`
|
4. Push your branch: `git push origin feat/xxxx`
|
||||||
5. submit`pull request`
|
5. Submit `pull request`
|
||||||
|
|
||||||
## Git Contribution submission specification
|
## Git Contribution Submission Specification
|
||||||
|
|
||||||
- reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) specification ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
Reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) specification ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||||
|
|
||||||
- `feat` Add new features
|
- `feat` Add new features
|
||||||
- `fix` Fix the problem/BUG
|
- `fix` Fix the problem/BUG
|
||||||
- `style` The code style is related and does not affect the running result
|
- `style` The code style is related and does not affect the running result
|
||||||
- `perf` Optimization/performance improvement
|
- `perf` Optimization/performance improvement
|
||||||
- `refactor` Refactor
|
- `refactor` Refactor
|
||||||
- `revert` Undo edit
|
- `revert` Undo edit
|
||||||
- `test` Test related
|
- `test` Test related
|
||||||
- `docs` Documentation/notes
|
- `docs` Documentation/notes
|
||||||
- `chore` Dependency update/scaffolding configuration modification etc.
|
- `chore` Dependency update/scaffolding configuration modification etc.
|
||||||
- `ci` Continuous integration
|
- `ci` Continuous integration
|
||||||
- `types` Type definition file changes
|
- `types` Type definition file changes
|
||||||
- `wip` In development
|
|
||||||
|
|
||||||
## Browser support
|
## Browser Support
|
||||||
|
|
||||||
The `Chrome 80+` browser is recommended for local development
|
The `Chrome 80+` browser is recommended for local development
|
||||||
|
|
||||||
Support modern browsers, not IE
|
Support modern browsers, not IE
|
||||||
|
|
||||||
| [<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/)</br>IE | [<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/)</br>Edge | [<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/)</br>Firefox | [<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/)</br>Chrome | [<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/)</br>Safari |
|
| [<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/)</br>Edge | [<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/)</br>Firefox | [<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/)</br>Chrome | [<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/)</br>Safari |
|
||||||
| :-: | :-: | :-: | :-: | :-: |
|
| :-: | :-: | :-: | :-: |
|
||||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
| last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||||
|
|
||||||
## Maintainer
|
## Maintainer
|
||||||
|
|
||||||
@@ -136,11 +138,10 @@ If you think this project is helpful to you, you can help the author buy a cup o
|
|||||||
|
|
||||||
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aee;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aee;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
||||||
|
|
||||||
## Contributor
|
## Contributors
|
||||||
|
|
||||||
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
||||||
<img alt="Contributors"
|
<img alt="Contributors" src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
|
||||||
src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
## Discord
|
## Discord
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ admin 账号: admin admin123
|
|||||||
git clone https://gitee.com/dapppp/ruoyi-plus-vben5.git
|
git clone https://gitee.com/dapppp/ruoyi-plus-vben5.git
|
||||||
```
|
```
|
||||||
|
|
||||||
- 安装依赖
|
2. 安装依赖
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ruoyi-plus-vben5
|
cd ruoyi-plus-vben5
|
||||||
@@ -150,7 +150,7 @@ VITE_GLOB_WEBSOCKET_ENABLE=false
|
|||||||
pnpm dev:antd
|
pnpm dev:antd
|
||||||
```
|
```
|
||||||
|
|
||||||
- 打包
|
4. 打包
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm build:antd
|
pnpm build:antd
|
||||||
@@ -164,21 +164,21 @@ pnpm build:antd
|
|||||||
|
|
||||||
## Git 贡献提交规范
|
## Git 贡献提交规范
|
||||||
|
|
||||||
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||||
|
|
||||||
- `feat` 增加新功能
|
- `feat` 增加新功能
|
||||||
- `fix` 修复问题/BUG
|
- `fix` 修复问题/BUG
|
||||||
- `style` 代码风格相关无影响运行结果的
|
- `style` 代码风格相关无影响运行结果的
|
||||||
- `perf` 优化/性能提升
|
- `perf` 优化/性能提升
|
||||||
- `refactor` 重构
|
- `refactor` 重构
|
||||||
- `revert` 撤销修改
|
- `revert` 撤销修改
|
||||||
- `test` 测试相关
|
- `test` 测试相关
|
||||||
- `docs` 文档/注释
|
- `docs` 文档/注释
|
||||||
- `chore` 依赖更新/脚手架配置修改等
|
- `chore` 依赖更新/脚手架配置修改等
|
||||||
- `workflow` 工作流改进
|
- `workflow` 工作流改进
|
||||||
- `ci` 持续集成
|
- `ci` 持续集成
|
||||||
- `types` 类型定义文件更改
|
- `types` 类型定义文件更改
|
||||||
- `wip` 开发中
|
- `wip` 开发中
|
||||||
|
|
||||||
## 浏览器支持
|
## 浏览器支持
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ pnpm build:antd
|
|||||||
|
|
||||||
本地开发推荐使用`Chrome` 最新版本浏览器
|
本地开发推荐使用`Chrome` 最新版本浏览器
|
||||||
|
|
||||||
支持现代浏览器, 不支持 IE
|
支持现代浏览器,不支持 IE
|
||||||
|
|
||||||
| [<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/)</br>IE | [<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/)</br>Edge | [<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/)</br>Firefox | [<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/)</br>Chrome | [<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/)</br>Safari |
|
| [<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/)</br>IE | [<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/)</br>Edge | [<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/)</br>Firefox | [<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/)</br>Chrome | [<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/)</br>Safari |
|
||||||
| :-: | :-: | :-: | :-: | :-: |
|
| :-: | :-: | :-: | :-: | :-: |
|
||||||
|
|||||||
13
apps/backend-mock/api/upload.ts
Normal file
13
apps/backend-mock/api/upload.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
|
import { unAuthorizedResponse } from '~/utils/response';
|
||||||
|
|
||||||
|
export default eventHandler((event) => {
|
||||||
|
const userinfo = verifyAccessToken(event);
|
||||||
|
if (!userinfo) {
|
||||||
|
return unAuthorizedResponse(event);
|
||||||
|
}
|
||||||
|
return useResponseSuccess({
|
||||||
|
url: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||||
|
});
|
||||||
|
// return useResponseError("test")
|
||||||
|
});
|
||||||
@@ -7,6 +7,7 @@ export default defineEventHandler(() => {
|
|||||||
<li><a href="/api/menu">/api/menu/all</a></li>
|
<li><a href="/api/menu">/api/menu/all</a></li>
|
||||||
<li><a href="/api/auth/codes">/api/auth/codes</a></li>
|
<li><a href="/api/auth/codes">/api/auth/codes</a></li>
|
||||||
<li><a href="/api/auth/login">/api/auth/login</a></li>
|
<li><a href="/api/auth/login">/api/auth/login</a></li>
|
||||||
|
<li><a href="/api/upload">/api/upload</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,3 +3,6 @@ VITE_APP_TITLE=Plus Admin
|
|||||||
|
|
||||||
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
|
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
|
||||||
VITE_APP_NAMESPACE=vben-web-antd
|
VITE_APP_NAMESPACE=vben-web-antd
|
||||||
|
|
||||||
|
# 对store进行加密的密钥,在将store持久化到localStorage时会使用该密钥进行加密
|
||||||
|
VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-antd",
|
"name": "@vben/web-antd",
|
||||||
"version": "1.3.0",
|
"version": "1.3.2",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import type { BaseFormComponentType } from '@vben/common-ui';
|
|||||||
import type { Recordable } from '@vben/types';
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
computed,
|
|
||||||
defineAsyncComponent,
|
defineAsyncComponent,
|
||||||
defineComponent,
|
defineComponent,
|
||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
@@ -88,18 +87,13 @@ const withDefaultPlaceholder = <T extends Component>(
|
|||||||
componentProps: Recordable<any> = {},
|
componentProps: Recordable<any> = {},
|
||||||
) => {
|
) => {
|
||||||
return defineComponent({
|
return defineComponent({
|
||||||
inheritAttrs: false,
|
|
||||||
name: component.name,
|
name: component.name,
|
||||||
|
inheritAttrs: false,
|
||||||
setup: (props: any, { attrs, expose, slots }) => {
|
setup: (props: any, { attrs, expose, slots }) => {
|
||||||
/**
|
const placeholder =
|
||||||
* 需要使用computed 否则后续updateSchema更新的placeholder无法显示(响应式问题)
|
props?.placeholder ||
|
||||||
*/
|
attrs?.placeholder ||
|
||||||
const placeholder = computed(
|
$t(`ui.placeholder.${type}`);
|
||||||
() =>
|
|
||||||
props?.placeholder ||
|
|
||||||
attrs?.placeholder ||
|
|
||||||
$t(`ui.placeholder.${type}`),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 透传组件暴露的方法
|
// 透传组件暴露的方法
|
||||||
const innerRef = ref();
|
const innerRef = ref();
|
||||||
@@ -118,7 +112,7 @@ const withDefaultPlaceholder = <T extends Component>(
|
|||||||
component,
|
component,
|
||||||
{
|
{
|
||||||
...componentProps,
|
...componentProps,
|
||||||
placeholder: placeholder.value,
|
placeholder,
|
||||||
...props,
|
...props,
|
||||||
...attrs,
|
...attrs,
|
||||||
ref: innerRef,
|
ref: innerRef,
|
||||||
@@ -168,20 +162,34 @@ async function initComponentAdapter() {
|
|||||||
// 如果你的组件体积比较大,可以使用异步加载
|
// 如果你的组件体积比较大,可以使用异步加载
|
||||||
// Button: () =>
|
// Button: () =>
|
||||||
// import('xxx').then((res) => res.Button),
|
// import('xxx').then((res) => res.Button),
|
||||||
ApiSelect: withDefaultPlaceholder(ApiComponent, 'select', {
|
ApiSelect: withDefaultPlaceholder(
|
||||||
component: Select,
|
{
|
||||||
loadingSlot: 'suffixIcon',
|
...ApiComponent,
|
||||||
visibleEvent: 'onDropdownVisibleChange',
|
name: 'ApiSelect',
|
||||||
modelPropName: 'value',
|
},
|
||||||
}),
|
'select',
|
||||||
ApiTreeSelect: withDefaultPlaceholder(ApiComponent, 'select', {
|
{
|
||||||
component: TreeSelect,
|
component: Select,
|
||||||
fieldNames: { label: 'label', value: 'value', children: 'children' },
|
loadingSlot: 'suffixIcon',
|
||||||
loadingSlot: 'suffixIcon',
|
visibleEvent: 'onDropdownVisibleChange',
|
||||||
modelPropName: 'value',
|
modelPropName: 'value',
|
||||||
optionsPropName: 'treeData',
|
},
|
||||||
visibleEvent: 'onVisibleChange',
|
),
|
||||||
}),
|
ApiTreeSelect: withDefaultPlaceholder(
|
||||||
|
{
|
||||||
|
...ApiComponent,
|
||||||
|
name: 'ApiTreeSelect',
|
||||||
|
},
|
||||||
|
'select',
|
||||||
|
{
|
||||||
|
component: TreeSelect,
|
||||||
|
fieldNames: { label: 'label', value: 'value', children: 'children' },
|
||||||
|
loadingSlot: 'suffixIcon',
|
||||||
|
modelPropName: 'value',
|
||||||
|
optionsPropName: 'treeData',
|
||||||
|
visibleEvent: 'onVisibleChange',
|
||||||
|
},
|
||||||
|
),
|
||||||
AutoComplete,
|
AutoComplete,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
CheckboxGroup,
|
CheckboxGroup,
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ Upload.Dragger只会影响样式
|
|||||||
{{ $t('component.upload.upload') }}
|
{{ $t('component.upload.upload') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="enableDragUpload && innerFileList?.length < maxCount">
|
<div v-if="enableDragUpload">
|
||||||
<p class="ant-upload-drag-icon">
|
<p class="ant-upload-drag-icon">
|
||||||
<InboxOutlined />
|
<InboxOutlined />
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ import type { RouteRecordStringComponent } from '@vben/types';
|
|||||||
|
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
const {
|
||||||
|
version,
|
||||||
|
// vite inject-metadata 插件注入的全局变量
|
||||||
|
} = __VBEN_ADMIN_METADATA__ || {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 该文件放非后台返回的路由 比如个人中心 等需要跳转显示的页面
|
* 该文件放非后台返回的路由 比如个人中心 等需要跳转显示的页面
|
||||||
*/
|
*/
|
||||||
@@ -134,8 +139,8 @@ export const localMenuList: RouteRecordStringComponent[] = [
|
|||||||
icon: 'lucide:book-open-text',
|
icon: 'lucide:book-open-text',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
title: '更新记录',
|
title: '更新记录',
|
||||||
badge: '1.3.0',
|
badge: `当前: ${version}`,
|
||||||
badgeVariants: '#CC0033',
|
badgeVariants: 'bg-primary',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 120,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 120,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -95,7 +95,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -39,11 +39,13 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
{
|
{
|
||||||
field: 'orderNum',
|
field: 'orderNum',
|
||||||
title: '排序',
|
title: '排序',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'status',
|
field: 'status',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
title: '状态',
|
title: '状态',
|
||||||
slots: {
|
slots: {
|
||||||
default: ({ row }) => {
|
default: ({ row }) => {
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
|
|
||||||
const { dictCode, dictType } = drawerApi.getData() as DrawerProps;
|
const { dictCode, dictType } = drawerApi.getData() as DrawerProps;
|
||||||
isUpdate.value = !!dictCode;
|
isUpdate.value = !!dictCode;
|
||||||
formApi.setFieldValue('dictType', dictType);
|
await formApi.setFieldValue('dictType', dictType);
|
||||||
|
|
||||||
if (dictCode && isUpdate.value) {
|
if (dictCode && isUpdate.value) {
|
||||||
const record = await dictDetailInfo(dictCode);
|
const record = await dictDetailInfo(dictCode);
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -145,7 +145,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 200,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { addFullName, cloneDeep } from '@vben/utils';
|
|||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import { postAdd, postInfo, postUpdate } from '#/api/system/post';
|
import { postAdd, postInfo, postUpdate } from '#/api/system/post';
|
||||||
import { getDeptTree } from '#/api/system/user';
|
import { getDeptTree } from '#/api/system/user';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
|
|
||||||
@@ -50,8 +51,16 @@ async function setupDeptSelect() {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
|
{
|
||||||
|
initializedGetter: defaultFormValueGetter(formApi),
|
||||||
|
currentGetter: defaultFormValueGetter(formApi),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [BasicDrawer, drawerApi] = useVbenDrawer({
|
const [BasicDrawer, drawerApi] = useVbenDrawer({
|
||||||
onCancel: handleCancel,
|
onBeforeClose,
|
||||||
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
async onOpenChange(isOpen) {
|
async onOpenChange(isOpen) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
@@ -67,31 +76,33 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
const record = await postInfo(id);
|
const record = await postInfo(id);
|
||||||
await formApi.setValues(record);
|
await formApi.setValues(record);
|
||||||
}
|
}
|
||||||
|
await markInitialized();
|
||||||
drawerApi.drawerLoading(false);
|
drawerApi.drawerLoading(false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handleConfirm() {
|
async function handleConfirm() {
|
||||||
try {
|
try {
|
||||||
drawerApi.drawerLoading(true);
|
drawerApi.lock(true);
|
||||||
const { valid } = await formApi.validate();
|
const { valid } = await formApi.validate();
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = cloneDeep(await formApi.getValues());
|
const data = cloneDeep(await formApi.getValues());
|
||||||
await (isUpdate.value ? postUpdate(data) : postAdd(data));
|
await (isUpdate.value ? postUpdate(data) : postAdd(data));
|
||||||
|
resetInitialized();
|
||||||
emit('reload');
|
emit('reload');
|
||||||
await handleCancel();
|
drawerApi.close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
drawerApi.drawerLoading(false);
|
drawerApi.lock(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCancel() {
|
async function handleClosed() {
|
||||||
drawerApi.close();
|
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
|
resetInitialized();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -94,7 +94,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -226,7 +226,11 @@ function handleAssignRole(record: Role) {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</template>
|
</template>
|
||||||
<a-button size="small" type="link">
|
<a-button
|
||||||
|
size="small"
|
||||||
|
type="link"
|
||||||
|
v-access:code="'system:role:edit'"
|
||||||
|
>
|
||||||
{{ $t('pages.common.more') }}
|
{{ $t('pages.common.more') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
|||||||
const [BasicModal, modalApi] = useVbenModal({
|
const [BasicModal, modalApi] = useVbenModal({
|
||||||
fullscreenButton: false,
|
fullscreenButton: false,
|
||||||
onBeforeClose,
|
onBeforeClose,
|
||||||
onCancel: handleClosed,
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
onOpenChange: async (isOpen) => {
|
onOpenChange: async (isOpen) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 200,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
resizable: false,
|
resizable: false,
|
||||||
width: 180,
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
} from '@vben/icons';
|
} from '@vben/icons';
|
||||||
|
|
||||||
import { useClipboard } from '@vueuse/core';
|
import { useClipboard } from '@vueuse/core';
|
||||||
import { Skeleton, Tree } from 'ant-design-vue';
|
import { Alert, Skeleton, Tree } from 'ant-design-vue';
|
||||||
|
|
||||||
import { previewCode } from '#/api/tool/gen';
|
import { previewCode } from '#/api/tool/gen';
|
||||||
|
|
||||||
@@ -185,6 +185,11 @@ const { copy } = useClipboard({ legacy: true });
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Tree>
|
</Tree>
|
||||||
|
<Alert
|
||||||
|
class="mt-2"
|
||||||
|
show-icon
|
||||||
|
message="👆显示的名称为模板的文件名,非最终下载文件名..."
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
v-model="codeContent"
|
v-model="codeContent"
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 200,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 210,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { PropType } from 'vue';
|
||||||
|
|
||||||
import type { CategoryTree } from '#/api/workflow/category/model';
|
import type { CategoryTree } from '#/api/workflow/category/model';
|
||||||
|
|
||||||
import { onMounted, type PropType, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
import { SyncOutlined } from '@ant-design/icons-vue';
|
import { SyncOutlined } from '@ant-design/icons-vue';
|
||||||
import { InputSearch, Skeleton, Tree } from 'ant-design-vue';
|
import { InputSearch, Skeleton, Tree } from 'ant-design-vue';
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export const columns: VxeGridProps['columns'] = [
|
|||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
resizable: false,
|
resizable: false,
|
||||||
width: 200,
|
width: 'auto',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
|||||||
|
|
||||||
const [BasicDrawer, modalApi] = useVbenModal({
|
const [BasicDrawer, modalApi] = useVbenModal({
|
||||||
onBeforeClose,
|
onBeforeClose,
|
||||||
onCancel: handleClosed,
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
async onOpenChange(isOpen) {
|
async onOpenChange(isOpen) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { categoryTree } from '#/api/workflow/category';
|
|||||||
import { pageByAllTaskFinish, pageByAllTaskWait } from '#/api/workflow/task';
|
import { pageByAllTaskFinish, pageByAllTaskWait } from '#/api/workflow/task';
|
||||||
|
|
||||||
import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
|
import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
|
||||||
|
import { bottomOffset } from './constant';
|
||||||
|
|
||||||
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||||
|
|
||||||
@@ -140,7 +141,9 @@ const handleScroll = debounce(async (e: Event) => {
|
|||||||
// e.target.scrollHeight 是元素的总高度。
|
// e.target.scrollHeight 是元素的总高度。
|
||||||
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
||||||
// 判断是否滚动到底部
|
// 判断是否滚动到底部
|
||||||
const isBottom = scrollTop + clientHeight >= scrollHeight;
|
const isBottom = scrollTop + clientHeight >= scrollHeight - bottomOffset;
|
||||||
|
console.log('scrollTop + clientHeight', scrollTop + clientHeight);
|
||||||
|
console.log('scrollHeight', scrollHeight);
|
||||||
|
|
||||||
// 滚动到底部且没有加载完成
|
// 滚动到底部且没有加载完成
|
||||||
if (isBottom && !isLoadComplete.value) {
|
if (isBottom && !isLoadComplete.value) {
|
||||||
|
|||||||
7
apps/web-antd/src/views/workflow/task/constant.ts
Normal file
7
apps/web-antd/src/views/workflow/task/constant.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* 底部偏移量
|
||||||
|
* 在缩放时会差大概0.5px 导致触底逻辑不会触发
|
||||||
|
* 在这里设置手动补偿
|
||||||
|
* @see https://gitee.com/dapppp/ruoyi-plus-vben5/issues/IC28RE#note_40175381
|
||||||
|
*/
|
||||||
|
export const bottomOffset = 2;
|
||||||
@@ -24,6 +24,7 @@ import { cloneDeep, debounce } from 'lodash-es';
|
|||||||
import { pageByCurrent } from '#/api/workflow/instance';
|
import { pageByCurrent } from '#/api/workflow/instance';
|
||||||
|
|
||||||
import { ApprovalCard, ApprovalPanel } from '../components';
|
import { ApprovalCard, ApprovalPanel } from '../components';
|
||||||
|
import { bottomOffset } from './constant';
|
||||||
|
|
||||||
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ const handleScroll = debounce(async (e: Event) => {
|
|||||||
// e.target.scrollHeight 是元素的总高度。
|
// e.target.scrollHeight 是元素的总高度。
|
||||||
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
||||||
// 判断是否滚动到底部
|
// 判断是否滚动到底部
|
||||||
const isBottom = scrollTop + clientHeight >= scrollHeight;
|
const isBottom = scrollTop + clientHeight >= scrollHeight - bottomOffset;
|
||||||
|
|
||||||
// 滚动到底部且没有加载完成
|
// 滚动到底部且没有加载完成
|
||||||
if (isBottom && !isLoadComplete.value) {
|
if (isBottom && !isLoadComplete.value) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { categoryTree } from '#/api/workflow/category';
|
|||||||
import { pageByTaskCopy } from '#/api/workflow/task';
|
import { pageByTaskCopy } from '#/api/workflow/task';
|
||||||
|
|
||||||
import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
|
import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
|
||||||
|
import { bottomOffset } from './constant';
|
||||||
|
|
||||||
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ const handleScroll = debounce(async (e: Event) => {
|
|||||||
// e.target.scrollHeight 是元素的总高度。
|
// e.target.scrollHeight 是元素的总高度。
|
||||||
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
||||||
// 判断是否滚动到底部
|
// 判断是否滚动到底部
|
||||||
const isBottom = scrollTop + clientHeight >= scrollHeight;
|
const isBottom = scrollTop + clientHeight >= scrollHeight - bottomOffset;
|
||||||
|
|
||||||
// 滚动到底部且没有加载完成
|
// 滚动到底部且没有加载完成
|
||||||
if (isBottom && !isLoadComplete.value) {
|
if (isBottom && !isLoadComplete.value) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { categoryTree } from '#/api/workflow/category';
|
|||||||
import { pageByTaskFinish } from '#/api/workflow/task';
|
import { pageByTaskFinish } from '#/api/workflow/task';
|
||||||
|
|
||||||
import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
|
import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
|
||||||
|
import { bottomOffset } from './constant';
|
||||||
|
|
||||||
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ const handleScroll = debounce(async (e: Event) => {
|
|||||||
// e.target.scrollHeight 是元素的总高度。
|
// e.target.scrollHeight 是元素的总高度。
|
||||||
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
||||||
// 判断是否滚动到底部
|
// 判断是否滚动到底部
|
||||||
const isBottom = scrollTop + clientHeight >= scrollHeight;
|
const isBottom = scrollTop + clientHeight >= scrollHeight - bottomOffset;
|
||||||
|
|
||||||
// 滚动到底部且没有加载完成
|
// 滚动到底部且没有加载完成
|
||||||
if (isBottom && !isLoadComplete.value) {
|
if (isBottom && !isLoadComplete.value) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { categoryTree } from '#/api/workflow/category';
|
|||||||
import { pageByTaskWait } from '#/api/workflow/task';
|
import { pageByTaskWait } from '#/api/workflow/task';
|
||||||
|
|
||||||
import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
|
import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
|
||||||
|
import { bottomOffset } from './constant';
|
||||||
|
|
||||||
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ const handleScroll = debounce(async (e: Event) => {
|
|||||||
// e.target.scrollHeight 是元素的总高度。
|
// e.target.scrollHeight 是元素的总高度。
|
||||||
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
const { scrollTop, clientHeight, scrollHeight } = e.target as HTMLElement;
|
||||||
// 判断是否滚动到底部
|
// 判断是否滚动到底部
|
||||||
const isBottom = scrollTop + clientHeight >= scrollHeight;
|
const isBottom = scrollTop + clientHeight >= scrollHeight - bottomOffset;
|
||||||
|
|
||||||
// 滚动到底部且没有加载完成
|
// 滚动到底部且没有加载完成
|
||||||
if (isBottom && !isLoadComplete.value) {
|
if (isBottom && !isLoadComplete.value) {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
import { Space } from 'ant-design-vue';
|
import { Space } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
import { sseList } from './api';
|
import { sseList } from './api';
|
||||||
import sendMsgModal from './send-msg-modal.vue';
|
import sendMsgModal from './send-msg-modal.vue';
|
||||||
@@ -31,7 +33,8 @@ const gridOptions: VxeGridProps = {
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'action' },
|
slots: { default: 'action' },
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 180,
|
resizable: false,
|
||||||
|
width: 'auto',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
|
|||||||
@@ -104,6 +104,11 @@
|
|||||||
--vp-custom-block-tip-text: var(--vp-c-text-1);
|
--vp-custom-block-tip-text: var(--vp-c-text-1);
|
||||||
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
|
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
|
||||||
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
|
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modal zIndex
|
||||||
|
*/
|
||||||
|
--popup-z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/docs",
|
"name": "@vben/docs",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vitepress build",
|
"build": "vitepress build",
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ Alert提供的功能与Modal类似,但只适用于简单应用场景。例如
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
::: tip 注意
|
||||||
|
|
||||||
|
Alert提供的快捷方法alert、confirm、prompt动态创建的弹窗在已打开的情况下,不支持HMR(热更新),代码变更后需要关闭这些弹窗后重新打开。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
::: tip README
|
::: tip README
|
||||||
|
|
||||||
下方示例代码中的,存在一些主题色未适配、样式缺失的问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
|
下方示例代码中的,存在一些主题色未适配、样式缺失的问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
|
||||||
@@ -32,6 +38,23 @@ Alert提供的功能与Modal类似,但只适用于简单应用场景。例如
|
|||||||
|
|
||||||
<DemoPreview dir="demos/vben-alert/prompt" />
|
<DemoPreview dir="demos/vben-alert/prompt" />
|
||||||
|
|
||||||
|
## useAlertContext
|
||||||
|
|
||||||
|
当弹窗的content、footer、icon使用自定义组件时,在这些组件中可以使用 `useAlertContext` 获取当前弹窗的上下文对象,用来主动控制弹窗。
|
||||||
|
|
||||||
|
::: tip 注意
|
||||||
|
|
||||||
|
`useAlertContext`只能用在setup或者函数式组件中。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
| 方法 | 描述 | 类型 | 版本要求 |
|
||||||
|
| --------- | ------------------ | -------- | -------- |
|
||||||
|
| doConfirm | 调用弹窗的确认操作 | ()=>void | >5.5.4 |
|
||||||
|
| doCancel | 调用弹窗的取消操作 | ()=>void | >5.5.4 |
|
||||||
|
|
||||||
## 类型说明
|
## 类型说明
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@@ -69,8 +92,14 @@ export type AlertProps = {
|
|||||||
contentClass?: string;
|
contentClass?: string;
|
||||||
/** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/
|
/** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/
|
||||||
contentMasking?: boolean;
|
contentMasking?: boolean;
|
||||||
|
/** 弹窗底部内容(与按钮在同一个容器中) */
|
||||||
|
footer?: Component | string;
|
||||||
/** 弹窗的图标(在标题的前面) */
|
/** 弹窗的图标(在标题的前面) */
|
||||||
icon?: Component | IconType;
|
icon?: Component | IconType;
|
||||||
|
/**
|
||||||
|
* 弹窗遮罩模糊效果
|
||||||
|
*/
|
||||||
|
overlayBlur?: number;
|
||||||
/** 是否显示取消按钮 */
|
/** 是否显示取消按钮 */
|
||||||
showCancel?: boolean;
|
showCancel?: boolean;
|
||||||
/** 弹窗标题 */
|
/** 弹窗标题 */
|
||||||
|
|||||||
@@ -131,26 +131,37 @@ function fetchApi(): Promise<Record<string, any>> {
|
|||||||
|
|
||||||
### Props
|
### Props
|
||||||
|
|
||||||
| 属性名 | 描述 | 类型 | 默认值 |
|
| 属性名 | 描述 | 类型 | 默认值 | 版本要求 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| modelValue(v-model) | 当前值 | `any` | - |
|
| modelValue(v-model) | 当前值 | `any` | - | - |
|
||||||
| component | 欲包装的组件(以下称为目标组件) | `Component` | - |
|
| component | 欲包装的组件(以下称为目标组件) | `Component` | - | - |
|
||||||
| numberToString | 是否将value从数字转为string | `boolean` | `false` |
|
| numberToString | 是否将value从数字转为string | `boolean` | `false` | - |
|
||||||
| api | 获取数据的函数 | `(arg?: any) => Promise<OptionsItem[] \| Record<string, any>>` | - |
|
| api | 获取数据的函数 | `(arg?: any) => Promise<OptionsItem[] \| Record<string, any>>` | - | - |
|
||||||
| params | 传递给api的参数 | `Record<string, any>` | - |
|
| params | 传递给api的参数 | `Record<string, any>` | - | - |
|
||||||
| resultField | 从api返回的结果中提取options数组的字段名 | `string` | - |
|
| resultField | 从api返回的结果中提取options数组的字段名 | `string` | - | - |
|
||||||
| labelField | label字段名 | `string` | `label` |
|
| labelField | label字段名 | `string` | `label` | - |
|
||||||
| childrenField | 子级数据字段名,需要层级数据的组件可用 | `string` | `` |
|
| childrenField | 子级数据字段名,需要层级数据的组件可用 | `string` | `` | - |
|
||||||
| valueField | value字段名 | `string` | `value` |
|
| valueField | value字段名 | `string` | `value` | - |
|
||||||
| optionsPropName | 目标组件接收options数据的属性名称 | `string` | `options` |
|
| optionsPropName | 目标组件接收options数据的属性名称 | `string` | `options` | - |
|
||||||
| modelPropName | 目标组件的双向绑定属性名,默认为modelValue。部分组件可能为value | `string` | `modelValue` |
|
| modelPropName | 目标组件的双向绑定属性名,默认为modelValue。部分组件可能为value | `string` | `modelValue` | - |
|
||||||
| immediate | 是否立即调用api | `boolean` | `true` |
|
| immediate | 是否立即调用api | `boolean` | `true` | - |
|
||||||
| alwaysLoad | 每次`visibleEvent`事件发生时都重新请求数据 | `boolean` | `false` |
|
| alwaysLoad | 每次`visibleEvent`事件发生时都重新请求数据 | `boolean` | `false` | - |
|
||||||
| beforeFetch | 在api请求之前的回调函数 | `AnyPromiseFunction<any, any>` | - |
|
| beforeFetch | 在api请求之前的回调函数 | `AnyPromiseFunction<any, any>` | - | - |
|
||||||
| afterFetch | 在api请求之后的回调函数 | `AnyPromiseFunction<any, any>` | - |
|
| afterFetch | 在api请求之后的回调函数 | `AnyPromiseFunction<any, any>` | - | - |
|
||||||
| options | 直接传入选项数据,也作为api返回空数据时的后备数据 | `OptionsItem[]` | - |
|
| options | 直接传入选项数据,也作为api返回空数据时的后备数据 | `OptionsItem[]` | - | - |
|
||||||
| visibleEvent | 触发重新请求数据的事件名 | `string` | - |
|
| visibleEvent | 触发重新请求数据的事件名 | `string` | - | - |
|
||||||
| loadingSlot | 目标组件的插槽名称,用来显示一个"加载中"的图标 | `string` | - |
|
| loadingSlot | 目标组件的插槽名称,用来显示一个"加载中"的图标 | `string` | - | - |
|
||||||
|
| autoSelect | 自动设置选项 | `'first' \| 'last' \| 'one'\| ((item: OptionsItem[]) => OptionsItem) \| false` | `false` | >5.5.4 |
|
||||||
|
|
||||||
|
#### autoSelect 自动设置选项
|
||||||
|
|
||||||
|
如果当前值为undefined,在选项数据成功加载之后,自动从备选项中选择一个作为当前值。默认值为`false`,即不自动选择选项。注意:该属性不应用于多选组件。可选值有:
|
||||||
|
|
||||||
|
- `"first"`:自动选择第一个选项
|
||||||
|
- `"last"`:自动选择最后一个选项
|
||||||
|
- `"one"`:有且仅有一个选项时,自动选择它
|
||||||
|
- `自定义函数`:自定义选择逻辑,函数的参数为options,返回值为选择的选项
|
||||||
|
- `false`:不自动选择选项
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
|
||||||
@@ -158,3 +169,5 @@ function fetchApi(): Promise<Record<string, any>> {
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| getComponentRef | 获取被包装的组件的实例 | ()=>T | >5.5.4 |
|
| getComponentRef | 获取被包装的组件的实例 | ()=>T | >5.5.4 |
|
||||||
| updateParam | 设置接口请求参数(将与params属性合并) | (newParams: Record<string, any>)=>void | >5.5.4 |
|
| updateParam | 设置接口请求参数(将与params属性合并) | (newParams: Record<string, any>)=>void | >5.5.4 |
|
||||||
|
| getOptions | 获取已加载的选项数据 | ()=>OptionsItem[] | >5.5.4 |
|
||||||
|
| getValue | 获取当前值 | ()=>any | >5.5.4 |
|
||||||
|
|||||||
@@ -127,13 +127,14 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||||||
|
|
||||||
除了上面的属性类型包含`slot`,还可以通过插槽来自定义弹窗的内容。
|
除了上面的属性类型包含`slot`,还可以通过插槽来自定义弹窗的内容。
|
||||||
|
|
||||||
| 插槽名 | 描述 |
|
| 插槽名 | 描述 |
|
||||||
| -------------- | ------------------- |
|
| -------------- | -------------------------------------------------- |
|
||||||
| default | 默认插槽 - 弹窗内容 |
|
| default | 默认插槽 - 弹窗内容 |
|
||||||
| prepend-footer | 取消按钮左侧 |
|
| prepend-footer | 取消按钮左侧 |
|
||||||
| append-footer | 取消按钮右侧 |
|
| center-footer | 取消按钮和确认按钮中间(不使用 footer 插槽时有效) |
|
||||||
| close-icon | 关闭按钮图标 |
|
| append-footer | 确认按钮右侧 |
|
||||||
| extra | 额外内容(标题右侧) |
|
| close-icon | 关闭按钮图标 |
|
||||||
|
| extra | 额外内容(标题右侧) |
|
||||||
|
|
||||||
### drawerApi
|
### drawerApi
|
||||||
|
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
|
|||||||
| actionWrapperClass | 表单操作区域class | `any` | - |
|
| actionWrapperClass | 表单操作区域class | `any` | - |
|
||||||
| handleReset | 表单重置回调 | `(values: Record<string, any>,) => Promise<void> \| void` | - |
|
| handleReset | 表单重置回调 | `(values: Record<string, any>,) => Promise<void> \| void` | - |
|
||||||
| handleSubmit | 表单提交回调 | `(values: Record<string, any>,) => Promise<void> \| void` | - |
|
| handleSubmit | 表单提交回调 | `(values: Record<string, any>,) => Promise<void> \| void` | - |
|
||||||
| handleValuesChange | 表单值变化回调 | `(values: Record<string, any>,) => void` | - |
|
| handleValuesChange | 表单值变化回调 | `(values: Record<string, any>, fieldsChanged: string[]) => void` | - |
|
||||||
| actionButtonsReverse | 调换操作按钮位置 | `boolean` | `false` |
|
| actionButtonsReverse | 调换操作按钮位置 | `boolean` | `false` |
|
||||||
| resetButtonOptions | 重置按钮组件参数 | `ActionButtonOptions` | - |
|
| resetButtonOptions | 重置按钮组件参数 | `ActionButtonOptions` | - |
|
||||||
| submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - |
|
| submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - |
|
||||||
@@ -325,6 +325,12 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
|
|||||||
| submitOnChange | 字段值改变时提交表单(内部防抖,这个属性一般用于表格的搜索表单) | `boolean` | false |
|
| submitOnChange | 字段值改变时提交表单(内部防抖,这个属性一般用于表格的搜索表单) | `boolean` | false |
|
||||||
| compact | 是否紧凑模式(忽略为校验信息所预留的空间) | `boolean` | false |
|
| compact | 是否紧凑模式(忽略为校验信息所预留的空间) | `boolean` | false |
|
||||||
|
|
||||||
|
::: tip handleValuesChange
|
||||||
|
|
||||||
|
`handleValuesChange` 回调函数的第一个参数`values`装载了表单改变后的当前值对象,第二个参数`fieldsChanged`是一个数组,包含了所有被改变的字段名。注意:第二个参数仅在v5.5.4(不含)以上版本可用,并且传递的是已在schema中定义的字段名。如果你使用了字段映射并且需要检查是哪些字段发生了变化的话,请注意该参数并不会包含映射后的字段名。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
::: tip fieldMappingTime
|
::: tip fieldMappingTime
|
||||||
|
|
||||||
此属性用于将表单内的数组值映射成 2 个字段,它应当传入一个数组,数组的每一项是一个映射规则,规则的第一个成员是一个字符串,表示需要映射的字段名,第二个成员是一个数组,表示映射后的字段名,第三个成员是一个可选的格式掩码,用于格式化日期时间字段;也可以提供一个格式化函数(参数分别为当前值和当前字段名,返回格式化后的值)。如果明确地将格式掩码设为null,则原值映射而不进行格式化(适用于非日期时间字段)。例如:`[['timeRange', ['startTime', 'endTime'], 'YYYY-MM-DD']]`,`timeRange`应当是一个至少具有2个成员的数组类型的值。Form会将`timeRange`的值前两个值分别按照格式掩码`YYYY-MM-DD`格式化后映射到`startTime`和`endTime`字段上。每一项的第三个参数是一个可选的格式掩码,
|
此属性用于将表单内的数组值映射成 2 个字段,它应当传入一个数组,数组的每一项是一个映射规则,规则的第一个成员是一个字符串,表示需要映射的字段名,第二个成员是一个数组,表示映射后的字段名,第三个成员是一个可选的格式掩码,用于格式化日期时间字段;也可以提供一个格式化函数(参数分别为当前值和当前字段名,返回格式化后的值)。如果明确地将格式掩码设为null,则原值映射而不进行格式化(适用于非日期时间字段)。例如:`[['timeRange', ['startTime', 'endTime'], 'YYYY-MM-DD']]`,`timeRange`应当是一个至少具有2个成员的数组类型的值。Form会将`timeRange`的值前两个值分别按照格式掩码`YYYY-MM-DD`格式化后映射到`startTime`和`endTime`字段上。每一项的第三个参数是一个可选的格式掩码,
|
||||||
|
|||||||
@@ -59,8 +59,7 @@ Modal 内的内容一般业务中,会比较复杂,所以我们可以将 moda
|
|||||||
::: info 注意
|
::: info 注意
|
||||||
|
|
||||||
- `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
|
- `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
|
||||||
- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onConfirm`,那么以内部的 `onConfirm` 为准。`onOpenChange`事件除外,内外都会触发。
|
- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onConfirm`,那么以内部的 `onConfirm` 为准。`onOpenChange`事件除外,内外都会触发。另外,如果设置了`destroyOnClose`,内部Modal及其子组件会在被关闭后<b>完全销毁</b>。
|
||||||
- 使用了`connectedComponent`参数时,可以配置`destroyOnClose`属性来决定当关闭弹窗时,是否要销毁`connectedComponent`组件(重新创建`connectedComponent`组件,这将会把其内部所有的变量、状态、数据等恢复到初始状态。)。
|
|
||||||
- 如果弹窗的默认行为不符合你的预期,可以在`src\bootstrap.ts`中修改`setDefaultModalProps`的参数来设置默认的属性,如默认隐藏全屏按钮,修改默认ZIndex等。
|
- 如果弹窗的默认行为不符合你的预期,可以在`src\bootstrap.ts`中修改`setDefaultModalProps`的参数来设置默认的属性,如默认隐藏全屏按钮,修改默认ZIndex等。
|
||||||
|
|
||||||
:::
|
:::
|
||||||
@@ -84,7 +83,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| appendToMain | 是否挂载到内容区域(默认挂载到body) | `boolean` | `false` |
|
| appendToMain | 是否挂载到内容区域(默认挂载到body) | `boolean` | `false` |
|
||||||
| connectedComponent | 连接另一个Modal组件 | `Component` | - |
|
| connectedComponent | 连接另一个Modal组件 | `Component` | - |
|
||||||
| destroyOnClose | 关闭时销毁`connectedComponent` | `boolean` | `false` |
|
| destroyOnClose | 关闭时销毁 | `boolean` | `false` |
|
||||||
| title | 标题 | `string\|slot` | - |
|
| title | 标题 | `string\|slot` | - |
|
||||||
| titleTooltip | 标题提示信息 | `string\|slot` | - |
|
| titleTooltip | 标题提示信息 | `string\|slot` | - |
|
||||||
| description | 描述信息 | `string\|slot` | - |
|
| description | 描述信息 | `string\|slot` | - |
|
||||||
@@ -138,11 +137,12 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
|
|
||||||
除了上面的属性类型包含`slot`,还可以通过插槽来自定义弹窗的内容。
|
除了上面的属性类型包含`slot`,还可以通过插槽来自定义弹窗的内容。
|
||||||
|
|
||||||
| 插槽名 | 描述 |
|
| 插槽名 | 描述 |
|
||||||
| -------------- | ------------------- |
|
| -------------- | -------------------------------------------------- |
|
||||||
| default | 默认插槽 - 弹窗内容 |
|
| default | 默认插槽 - 弹窗内容 |
|
||||||
| prepend-footer | 取消按钮左侧 |
|
| prepend-footer | 取消按钮左侧 |
|
||||||
| append-footer | 取消按钮右侧 |
|
| center-footer | 取消按钮和确认按钮中间(不使用 footer 插槽时有效) |
|
||||||
|
| append-footer | 确认按钮右侧 |
|
||||||
|
|
||||||
### modalApi
|
### modalApi
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { h, ref } from 'vue';
|
||||||
|
|
||||||
import { alert, confirm, VbenButton } from '@vben/common-ui';
|
import { alert, confirm, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { Checkbox, message } from 'ant-design-vue';
|
||||||
|
|
||||||
function showConfirm() {
|
function showConfirm() {
|
||||||
confirm('This is an alert message')
|
confirm('This is an alert message')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -18,6 +22,34 @@ function showIconConfirm() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showfooterConfirm() {
|
||||||
|
const checked = ref(false);
|
||||||
|
confirm({
|
||||||
|
cancelText: '不要虾扯蛋',
|
||||||
|
confirmText: '是的,我们都是NPC',
|
||||||
|
content:
|
||||||
|
'刚才发生的事情,为什么我似乎早就经历过一般?\n我甚至能在事情发生过程中潜意识里预知到接下来会发生什么。\n\n听起来挺玄乎的,你有过这种感觉吗?',
|
||||||
|
footer: () =>
|
||||||
|
h(
|
||||||
|
Checkbox,
|
||||||
|
{
|
||||||
|
checked: checked.value,
|
||||||
|
class: 'flex-1',
|
||||||
|
'onUpdate:checked': (v) => (checked.value = v),
|
||||||
|
},
|
||||||
|
'不再提示',
|
||||||
|
),
|
||||||
|
icon: 'question',
|
||||||
|
title: '未解之谜',
|
||||||
|
}).then(() => {
|
||||||
|
if (checked.value) {
|
||||||
|
message.success('我不会再拿这个问题烦你了');
|
||||||
|
} else {
|
||||||
|
message.info('下次还要继续问你哟');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function showAsyncConfirm() {
|
function showAsyncConfirm() {
|
||||||
confirm({
|
confirm({
|
||||||
beforeClose({ isConfirm }) {
|
beforeClose({ isConfirm }) {
|
||||||
@@ -37,6 +69,7 @@ function showAsyncConfirm() {
|
|||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<VbenButton @click="showConfirm">Confirm</VbenButton>
|
<VbenButton @click="showConfirm">Confirm</VbenButton>
|
||||||
<VbenButton @click="showIconConfirm">Confirm With Icon</VbenButton>
|
<VbenButton @click="showIconConfirm">Confirm With Icon</VbenButton>
|
||||||
|
<VbenButton @click="showfooterConfirm">Confirm With Footer</VbenButton>
|
||||||
<VbenButton @click="showAsyncConfirm">Async Confirm</VbenButton>
|
<VbenButton @click="showAsyncConfirm">Async Confirm</VbenButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
|
|
||||||
import { alert, prompt, VbenButton } from '@vben/common-ui';
|
import { alert, prompt, useAlertContext, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
import { Input, RadioGroup } from 'ant-design-vue';
|
import { Input, RadioGroup, Select } from 'ant-design-vue';
|
||||||
import { BadgeJapaneseYen } from 'lucide-vue-next';
|
import { BadgeJapaneseYen } from 'lucide-vue-next';
|
||||||
|
|
||||||
function showPrompt() {
|
function showPrompt() {
|
||||||
@@ -18,18 +18,32 @@ function showPrompt() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSelectPrompt() {
|
function showSlotsPrompt() {
|
||||||
prompt({
|
prompt({
|
||||||
component: Input,
|
component: () => {
|
||||||
componentProps: {
|
// 获取弹窗上下文。注意:只能在setup或者函数式组件中调用
|
||||||
placeholder: '请输入',
|
const { doConfirm } = useAlertContext();
|
||||||
prefix: '充值金额',
|
return h(
|
||||||
type: 'number',
|
Input,
|
||||||
|
{
|
||||||
|
onKeydown(e: KeyboardEvent) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
// 调用弹窗提供的确认方法
|
||||||
|
doConfirm();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
placeholder: '请输入',
|
||||||
|
prefix: '充值金额:',
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addonAfter: () => h(BadgeJapaneseYen),
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
componentSlots: {
|
content:
|
||||||
addonAfter: () => h(BadgeJapaneseYen),
|
'此弹窗演示了如何使用自定义插槽,并且可以使用useAlertContext获取到弹窗的上下文。\n在输入框中按下回车键会触发确认操作。',
|
||||||
},
|
|
||||||
content: '此弹窗演示了如何使用componentSlots传递自定义插槽',
|
|
||||||
icon: 'question',
|
icon: 'question',
|
||||||
modelPropName: 'value',
|
modelPropName: 'value',
|
||||||
}).then((val) => {
|
}).then((val) => {
|
||||||
@@ -37,6 +51,29 @@ function showSelectPrompt() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showSelectPrompt() {
|
||||||
|
prompt({
|
||||||
|
component: Select,
|
||||||
|
componentProps: {
|
||||||
|
options: [
|
||||||
|
{ label: 'Option A', value: 'Option A' },
|
||||||
|
{ label: 'Option B', value: 'Option B' },
|
||||||
|
{ label: 'Option C', value: 'Option C' },
|
||||||
|
],
|
||||||
|
placeholder: '请选择',
|
||||||
|
// 弹窗会设置body的pointer-events为none,这回影响下拉框的点击事件
|
||||||
|
popupClassName: 'pointer-events-auto',
|
||||||
|
},
|
||||||
|
content: '此弹窗演示了如何使用component传递自定义组件',
|
||||||
|
icon: 'question',
|
||||||
|
modelPropName: 'value',
|
||||||
|
}).then((val) => {
|
||||||
|
if (val) {
|
||||||
|
alert(`你选择了${val}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function sleep(ms: number) {
|
function sleep(ms: number) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
@@ -44,7 +81,6 @@ function sleep(ms: number) {
|
|||||||
function showAsyncPrompt() {
|
function showAsyncPrompt() {
|
||||||
prompt({
|
prompt({
|
||||||
async beforeClose(scope) {
|
async beforeClose(scope) {
|
||||||
console.log(scope);
|
|
||||||
if (scope.isConfirm) {
|
if (scope.isConfirm) {
|
||||||
if (scope.value) {
|
if (scope.value) {
|
||||||
// 模拟异步操作,如果不成功,可以返回false
|
// 模拟异步操作,如果不成功,可以返回false
|
||||||
@@ -75,6 +111,7 @@ function showAsyncPrompt() {
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<VbenButton @click="showPrompt">Prompt</VbenButton>
|
<VbenButton @click="showPrompt">Prompt</VbenButton>
|
||||||
|
<VbenButton @click="showSlotsPrompt"> Prompt With slots </VbenButton>
|
||||||
<VbenButton @click="showSelectPrompt">Prompt With Select</VbenButton>
|
<VbenButton @click="showSelectPrompt">Prompt With Select</VbenButton>
|
||||||
<VbenButton @click="showAsyncPrompt">Prompt With Async</VbenButton>
|
<VbenButton @click="showAsyncPrompt">Prompt With Async</VbenButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ Open a terminal in your code directory and execute the following commands:
|
|||||||
cd vue-vben-admin
|
cd vue-vben-admin
|
||||||
|
|
||||||
# Enable the project-specified version of pnpm
|
# Enable the project-specified version of pnpm
|
||||||
corepack enable
|
npm i -g corepack
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
pnpm install
|
pnpm install
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
|||||||
```ts
|
```ts
|
||||||
const dashboardMenus = [
|
const dashboardMenus = [
|
||||||
{
|
{
|
||||||
// 这里固定写死 BasicLayout,不可更改
|
|
||||||
component: 'BasicLayout',
|
|
||||||
meta: {
|
meta: {
|
||||||
order: -1,
|
order: -1,
|
||||||
title: 'page.dashboard.title',
|
title: 'page.dashboard.title',
|
||||||
@@ -144,6 +142,16 @@ const dashboardMenus = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Test',
|
||||||
|
path: '/test',
|
||||||
|
component: '/test/index',
|
||||||
|
meta: {
|
||||||
|
title: 'page.test',
|
||||||
|
// 部分特殊页面如果不需要基础布局(页面顶部和侧边栏),可将noBasicLayout设置为true
|
||||||
|
noBasicLayout: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ git clone https://gitee.com/annsion/vue-vben-admin.git
|
|||||||
cd vue-vben-admin
|
cd vue-vben-admin
|
||||||
|
|
||||||
# 使用项目指定的pnpm版本进行依赖安装
|
# 使用项目指定的pnpm版本进行依赖安装
|
||||||
corepack enable
|
npm i -g corepack
|
||||||
|
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
pnpm install
|
pnpm install
|
||||||
|
|||||||
@@ -11,3 +11,7 @@
|
|||||||
当前只有对应的包下面存在 `tailwind.config.mjs` 文件才会启用 tailwindcss 的编译,否则不会启用 tailwindcss。如果你是纯粹的 SDK 包,不需要使用 tailwindcss,可以不用创建 `tailwind.config.mjs` 文件。
|
当前只有对应的包下面存在 `tailwind.config.mjs` 文件才会启用 tailwindcss 的编译,否则不会启用 tailwindcss。如果你是纯粹的 SDK 包,不需要使用 tailwindcss,可以不用创建 `tailwind.config.mjs` 文件。
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## 提示
|
||||||
|
|
||||||
|
现`tailwindcss`已至v4.x版本,使用方法与`tailwindcss: ^3.4.17`有差异,v4.0无法与v3.x版本兼容,在开发前请确认`package.json`中的`tailwindcss`版本。
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ hero:
|
|||||||
- theme: alt
|
- theme: alt
|
||||||
text: 在 GitHub 查看
|
text: 在 GitHub 查看
|
||||||
link: https://github.com/vbenjs/vue-vben-admin
|
link: https://github.com/vbenjs/vue-vben-admin
|
||||||
|
- theme: alt
|
||||||
|
text: DeepWiki 文档
|
||||||
|
link: https://deepwiki.com/vbenjs/vue-vben-admin
|
||||||
|
|
||||||
features:
|
features:
|
||||||
- icon: 🚀
|
- icon: 🚀
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/commitlint-config",
|
"name": "@vben/commitlint-config",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@@ -10,7 +10,15 @@ export async function vue(): Promise<Linter.Config[]> {
|
|||||||
interopDefault(import('@typescript-eslint/parser')),
|
interopDefault(import('@typescript-eslint/parser')),
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
|
const flatEssential = pluginVue.configs?.['flat/essential'] || [];
|
||||||
|
const flatStronglyRecommended =
|
||||||
|
pluginVue.configs?.['flat/strongly-recommended'] || [];
|
||||||
|
const flatRecommended = pluginVue.configs?.['flat/recommended'] || [];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
...flatEssential,
|
||||||
|
...flatStronglyRecommended,
|
||||||
|
...flatRecommended,
|
||||||
{
|
{
|
||||||
files: ['**/*.vue'],
|
files: ['**/*.vue'],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
@@ -43,12 +51,9 @@ export async function vue(): Promise<Linter.Config[]> {
|
|||||||
plugins: {
|
plugins: {
|
||||||
vue: pluginVue,
|
vue: pluginVue,
|
||||||
},
|
},
|
||||||
processor: pluginVue.processors['.vue'],
|
processor: pluginVue.processors?.['.vue'],
|
||||||
rules: {
|
rules: {
|
||||||
...pluginVue.configs.base.rules,
|
...pluginVue.configs?.base?.rules,
|
||||||
...pluginVue.configs['vue3-essential'].rules,
|
|
||||||
...pluginVue.configs['vue3-strongly-recommended'].rules,
|
|
||||||
...pluginVue.configs['vue3-recommended'].rules,
|
|
||||||
|
|
||||||
'vue/attribute-hyphenation': [
|
'vue/attribute-hyphenation': [
|
||||||
'error',
|
'error',
|
||||||
@@ -131,7 +136,6 @@ export async function vue(): Promise<Linter.Config[]> {
|
|||||||
'vue/require-default-prop': 'error',
|
'vue/require-default-prop': 'error',
|
||||||
'vue/require-explicit-emits': 'error',
|
'vue/require-explicit-emits': 'error',
|
||||||
'vue/require-prop-types': 'off',
|
'vue/require-prop-types': 'off',
|
||||||
'vue/script-setup-uses-vars': 'error',
|
|
||||||
'vue/singleline-html-element-content-newline': 'off',
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
'vue/space-infix-ops': 'error',
|
'vue/space-infix-ops': 'error',
|
||||||
'vue/space-unary-ops': ['error', { nonwords: false, words: true }],
|
'vue/space-unary-ops': ['error', { nonwords: false, words: true }],
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export default {
|
|||||||
'stylelint-scss',
|
'stylelint-scss',
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
|
'at-rule-no-deprecated': null,
|
||||||
'at-rule-no-unknown': [
|
'at-rule-no-unknown': [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/stylelint-config",
|
"name": "@vben/stylelint-config",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/node-utils",
|
"name": "@vben/node-utils",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/tailwind-config",
|
"name": "@vben/tailwind-config",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/tsconfig",
|
"name": "@vben/tsconfig",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/vite-config",
|
"name": "@vben/vite-config",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@@ -3,149 +3,328 @@ import type { ConfigEnv, PluginOption, UserConfig } from 'vite';
|
|||||||
import type { PluginOptions } from 'vite-plugin-dts';
|
import type { PluginOptions } from 'vite-plugin-dts';
|
||||||
import type { Options as PwaPluginOptions } from 'vite-plugin-pwa';
|
import type { Options as PwaPluginOptions } from 'vite-plugin-pwa';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ImportMap 配置接口
|
||||||
|
* @description 用于配置模块导入映射,支持自定义导入路径和范围
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* {
|
||||||
|
* imports: {
|
||||||
|
* 'vue': 'https://unpkg.com/vue@3.2.47/dist/vue.esm-browser.js'
|
||||||
|
* },
|
||||||
|
* scopes: {
|
||||||
|
* 'https://site.com/': {
|
||||||
|
* 'vue': 'https://unpkg.com/vue@3.2.47/dist/vue.esm-browser.js'
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
interface IImportMap {
|
interface IImportMap {
|
||||||
|
/** 模块导入映射 */
|
||||||
imports?: Record<string, string>;
|
imports?: Record<string, string>;
|
||||||
|
/** 作用域特定的导入映射 */
|
||||||
scopes?: {
|
scopes?: {
|
||||||
[scope: string]: Record<string, string>;
|
[scope: string]: Record<string, string>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印插件配置选项
|
||||||
|
* @description 用于配置控制台打印信息
|
||||||
|
*/
|
||||||
interface PrintPluginOptions {
|
interface PrintPluginOptions {
|
||||||
/**
|
/**
|
||||||
* 打印的数据
|
* 打印的数据映射
|
||||||
|
* @description 键值对形式的数据,将在控制台打印
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* {
|
||||||
|
* 'App Version': '1.0.0',
|
||||||
|
* 'Build Time': '2024-01-01'
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
infoMap?: Record<string, string | undefined>;
|
infoMap?: Record<string, string | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nitro Mock 插件配置选项
|
||||||
|
* @description 用于配置 Nitro Mock 服务器的行为
|
||||||
|
*/
|
||||||
interface NitroMockPluginOptions {
|
interface NitroMockPluginOptions {
|
||||||
/**
|
/**
|
||||||
* mock server 包名
|
* Mock 服务器包名
|
||||||
|
* @default '@vbenjs/nitro-mock'
|
||||||
*/
|
*/
|
||||||
mockServerPackage?: string;
|
mockServerPackage?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mock 服务端口
|
* Mock 服务端口
|
||||||
|
* @default 3000
|
||||||
*/
|
*/
|
||||||
port?: number;
|
port?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mock 日志是否打印
|
* 是否打印 Mock 日志
|
||||||
|
* @default false
|
||||||
*/
|
*/
|
||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 归档插件配置选项
|
||||||
|
* @description 用于配置构建产物的压缩归档
|
||||||
|
*/
|
||||||
interface ArchiverPluginOptions {
|
interface ArchiverPluginOptions {
|
||||||
/**
|
/**
|
||||||
* 输出文件名
|
* 输出文件名
|
||||||
* @default dist
|
* @default 'dist'
|
||||||
*/
|
*/
|
||||||
name?: string;
|
name?: string;
|
||||||
/**
|
/**
|
||||||
* 输出目录
|
* 输出目录
|
||||||
* @default .
|
* @default '.'
|
||||||
*/
|
*/
|
||||||
outputDir?: string;
|
outputDir?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* importmap 插件配置
|
* ImportMap 插件配置
|
||||||
|
* @description 用于配置模块的 CDN 导入
|
||||||
*/
|
*/
|
||||||
interface ImportmapPluginOptions {
|
interface ImportmapPluginOptions {
|
||||||
/**
|
/**
|
||||||
* CDN 供应商
|
* CDN 供应商
|
||||||
* @default jspm.io
|
* @default 'jspm.io'
|
||||||
|
* @description 支持 esm.sh 和 jspm.io 两种 CDN 供应商
|
||||||
*/
|
*/
|
||||||
defaultProvider?: 'esm.sh' | 'jspm.io';
|
defaultProvider?: 'esm.sh' | 'jspm.io';
|
||||||
/** importmap 配置 */
|
/**
|
||||||
|
* ImportMap 配置数组
|
||||||
|
* @description 配置需要从 CDN 导入的包
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* [
|
||||||
|
* { name: 'vue' },
|
||||||
|
* { name: 'pinia', range: '^2.0.0' }
|
||||||
|
* ]
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
importmap?: Array<{ name: string; range?: string }>;
|
importmap?: Array<{ name: string; range?: string }>;
|
||||||
/** 手动配置importmap */
|
/**
|
||||||
|
* 手动配置 ImportMap
|
||||||
|
* @description 自定义 ImportMap 配置
|
||||||
|
*/
|
||||||
inputMap?: IImportMap;
|
inputMap?: IImportMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于判断是否需要加载插件
|
* 条件插件配置
|
||||||
|
* @description 用于根据条件动态加载插件
|
||||||
*/
|
*/
|
||||||
interface ConditionPlugin {
|
interface ConditionPlugin {
|
||||||
// 判断条件
|
/**
|
||||||
|
* 判断条件
|
||||||
|
* @description 当条件为 true 时加载插件
|
||||||
|
*/
|
||||||
condition?: boolean;
|
condition?: boolean;
|
||||||
// 插件对象
|
/**
|
||||||
|
* 插件对象
|
||||||
|
* @description 返回插件数组或 Promise
|
||||||
|
*/
|
||||||
plugins: () => PluginOption[] | PromiseLike<PluginOption[]>;
|
plugins: () => PluginOption[] | PromiseLike<PluginOption[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用插件配置选项
|
||||||
|
* @description 所有插件共用的基础配置
|
||||||
|
*/
|
||||||
interface CommonPluginOptions {
|
interface CommonPluginOptions {
|
||||||
/** 是否开启devtools */
|
/**
|
||||||
|
* 是否开启开发工具
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
devtools?: boolean;
|
devtools?: boolean;
|
||||||
/** 环境变量 */
|
/**
|
||||||
|
* 环境变量
|
||||||
|
* @description 自定义环境变量
|
||||||
|
*/
|
||||||
env?: Record<string, any>;
|
env?: Record<string, any>;
|
||||||
/** 是否注入metadata */
|
/**
|
||||||
|
* 是否注入元数据
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
injectMetadata?: boolean;
|
injectMetadata?: boolean;
|
||||||
/** 是否构建模式 */
|
/**
|
||||||
|
* 是否为构建模式
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
isBuild?: boolean;
|
isBuild?: boolean;
|
||||||
/** 构建模式 */
|
/**
|
||||||
|
* 构建模式
|
||||||
|
* @default 'development'
|
||||||
|
*/
|
||||||
mode?: string;
|
mode?: string;
|
||||||
/** 开启依赖分析 */
|
/**
|
||||||
|
* 是否开启依赖分析
|
||||||
|
* @default false
|
||||||
|
* @description 使用 rollup-plugin-visualizer 分析依赖
|
||||||
|
*/
|
||||||
visualizer?: boolean | PluginVisualizerOptions;
|
visualizer?: boolean | PluginVisualizerOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用插件配置选项
|
||||||
|
* @description 用于配置应用构建时的插件选项
|
||||||
|
*/
|
||||||
interface ApplicationPluginOptions extends CommonPluginOptions {
|
interface ApplicationPluginOptions extends CommonPluginOptions {
|
||||||
/** 开启后,会在打包dist同级生成dist.zip */
|
/**
|
||||||
|
* 是否开启压缩归档
|
||||||
|
* @default false
|
||||||
|
* @description 开启后会在打包目录生成 zip 文件
|
||||||
|
*/
|
||||||
archiver?: boolean;
|
archiver?: boolean;
|
||||||
/** 压缩归档插件配置 */
|
/**
|
||||||
|
* 压缩归档插件配置
|
||||||
|
* @description 配置压缩归档的行为
|
||||||
|
*/
|
||||||
archiverPluginOptions?: ArchiverPluginOptions;
|
archiverPluginOptions?: ArchiverPluginOptions;
|
||||||
/** 开启 gzip|brotli 压缩 */
|
/**
|
||||||
|
* 是否开启压缩
|
||||||
|
* @default false
|
||||||
|
* @description 支持 gzip 和 brotli 压缩
|
||||||
|
*/
|
||||||
compress?: boolean;
|
compress?: boolean;
|
||||||
/** 压缩类型 */
|
/**
|
||||||
|
* 压缩类型
|
||||||
|
* @default ['gzip']
|
||||||
|
* @description 可选的压缩类型
|
||||||
|
*/
|
||||||
compressTypes?: ('brotli' | 'gzip')[];
|
compressTypes?: ('brotli' | 'gzip')[];
|
||||||
/** 在构建的时候抽离配置文件 */
|
/**
|
||||||
|
* 是否抽离配置文件
|
||||||
|
* @default false
|
||||||
|
* @description 在构建时抽离配置文件
|
||||||
|
*/
|
||||||
extraAppConfig?: boolean;
|
extraAppConfig?: boolean;
|
||||||
/** 是否开启html插件 */
|
/**
|
||||||
|
* 是否开启 HTML 插件
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
html?: boolean;
|
html?: boolean;
|
||||||
/** 是否开启i18n */
|
/**
|
||||||
|
* 是否开启国际化
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
i18n?: boolean;
|
i18n?: boolean;
|
||||||
/** 是否开启 importmap CDN */
|
/**
|
||||||
|
* 是否开启 ImportMap CDN
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
importmap?: boolean;
|
importmap?: boolean;
|
||||||
/** importmap 插件配置 */
|
/**
|
||||||
|
* ImportMap 插件配置
|
||||||
|
*/
|
||||||
importmapOptions?: ImportmapPluginOptions;
|
importmapOptions?: ImportmapPluginOptions;
|
||||||
/** 是否注入app loading */
|
/**
|
||||||
|
* 是否注入应用加载动画
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
injectAppLoading?: boolean;
|
injectAppLoading?: boolean;
|
||||||
/** 是否注入全局scss */
|
/**
|
||||||
|
* 是否注入全局 SCSS
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
injectGlobalScss?: boolean;
|
injectGlobalScss?: boolean;
|
||||||
/** 是否注入版权信息 */
|
/**
|
||||||
|
* 是否注入版权信息
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
license?: boolean;
|
license?: boolean;
|
||||||
/** 是否开启nitro mock */
|
/**
|
||||||
|
* 是否开启 Nitro Mock
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
nitroMock?: boolean;
|
nitroMock?: boolean;
|
||||||
/** nitro mock 插件配置 */
|
/**
|
||||||
|
* Nitro Mock 插件配置
|
||||||
|
*/
|
||||||
nitroMockOptions?: NitroMockPluginOptions;
|
nitroMockOptions?: NitroMockPluginOptions;
|
||||||
/** 开启控制台自定义打印 */
|
/**
|
||||||
|
* 是否开启控制台打印
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
print?: boolean;
|
print?: boolean;
|
||||||
/** 打印插件配置 */
|
/**
|
||||||
|
* 打印插件配置
|
||||||
|
*/
|
||||||
printInfoMap?: PrintPluginOptions['infoMap'];
|
printInfoMap?: PrintPluginOptions['infoMap'];
|
||||||
/** 是否开启pwa */
|
/**
|
||||||
|
* 是否开启 PWA
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
pwa?: boolean;
|
pwa?: boolean;
|
||||||
/** pwa 插件配置 */
|
/**
|
||||||
|
* PWA 插件配置
|
||||||
|
*/
|
||||||
pwaOptions?: Partial<PwaPluginOptions>;
|
pwaOptions?: Partial<PwaPluginOptions>;
|
||||||
/** 是否开启vxe-table懒加载 */
|
/**
|
||||||
|
* 是否开启 VXE Table 懒加载
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
vxeTableLazyImport?: boolean;
|
vxeTableLazyImport?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 库插件配置选项
|
||||||
|
* @description 用于配置库构建时的插件选项
|
||||||
|
*/
|
||||||
interface LibraryPluginOptions extends CommonPluginOptions {
|
interface LibraryPluginOptions extends CommonPluginOptions {
|
||||||
/** 开启 dts 输出 */
|
/**
|
||||||
|
* 是否开启 DTS 输出
|
||||||
|
* @default true
|
||||||
|
* @description 生成 TypeScript 类型声明文件
|
||||||
|
*/
|
||||||
dts?: boolean | PluginOptions;
|
dts?: boolean | PluginOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用配置选项类型
|
||||||
|
*/
|
||||||
type ApplicationOptions = ApplicationPluginOptions;
|
type ApplicationOptions = ApplicationPluginOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 库配置选项类型
|
||||||
|
*/
|
||||||
type LibraryOptions = LibraryPluginOptions;
|
type LibraryOptions = LibraryPluginOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用配置定义函数类型
|
||||||
|
* @description 用于定义应用构建配置
|
||||||
|
*/
|
||||||
type DefineApplicationOptions = (config?: ConfigEnv) => Promise<{
|
type DefineApplicationOptions = (config?: ConfigEnv) => Promise<{
|
||||||
|
/** 应用插件配置 */
|
||||||
application?: ApplicationOptions;
|
application?: ApplicationOptions;
|
||||||
|
/** Vite 配置 */
|
||||||
vite?: UserConfig;
|
vite?: UserConfig;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 库配置定义函数类型
|
||||||
|
* @description 用于定义库构建配置
|
||||||
|
*/
|
||||||
type DefineLibraryOptions = (config?: ConfigEnv) => Promise<{
|
type DefineLibraryOptions = (config?: ConfigEnv) => Promise<{
|
||||||
|
/** 库插件配置 */
|
||||||
library?: LibraryOptions;
|
library?: LibraryOptions;
|
||||||
|
/** Vite 配置 */
|
||||||
vite?: UserConfig;
|
vite?: UserConfig;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置定义类型
|
||||||
|
* @description 应用或库的配置定义
|
||||||
|
*/
|
||||||
type DefineConfig = DefineApplicationOptions | DefineLibraryOptions;
|
type DefineConfig = DefineApplicationOptions | DefineLibraryOptions;
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vben-admin-monorepo",
|
"name": "vben-admin-monorepo",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"monorepo",
|
"monorepo",
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"node": ">=20.10.0",
|
"node": ">=20.10.0",
|
||||||
"pnpm": ">=9.12.0"
|
"pnpm": ">=9.12.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.15.9",
|
"packageManager": "pnpm@10.10.0",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"peerDependencyRules": {
|
"peerDependencyRules": {
|
||||||
"allowedVersions": {
|
"allowedVersions": {
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
"@ast-grep/napi": "catalog:",
|
"@ast-grep/napi": "catalog:",
|
||||||
"@ctrl/tinycolor": "catalog:",
|
"@ctrl/tinycolor": "catalog:",
|
||||||
"clsx": "catalog:",
|
"clsx": "catalog:",
|
||||||
"esbuild": "0.24.0",
|
"esbuild": "0.25.3",
|
||||||
"pinia": "catalog:",
|
"pinia": "catalog:",
|
||||||
"vue": "catalog:"
|
"vue": "catalog:"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/design",
|
"name": "@vben-core/design",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/icons",
|
"name": "@vben-core/icons",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/shared",
|
"name": "@vben-core/shared",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// eslint-disable-next-line vue/prefer-import-from-vue
|
||||||
import { isFunction, isObject, isString } from '@vue/shared';
|
import { isFunction, isObject, isString } from '@vue/shared';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/typings",
|
"name": "@vben-core/typings",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/composables",
|
"name": "@vben-core/composables",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/preferences",
|
"name": "@vben-core/preferences",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -2,14 +2,16 @@ import type { DeepPartial } from '@vben-core/typings';
|
|||||||
|
|
||||||
import type { InitialOptions, Preferences } from './types';
|
import type { InitialOptions, Preferences } from './types';
|
||||||
|
|
||||||
|
import { markRaw, reactive, readonly, watch } from 'vue';
|
||||||
|
|
||||||
import { StorageManager } from '@vben-core/shared/cache';
|
import { StorageManager } from '@vben-core/shared/cache';
|
||||||
import { isMacOs, merge } from '@vben-core/shared/utils';
|
import { isMacOs, merge } from '@vben-core/shared/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
breakpointsTailwind,
|
breakpointsTailwind,
|
||||||
useBreakpoints,
|
useBreakpoints,
|
||||||
useDebounceFn,
|
useDebounceFn,
|
||||||
} from '@vueuse/core';
|
} from '@vueuse/core';
|
||||||
import { markRaw, reactive, readonly, watch } from 'vue';
|
|
||||||
|
|
||||||
import { defaultPreferences } from './config';
|
import { defaultPreferences } from './config';
|
||||||
import { updateCSSVariables } from './update-css-variables';
|
import { updateCSSVariables } from './update-css-variables';
|
||||||
@@ -37,106 +39,6 @@ class PreferenceManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存偏好设置
|
|
||||||
* @param {Preferences} preference - 需要保存的偏好设置
|
|
||||||
*/
|
|
||||||
private _savePreferences(preference: Preferences) {
|
|
||||||
this.cache?.setItem(STORAGE_KEY, preference);
|
|
||||||
this.cache?.setItem(STORAGE_KEY_LOCALE, preference.app.locale);
|
|
||||||
this.cache?.setItem(STORAGE_KEY_THEME, preference.theme.mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理更新的键值
|
|
||||||
* 根据更新的键值执行相应的操作。
|
|
||||||
* @param {DeepPartial<Preferences>} updates - 部分更新的偏好设置
|
|
||||||
*/
|
|
||||||
private handleUpdates(updates: DeepPartial<Preferences>) {
|
|
||||||
const themeUpdates = updates.theme || {};
|
|
||||||
const appUpdates = updates.app || {};
|
|
||||||
if (themeUpdates && Object.keys(themeUpdates).length > 0) {
|
|
||||||
updateCSSVariables(this.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Reflect.has(appUpdates, 'colorGrayMode') ||
|
|
||||||
Reflect.has(appUpdates, 'colorWeakMode')
|
|
||||||
) {
|
|
||||||
this.updateColorMode(this.state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private initPlatform() {
|
|
||||||
const dom = document.documentElement;
|
|
||||||
dom.dataset.platform = isMacOs() ? 'macOs' : 'window';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从缓存中加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。
|
|
||||||
*/
|
|
||||||
private loadCachedPreferences() {
|
|
||||||
return this.cache?.getItem<Preferences>(STORAGE_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载偏好设置
|
|
||||||
* @returns {Preferences} 加载的偏好设置
|
|
||||||
*/
|
|
||||||
private loadPreferences(): Preferences {
|
|
||||||
return this.loadCachedPreferences() || { ...defaultPreferences };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 监听状态和系统偏好设置的变化。
|
|
||||||
*/
|
|
||||||
private setupWatcher() {
|
|
||||||
if (this.isInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听断点,判断是否移动端
|
|
||||||
const breakpoints = useBreakpoints(breakpointsTailwind);
|
|
||||||
const isMobile = breakpoints.smaller('md');
|
|
||||||
watch(
|
|
||||||
() => isMobile.value,
|
|
||||||
(val) => {
|
|
||||||
this.updatePreferences({
|
|
||||||
app: { isMobile: val },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{ immediate: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
// 监听系统主题偏好设置变化
|
|
||||||
window
|
|
||||||
.matchMedia('(prefers-color-scheme: dark)')
|
|
||||||
.addEventListener('change', ({ matches: isDark }) => {
|
|
||||||
this.updatePreferences({
|
|
||||||
theme: { mode: isDark ? 'dark' : 'light' },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新页面颜色模式(灰色、色弱)
|
|
||||||
* @param preference
|
|
||||||
*/
|
|
||||||
private updateColorMode(preference: Preferences) {
|
|
||||||
if (preference.app) {
|
|
||||||
const { colorGrayMode, colorWeakMode } = preference.app;
|
|
||||||
const dom = document.documentElement;
|
|
||||||
const COLOR_WEAK = 'invert-mode';
|
|
||||||
const COLOR_GRAY = 'grayscale-mode';
|
|
||||||
colorWeakMode
|
|
||||||
? dom.classList.add(COLOR_WEAK)
|
|
||||||
: dom.classList.remove(COLOR_WEAK);
|
|
||||||
colorGrayMode
|
|
||||||
? dom.classList.add(COLOR_GRAY)
|
|
||||||
: dom.classList.remove(COLOR_GRAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
[STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach((key) => {
|
[STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach((key) => {
|
||||||
this.cache?.removeItem(key);
|
this.cache?.removeItem(key);
|
||||||
@@ -220,6 +122,113 @@ class PreferenceManager {
|
|||||||
this.handleUpdates(updates);
|
this.handleUpdates(updates);
|
||||||
this.savePreferences(this.state);
|
this.savePreferences(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存偏好设置
|
||||||
|
* @param {Preferences} preference - 需要保存的偏好设置
|
||||||
|
*/
|
||||||
|
private _savePreferences(preference: Preferences) {
|
||||||
|
this.cache?.setItem(STORAGE_KEY, preference);
|
||||||
|
this.cache?.setItem(STORAGE_KEY_LOCALE, preference.app.locale);
|
||||||
|
this.cache?.setItem(STORAGE_KEY_THEME, preference.theme.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理更新的键值
|
||||||
|
* 根据更新的键值执行相应的操作。
|
||||||
|
* @param {DeepPartial<Preferences>} updates - 部分更新的偏好设置
|
||||||
|
*/
|
||||||
|
private handleUpdates(updates: DeepPartial<Preferences>) {
|
||||||
|
const themeUpdates = updates.theme || {};
|
||||||
|
const appUpdates = updates.app || {};
|
||||||
|
if (themeUpdates && Object.keys(themeUpdates).length > 0) {
|
||||||
|
updateCSSVariables(this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Reflect.has(appUpdates, 'colorGrayMode') ||
|
||||||
|
Reflect.has(appUpdates, 'colorWeakMode')
|
||||||
|
) {
|
||||||
|
this.updateColorMode(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private initPlatform() {
|
||||||
|
const dom = document.documentElement;
|
||||||
|
dom.dataset.platform = isMacOs() ? 'macOs' : 'window';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从缓存中加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。
|
||||||
|
*/
|
||||||
|
private loadCachedPreferences() {
|
||||||
|
return this.cache?.getItem<Preferences>(STORAGE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载偏好设置
|
||||||
|
* @returns {Preferences} 加载的偏好设置
|
||||||
|
*/
|
||||||
|
private loadPreferences(): Preferences {
|
||||||
|
return this.loadCachedPreferences() || { ...defaultPreferences };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听状态和系统偏好设置的变化。
|
||||||
|
*/
|
||||||
|
private setupWatcher() {
|
||||||
|
if (this.isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听断点,判断是否移动端
|
||||||
|
const breakpoints = useBreakpoints(breakpointsTailwind);
|
||||||
|
const isMobile = breakpoints.smaller('md');
|
||||||
|
watch(
|
||||||
|
() => isMobile.value,
|
||||||
|
(val) => {
|
||||||
|
this.updatePreferences({
|
||||||
|
app: { isMobile: val },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// 监听系统主题偏好设置变化
|
||||||
|
window
|
||||||
|
.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.addEventListener('change', ({ matches: isDark }) => {
|
||||||
|
// 如果偏好设置中主题模式为auto,则跟随系统更新
|
||||||
|
if (this.state.theme.mode === 'auto') {
|
||||||
|
this.updatePreferences({
|
||||||
|
theme: { mode: isDark ? 'dark' : 'light' },
|
||||||
|
});
|
||||||
|
// 恢复为auto模式
|
||||||
|
this.updatePreferences({
|
||||||
|
theme: { mode: 'auto' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新页面颜色模式(灰色、色弱)
|
||||||
|
* @param preference
|
||||||
|
*/
|
||||||
|
private updateColorMode(preference: Preferences) {
|
||||||
|
if (preference.app) {
|
||||||
|
const { colorGrayMode, colorWeakMode } = preference.app;
|
||||||
|
const dom = document.documentElement;
|
||||||
|
const COLOR_WEAK = 'invert-mode';
|
||||||
|
const COLOR_GRAY = 'grayscale-mode';
|
||||||
|
colorWeakMode
|
||||||
|
? dom.classList.add(COLOR_WEAK)
|
||||||
|
: dom.classList.remove(COLOR_WEAK);
|
||||||
|
colorGrayMode
|
||||||
|
? dom.classList.add(COLOR_GRAY)
|
||||||
|
: dom.classList.remove(COLOR_GRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const preferencesManager = new PreferenceManager();
|
const preferencesManager = new PreferenceManager();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/form-ui",
|
"name": "@vben-core/form-ui",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ async function handleReset(e: Event) {
|
|||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
const props = unref(rootProps);
|
const props = unref(rootProps);
|
||||||
|
|
||||||
const values = toRaw(props.formApi?.getValues());
|
const values = toRaw(await props.formApi?.getValues());
|
||||||
|
|
||||||
if (isFunction(props.handleReset)) {
|
if (isFunction(props.handleReset)) {
|
||||||
await props.handleReset?.(values);
|
await props.handleReset?.(values);
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ export class FormApi {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
const filteredFields = fieldMergeFn(fields, form.values);
|
const filteredFields = fieldMergeFn(fields, form.values);
|
||||||
|
this.handleStringToArrayFields(filteredFields);
|
||||||
form.setValues(filteredFields, shouldValidate);
|
form.setValues(filteredFields, shouldValidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,6 +305,7 @@ export class FormApi {
|
|||||||
const form = await this.getForm();
|
const form = await this.getForm();
|
||||||
await form.submitForm();
|
await form.submitForm();
|
||||||
const rawValues = toRaw(await this.getValues());
|
const rawValues = toRaw(await this.getValues());
|
||||||
|
this.handleArrayToStringFields(rawValues);
|
||||||
await this.state?.handleSubmit?.(rawValues);
|
await this.state?.handleSubmit?.(rawValues);
|
||||||
|
|
||||||
return rawValues;
|
return rawValues;
|
||||||
@@ -392,10 +394,53 @@ export class FormApi {
|
|||||||
return this.form;
|
return this.form;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleArrayToStringFields = (originValues: Record<string, any>) => {
|
||||||
|
const arrayToStringFields = this.state?.arrayToStringFields;
|
||||||
|
if (!arrayToStringFields || !Array.isArray(arrayToStringFields)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const processFields = (fields: string[], separator: string = ',') => {
|
||||||
|
this.processFields(fields, separator, originValues, (value, sep) =>
|
||||||
|
Array.isArray(value) ? value.join(sep) : value,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理简单数组格式 ['field1', 'field2', ';'] 或 ['field1', 'field2']
|
||||||
|
if (arrayToStringFields.every((item) => typeof item === 'string')) {
|
||||||
|
const lastItem =
|
||||||
|
arrayToStringFields[arrayToStringFields.length - 1] || '';
|
||||||
|
const fields =
|
||||||
|
lastItem.length === 1
|
||||||
|
? arrayToStringFields.slice(0, -1)
|
||||||
|
: arrayToStringFields;
|
||||||
|
const separator = lastItem.length === 1 ? lastItem : ',';
|
||||||
|
processFields(fields, separator);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理嵌套数组格式 [['field1'], ';']
|
||||||
|
arrayToStringFields.forEach((fieldConfig) => {
|
||||||
|
if (Array.isArray(fieldConfig)) {
|
||||||
|
const [fields, separator = ','] = fieldConfig;
|
||||||
|
// 根据类型定义,fields 应该始终是字符串数组
|
||||||
|
if (!Array.isArray(fields)) {
|
||||||
|
console.warn(
|
||||||
|
`Invalid field configuration: fields should be an array of strings, got ${typeof fields}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processFields(fields, separator);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
private handleRangeTimeValue = (originValues: Record<string, any>) => {
|
private handleRangeTimeValue = (originValues: Record<string, any>) => {
|
||||||
const values = { ...originValues };
|
const values = { ...originValues };
|
||||||
const fieldMappingTime = this.state?.fieldMappingTime;
|
const fieldMappingTime = this.state?.fieldMappingTime;
|
||||||
|
|
||||||
|
this.handleStringToArrayFields(values);
|
||||||
|
|
||||||
if (!fieldMappingTime || !Array.isArray(fieldMappingTime)) {
|
if (!fieldMappingTime || !Array.isArray(fieldMappingTime)) {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
@@ -441,6 +486,80 @@ export class FormApi {
|
|||||||
return values;
|
return values;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleStringToArrayFields = (originValues: Record<string, any>) => {
|
||||||
|
const arrayToStringFields = this.state?.arrayToStringFields;
|
||||||
|
if (!arrayToStringFields || !Array.isArray(arrayToStringFields)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const processFields = (fields: string[], separator: string = ',') => {
|
||||||
|
this.processFields(fields, separator, originValues, (value, sep) => {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
// 处理空字符串的情况
|
||||||
|
if (value === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// 处理复杂分隔符的情况
|
||||||
|
const escapedSeparator = sep.replaceAll(
|
||||||
|
/[.*+?^${}()|[\]\\]/g,
|
||||||
|
String.raw`\$&`,
|
||||||
|
);
|
||||||
|
return value.split(new RegExp(escapedSeparator));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理简单数组格式 ['field1', 'field2', ';'] 或 ['field1', 'field2']
|
||||||
|
if (arrayToStringFields.every((item) => typeof item === 'string')) {
|
||||||
|
const lastItem =
|
||||||
|
arrayToStringFields[arrayToStringFields.length - 1] || '';
|
||||||
|
const fields =
|
||||||
|
lastItem.length === 1
|
||||||
|
? arrayToStringFields.slice(0, -1)
|
||||||
|
: arrayToStringFields;
|
||||||
|
const separator = lastItem.length === 1 ? lastItem : ',';
|
||||||
|
processFields(fields, separator);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理嵌套数组格式 [['field1'], ';']
|
||||||
|
arrayToStringFields.forEach((fieldConfig) => {
|
||||||
|
if (Array.isArray(fieldConfig)) {
|
||||||
|
const [fields, separator = ','] = fieldConfig;
|
||||||
|
if (Array.isArray(fields)) {
|
||||||
|
processFields(fields, separator);
|
||||||
|
} else if (typeof originValues[fields] === 'string') {
|
||||||
|
const value = originValues[fields];
|
||||||
|
if (value === '') {
|
||||||
|
originValues[fields] = [];
|
||||||
|
} else {
|
||||||
|
const escapedSeparator = separator.replaceAll(
|
||||||
|
/[.*+?^${}()|[\]\\]/g,
|
||||||
|
String.raw`\$&`,
|
||||||
|
);
|
||||||
|
originValues[fields] = value.split(new RegExp(escapedSeparator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private processFields = (
|
||||||
|
fields: string[],
|
||||||
|
separator: string,
|
||||||
|
originValues: Record<string, any>,
|
||||||
|
transformFn: (value: any, separator: string) => any,
|
||||||
|
) => {
|
||||||
|
fields.forEach((field) => {
|
||||||
|
const value = originValues[field];
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
originValues[field] = transformFn(value, separator);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
private updateState() {
|
private updateState() {
|
||||||
const currentSchema = this.state?.schema ?? [];
|
const currentSchema = this.state?.schema ?? [];
|
||||||
const prevSchema = this.prevState?.schema ?? [];
|
const prevSchema = this.prevState?.schema ?? [];
|
||||||
|
|||||||
@@ -2,13 +2,18 @@ import type { FormRenderProps } from '../types';
|
|||||||
|
|
||||||
import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue';
|
import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue';
|
||||||
|
|
||||||
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
|
import {
|
||||||
|
breakpointsTailwind,
|
||||||
|
useBreakpoints,
|
||||||
|
useElementVisibility,
|
||||||
|
} from '@vueuse/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 动态计算行数
|
* 动态计算行数
|
||||||
*/
|
*/
|
||||||
export function useExpandable(props: FormRenderProps) {
|
export function useExpandable(props: FormRenderProps) {
|
||||||
const wrapperRef = useTemplateRef<HTMLElement>('wrapperRef');
|
const wrapperRef = useTemplateRef<HTMLElement>('wrapperRef');
|
||||||
|
const isVisible = useElementVisibility(wrapperRef);
|
||||||
const rowMapping = ref<Record<number, number>>({});
|
const rowMapping = ref<Record<number, number>>({});
|
||||||
// 是否已经计算过一次
|
// 是否已经计算过一次
|
||||||
const isCalculated = ref(false);
|
const isCalculated = ref(false);
|
||||||
@@ -31,6 +36,7 @@ export function useExpandable(props: FormRenderProps) {
|
|||||||
() => props.showCollapseButton,
|
() => props.showCollapseButton,
|
||||||
() => breakpoints.active().value,
|
() => breakpoints.active().value,
|
||||||
() => props.schema?.length,
|
() => props.schema?.length,
|
||||||
|
() => isVisible.value,
|
||||||
],
|
],
|
||||||
async ([val]) => {
|
async ([val]) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
|
|||||||
@@ -232,6 +232,12 @@ export type FieldMappingTime = [
|
|||||||
)?,
|
)?,
|
||||||
][];
|
][];
|
||||||
|
|
||||||
|
export type ArrayToStringFields = Array<
|
||||||
|
| [string[], string?] // 嵌套数组格式,可选分隔符
|
||||||
|
| string // 单个字段,使用默认分隔符
|
||||||
|
| string[] // 简单数组格式,最后一个元素可以是分隔符
|
||||||
|
>;
|
||||||
|
|
||||||
export interface FormSchema<
|
export interface FormSchema<
|
||||||
T extends BaseFormComponentType = BaseFormComponentType,
|
T extends BaseFormComponentType = BaseFormComponentType,
|
||||||
> extends FormCommonConfig {
|
> extends FormCommonConfig {
|
||||||
@@ -266,6 +272,10 @@ export interface FormFieldProps extends FormSchema {
|
|||||||
export interface FormRenderProps<
|
export interface FormRenderProps<
|
||||||
T extends BaseFormComponentType = BaseFormComponentType,
|
T extends BaseFormComponentType = BaseFormComponentType,
|
||||||
> {
|
> {
|
||||||
|
/**
|
||||||
|
* 表单字段数组映射字符串配置 默认使用","
|
||||||
|
*/
|
||||||
|
arrayToStringFields?: ArrayToStringFields;
|
||||||
/**
|
/**
|
||||||
* 是否展开,在showCollapseButton=true下生效
|
* 是否展开,在showCollapseButton=true下生效
|
||||||
*/
|
*/
|
||||||
@@ -296,6 +306,10 @@ export interface FormRenderProps<
|
|||||||
* 组件集合
|
* 组件集合
|
||||||
*/
|
*/
|
||||||
componentMap: Record<BaseFormComponentType, Component>;
|
componentMap: Record<BaseFormComponentType, Component>;
|
||||||
|
/**
|
||||||
|
* 表单字段映射到时间格式
|
||||||
|
*/
|
||||||
|
fieldMappingTime?: FieldMappingTime;
|
||||||
/**
|
/**
|
||||||
* 表单实例
|
* 表单实例
|
||||||
*/
|
*/
|
||||||
@@ -308,10 +322,15 @@ export interface FormRenderProps<
|
|||||||
* 表单定义
|
* 表单定义
|
||||||
*/
|
*/
|
||||||
schema?: FormSchema<T>[];
|
schema?: FormSchema<T>[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示展开/折叠
|
* 是否显示展开/折叠
|
||||||
*/
|
*/
|
||||||
showCollapseButton?: boolean;
|
showCollapseButton?: boolean;
|
||||||
|
/**
|
||||||
|
* 格式化日期
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表单栅格布局
|
* 表单栅格布局
|
||||||
* @default "grid-cols-1"
|
* @default "grid-cols-1"
|
||||||
@@ -339,6 +358,11 @@ export interface VbenFormProps<
|
|||||||
* 表单操作区域class
|
* 表单操作区域class
|
||||||
*/
|
*/
|
||||||
actionWrapperClass?: ClassType;
|
actionWrapperClass?: ClassType;
|
||||||
|
/**
|
||||||
|
* 表单字段数组映射字符串配置 默认使用","
|
||||||
|
*/
|
||||||
|
arrayToStringFields?: ArrayToStringFields;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表单字段映射
|
* 表单字段映射
|
||||||
*/
|
*/
|
||||||
@@ -354,11 +378,15 @@ export interface VbenFormProps<
|
|||||||
/**
|
/**
|
||||||
* 表单值变化回调
|
* 表单值变化回调
|
||||||
*/
|
*/
|
||||||
handleValuesChange?: (values: Record<string, any>) => void;
|
handleValuesChange?: (
|
||||||
|
values: Record<string, any>,
|
||||||
|
fieldsChanged: string[],
|
||||||
|
) => void;
|
||||||
/**
|
/**
|
||||||
* 重置按钮参数
|
* 重置按钮参数
|
||||||
*/
|
*/
|
||||||
resetButtonOptions?: ActionButtonOptions;
|
resetButtonOptions?: ActionButtonOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示默认操作按钮
|
* 是否显示默认操作按钮
|
||||||
* @default true
|
* @default true
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { Recordable } from '@vben-core/typings';
|
||||||
|
|
||||||
import type { ExtendedFormApi, VbenFormProps } from './types';
|
import type { ExtendedFormApi, VbenFormProps } from './types';
|
||||||
|
|
||||||
// import { toRaw, watch } from 'vue';
|
// import { toRaw, watch } from 'vue';
|
||||||
import { nextTick, onMounted, watch } from 'vue';
|
import { nextTick, onMounted, watch } from 'vue';
|
||||||
// import { isFunction } from '@vben-core/shared/utils';
|
|
||||||
|
|
||||||
import { useForwardPriorityValues } from '@vben-core/composables';
|
import { useForwardPriorityValues } from '@vben-core/composables';
|
||||||
import { cloneDeep } from '@vben-core/shared/utils';
|
import { cloneDeep, get, isEqual, set } from '@vben-core/shared/utils';
|
||||||
|
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
|
|
||||||
@@ -61,16 +62,46 @@ function handleKeyDownEnter(event: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleValuesChangeDebounced = useDebounceFn(async () => {
|
const handleValuesChangeDebounced = useDebounceFn(async () => {
|
||||||
forward.value.handleValuesChange?.(
|
|
||||||
cloneDeep(await forward.value.formApi.getValues()),
|
|
||||||
);
|
|
||||||
state.value.submitOnChange && forward.value.formApi?.validateAndSubmitForm();
|
state.value.submitOnChange && forward.value.formApi?.validateAndSubmitForm();
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
|
const valuesCache: Recordable<any> = {};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 只在挂载后开始监听,form.values会有一个初始化的过程
|
// 只在挂载后开始监听,form.values会有一个初始化的过程
|
||||||
await nextTick();
|
await nextTick();
|
||||||
watch(() => form.values, handleValuesChangeDebounced, { deep: true });
|
watch(
|
||||||
|
() => form.values,
|
||||||
|
async (newVal) => {
|
||||||
|
if (forward.value.handleValuesChange) {
|
||||||
|
const fields = state.value.schema?.map((item) => {
|
||||||
|
return item.fieldName;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fields && fields.length > 0) {
|
||||||
|
const changedFields: string[] = [];
|
||||||
|
fields.forEach((field) => {
|
||||||
|
const newFieldValue = get(newVal, field);
|
||||||
|
const oldFieldValue = get(valuesCache, field);
|
||||||
|
if (!isEqual(newFieldValue, oldFieldValue)) {
|
||||||
|
changedFields.push(field);
|
||||||
|
set(valuesCache, field, newFieldValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (changedFields.length > 0) {
|
||||||
|
// 调用handleValuesChange回调,传入所有表单值的深拷贝和变更的字段列表
|
||||||
|
forward.value.handleValuesChange(
|
||||||
|
cloneDeep(await forward.value.formApi.getValues()),
|
||||||
|
changedFields,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleValuesChangeDebounced();
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/layout-ui",
|
"name": "@vben-core/layout-ui",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/menu-ui",
|
"name": "@vben-core/menu-ui",
|
||||||
"version": "5.5.4",
|
"version": "5.5.5",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { VbenIcon } from '@vben-core/shadcn-ui';
|
|||||||
import { useMenuContext } from '../hooks';
|
import { useMenuContext } from '../hooks';
|
||||||
|
|
||||||
interface Props extends MenuItemProps {
|
interface Props extends MenuItemProps {
|
||||||
isMenuMore: boolean;
|
isMenuMore?: boolean;
|
||||||
isTopLevelMenuSubmenu: boolean;
|
isTopLevelMenuSubmenu: boolean;
|
||||||
level?: number;
|
level?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,6 +208,8 @@ onBeforeUnmount(() => {
|
|||||||
nsMenu.e('popup-container'),
|
nsMenu.e('popup-container'),
|
||||||
is(rootMenu.theme, true),
|
is(rootMenu.theme, true),
|
||||||
opened ? '' : 'hidden',
|
opened ? '' : 'hidden',
|
||||||
|
'overflow-auto',
|
||||||
|
'max-h-[calc(var(--radix-hover-card-content-available-height)-20px)]',
|
||||||
]"
|
]"
|
||||||
:content-props="contentProps"
|
:content-props="contentProps"
|
||||||
:open="true"
|
:open="true"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type { AlertProps, BeforeCloseScope, PromptProps } from './alert';
|
|||||||
import { h, nextTick, ref, render } from 'vue';
|
import { h, nextTick, ref, render } from 'vue';
|
||||||
|
|
||||||
import { useSimpleLocale } from '@vben-core/composables';
|
import { useSimpleLocale } from '@vben-core/composables';
|
||||||
import { Input } from '@vben-core/shadcn-ui';
|
import { Input, VbenRenderContent } from '@vben-core/shadcn-ui';
|
||||||
import { isFunction, isString } from '@vben-core/shared/utils';
|
import { isFunction, isString } from '@vben-core/shared/utils';
|
||||||
|
|
||||||
import Alert from './alert.vue';
|
import Alert from './alert.vue';
|
||||||
@@ -146,11 +146,7 @@ export async function vbenPrompt<T = any>(
|
|||||||
const inputComponentRef = ref<null | VNode>(null);
|
const inputComponentRef = ref<null | VNode>(null);
|
||||||
const staticContents: Component[] = [];
|
const staticContents: Component[] = [];
|
||||||
|
|
||||||
if (isString(content)) {
|
staticContents.push(h(VbenRenderContent, { content, renderBr: true }));
|
||||||
staticContents.push(h('span', content));
|
|
||||||
} else if (content) {
|
|
||||||
staticContents.push(content as Component);
|
|
||||||
}
|
|
||||||
|
|
||||||
const modelPropName = _modelPropName || 'modelValue';
|
const modelPropName = _modelPropName || 'modelValue';
|
||||||
const componentProps = { ..._componentProps };
|
const componentProps = { ..._componentProps };
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import type { Component, VNode, VNodeArrayChildren } from 'vue';
|
|||||||
|
|
||||||
import type { Recordable } from '@vben-core/typings';
|
import type { Recordable } from '@vben-core/typings';
|
||||||
|
|
||||||
|
import { createContext } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning';
|
export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning';
|
||||||
|
|
||||||
export type BeforeCloseScope = {
|
export type BeforeCloseScope = {
|
||||||
@@ -34,8 +36,14 @@ export type AlertProps = {
|
|||||||
contentClass?: string;
|
contentClass?: string;
|
||||||
/** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/
|
/** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/
|
||||||
contentMasking?: boolean;
|
contentMasking?: boolean;
|
||||||
|
/** 弹窗底部内容(与按钮在同一个容器中) */
|
||||||
|
footer?: Component | string;
|
||||||
/** 弹窗的图标(在标题的前面) */
|
/** 弹窗的图标(在标题的前面) */
|
||||||
icon?: Component | IconType;
|
icon?: Component | IconType;
|
||||||
|
/**
|
||||||
|
* 弹窗遮罩模糊效果
|
||||||
|
*/
|
||||||
|
overlayBlur?: number;
|
||||||
/** 是否显示取消按钮 */
|
/** 是否显示取消按钮 */
|
||||||
showCancel?: boolean;
|
showCancel?: boolean;
|
||||||
/** 弹窗标题 */
|
/** 弹窗标题 */
|
||||||
@@ -64,3 +72,28 @@ export type PromptProps<T = any> = {
|
|||||||
/** 输入组件的值属性名 */
|
/** 输入组件的值属性名 */
|
||||||
modelPropName?: string;
|
modelPropName?: string;
|
||||||
} & Omit<AlertProps, 'beforeClose'>;
|
} & Omit<AlertProps, 'beforeClose'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alert上下文
|
||||||
|
*/
|
||||||
|
export type AlertContext = {
|
||||||
|
/** 执行取消操作 */
|
||||||
|
doCancel: () => void;
|
||||||
|
/** 执行确认操作 */
|
||||||
|
doConfirm: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const [injectAlertContext, provideAlertContext] =
|
||||||
|
createContext<AlertContext>('VbenAlertContext');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Alert上下文
|
||||||
|
* @returns AlertContext
|
||||||
|
*/
|
||||||
|
export function useAlertContext() {
|
||||||
|
const context = injectAlertContext();
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useAlertContext must be used within an AlertProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { Component } from 'vue';
|
|||||||
|
|
||||||
import type { AlertProps } from './alert';
|
import type { AlertProps } from './alert';
|
||||||
|
|
||||||
import { computed, h, nextTick, ref, watch } from 'vue';
|
import { computed, h, nextTick, ref } from 'vue';
|
||||||
|
|
||||||
import { useSimpleLocale } from '@vben-core/composables';
|
import { useSimpleLocale } from '@vben-core/composables';
|
||||||
import {
|
import {
|
||||||
@@ -28,6 +28,8 @@ import {
|
|||||||
import { globalShareState } from '@vben-core/shared/global-state';
|
import { globalShareState } from '@vben-core/shared/global-state';
|
||||||
import { cn } from '@vben-core/shared/utils';
|
import { cn } from '@vben-core/shared/utils';
|
||||||
|
|
||||||
|
import { provideAlertContext } from './alert';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<AlertProps>(), {
|
const props = withDefaults(defineProps<AlertProps>(), {
|
||||||
bordered: true,
|
bordered: true,
|
||||||
buttonAlign: 'end',
|
buttonAlign: 'end',
|
||||||
@@ -39,14 +41,16 @@ const open = defineModel<boolean>('open', { default: false });
|
|||||||
const { $t } = useSimpleLocale();
|
const { $t } = useSimpleLocale();
|
||||||
const components = globalShareState.getComponents();
|
const components = globalShareState.getComponents();
|
||||||
const isConfirm = ref(false);
|
const isConfirm = ref(false);
|
||||||
watch(open, async (val) => {
|
|
||||||
await nextTick();
|
function onAlertClosed() {
|
||||||
if (val) {
|
emits('closed', isConfirm.value);
|
||||||
isConfirm.value = false;
|
isConfirm.value = false;
|
||||||
} else {
|
}
|
||||||
emits('closed', isConfirm.value);
|
|
||||||
}
|
function onEscapeKeyDown() {
|
||||||
});
|
isConfirm.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
const getIconRender = computed(() => {
|
const getIconRender = computed(() => {
|
||||||
let iconRender: Component | null = null;
|
let iconRender: Component | null = null;
|
||||||
if (props.icon) {
|
if (props.icon) {
|
||||||
@@ -89,6 +93,22 @@ const getIconRender = computed(() => {
|
|||||||
}
|
}
|
||||||
return iconRender;
|
return iconRender;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function doCancel() {
|
||||||
|
handleCancel();
|
||||||
|
handleOpenChange(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function doConfirm() {
|
||||||
|
handleConfirm();
|
||||||
|
handleOpenChange(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
provideAlertContext({
|
||||||
|
doCancel,
|
||||||
|
doConfirm,
|
||||||
|
});
|
||||||
|
|
||||||
function handleConfirm() {
|
function handleConfirm() {
|
||||||
isConfirm.value = true;
|
isConfirm.value = true;
|
||||||
emits('confirm');
|
emits('confirm');
|
||||||
@@ -100,6 +120,7 @@ function handleCancel() {
|
|||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
async function handleOpenChange(val: boolean) {
|
async function handleOpenChange(val: boolean) {
|
||||||
|
await nextTick(); // 等待标记isConfirm状态
|
||||||
if (!val && props.beforeClose) {
|
if (!val && props.beforeClose) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
@@ -120,15 +141,17 @@ async function handleOpenChange(val: boolean) {
|
|||||||
<AlertDialogContent
|
<AlertDialogContent
|
||||||
:open="open"
|
:open="open"
|
||||||
:centered="centered"
|
:centered="centered"
|
||||||
|
:overlay-blur="overlayBlur"
|
||||||
@opened="emits('opened')"
|
@opened="emits('opened')"
|
||||||
|
@closed="onAlertClosed"
|
||||||
|
@escape-key-down="onEscapeKeyDown"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
containerClass,
|
containerClass,
|
||||||
'left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] flex-col p-0 duration-300 sm:rounded-[var(--radius)] md:w-[520px] md:max-w-[80%]',
|
'left-0 right-0 mx-auto flex max-h-[80%] flex-col p-0 duration-300 sm:rounded-[var(--radius)] md:w-[520px] md:max-w-[80%]',
|
||||||
{
|
{
|
||||||
'border-border border': bordered,
|
'border-border border': bordered,
|
||||||
'shadow-3xl': !bordered,
|
'shadow-3xl': !bordered,
|
||||||
'top-1/2 !-translate-y-1/2': centered,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
@@ -138,7 +161,7 @@ async function handleOpenChange(val: boolean) {
|
|||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<component :is="getIconRender" class="mr-2" />
|
<component :is="getIconRender" class="mr-2" />
|
||||||
<span class="flex-auto">{{ $t(title) }}</span>
|
<span class="flex-auto">{{ $t(title) }}</span>
|
||||||
<AlertDialogCancel v-if="showCancel">
|
<AlertDialogCancel v-if="showCancel" as-child>
|
||||||
<VbenButton
|
<VbenButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
@@ -152,22 +175,27 @@ async function handleOpenChange(val: boolean) {
|
|||||||
</div>
|
</div>
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
<div class="m-4 mb-6 min-h-[30px]">
|
<div class="m-4 min-h-[30px]">
|
||||||
<VbenRenderContent :content="content" render-br />
|
<VbenRenderContent :content="content" render-br />
|
||||||
</div>
|
</div>
|
||||||
<VbenLoading v-if="loading && contentMasking" :spinning="loading" />
|
<VbenLoading v-if="loading && contentMasking" :spinning="loading" />
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
<div class="flex justify-end gap-x-2" :class="`justify-${buttonAlign}`">
|
<div
|
||||||
<AlertDialogCancel v-if="showCancel" :disabled="loading">
|
class="flex items-center justify-end gap-x-2"
|
||||||
|
:class="`justify-${buttonAlign}`"
|
||||||
|
>
|
||||||
|
<VbenRenderContent :content="footer" />
|
||||||
|
<AlertDialogCancel v-if="showCancel" as-child>
|
||||||
<component
|
<component
|
||||||
:is="components.DefaultButton || VbenButton"
|
:is="components.DefaultButton || VbenButton"
|
||||||
|
:disabled="loading"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@click="handleCancel"
|
@click="handleCancel"
|
||||||
>
|
>
|
||||||
{{ cancelText || $t('cancel') }}
|
{{ cancelText || $t('cancel') }}
|
||||||
</component>
|
</component>
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction>
|
<AlertDialogAction as-child>
|
||||||
<component
|
<component
|
||||||
:is="components.PrimaryButton || VbenButton"
|
:is="components.PrimaryButton || VbenButton"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
export * from './alert';
|
export type {
|
||||||
|
AlertProps,
|
||||||
|
BeforeCloseScope,
|
||||||
|
IconType,
|
||||||
|
PromptProps,
|
||||||
|
} from './alert';
|
||||||
|
export { useAlertContext } from './alert';
|
||||||
export { default as Alert } from './alert.vue';
|
export { default as Alert } from './alert.vue';
|
||||||
export {
|
export {
|
||||||
vbenAlert as alert,
|
vbenAlert as alert,
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ vi.mock('@vben-core/shared/store', () => {
|
|||||||
return {
|
return {
|
||||||
isFunction: (fn: any) => typeof fn === 'function',
|
isFunction: (fn: any) => typeof fn === 'function',
|
||||||
Store: class {
|
Store: class {
|
||||||
|
get state() {
|
||||||
|
return this._state;
|
||||||
|
}
|
||||||
private _state: DrawerState;
|
private _state: DrawerState;
|
||||||
|
|
||||||
private options: any;
|
private options: any;
|
||||||
|
|
||||||
constructor(initialState: DrawerState, options: any) {
|
constructor(initialState: DrawerState, options: any) {
|
||||||
@@ -25,10 +29,6 @@ vi.mock('@vben-core/shared/store', () => {
|
|||||||
this._state = fn(this._state);
|
this._state = fn(this._state);
|
||||||
this.options.onUpdate();
|
this.options.onUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
get state() {
|
|
||||||
return this._state;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -54,7 +54,6 @@ describe('drawerApi', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should close the drawer if onBeforeClose allows it', () => {
|
it('should close the drawer if onBeforeClose allows it', () => {
|
||||||
drawerApi.open();
|
|
||||||
drawerApi.close();
|
drawerApi.close();
|
||||||
expect(drawerApi.store.state.isOpen).toBe(false);
|
expect(drawerApi.store.state.isOpen).toBe(false);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ export class DrawerApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭弹窗
|
* 关闭抽屉
|
||||||
|
* @description 关闭抽屉时会调用 onBeforeClose 钩子函数,如果 onBeforeClose 返回 false,则不关闭弹窗
|
||||||
*/
|
*/
|
||||||
async close() {
|
async close() {
|
||||||
// 通过 onBeforeClose 钩子函数来判断是否允许关闭弹窗
|
// 通过 onBeforeClose 钩子函数来判断是否允许关闭弹窗
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ export interface DrawerProps {
|
|||||||
* 弹窗描述
|
* 弹窗描述
|
||||||
*/
|
*/
|
||||||
description?: string;
|
description?: string;
|
||||||
|
/**
|
||||||
|
* 在关闭时销毁抽屉
|
||||||
|
*/
|
||||||
|
destroyOnClose?: boolean;
|
||||||
/**
|
/**
|
||||||
* 是否显示底部
|
* 是否显示底部
|
||||||
* @default true
|
* @default true
|
||||||
@@ -143,10 +147,6 @@ export interface DrawerApiOptions extends DrawerState {
|
|||||||
* 独立的抽屉组件
|
* 独立的抽屉组件
|
||||||
*/
|
*/
|
||||||
connectedComponent?: Component;
|
connectedComponent?: Component;
|
||||||
/**
|
|
||||||
* 在关闭时销毁抽屉。仅在使用 connectedComponent 时有效
|
|
||||||
*/
|
|
||||||
destroyOnClose?: boolean;
|
|
||||||
/**
|
/**
|
||||||
* 关闭前的回调,返回 false 可以阻止关闭
|
* 关闭前的回调,返回 false 可以阻止关闭
|
||||||
* @returns
|
* @returns
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user