Compare commits
163 Commits
1.3.0-back
...
1.3.6-back
Author | SHA1 | Date | |
---|---|---|---|
![]() |
10b8b81954 | ||
![]() |
1f50c95c66 | ||
![]() |
cd4706b717 | ||
![]() |
769aceb55f | ||
![]() |
e89cf400c0 | ||
![]() |
9e67929ee7 | ||
![]() |
7926865bf9 | ||
![]() |
51fbfcedd2 | ||
![]() |
8f71d6a5d9 | ||
![]() |
90625782c0 | ||
![]() |
12d0ba24e5 | ||
![]() |
540f24ed43 | ||
![]() |
c57d3f32b5 | ||
![]() |
b52f3ba0c5 | ||
![]() |
84ef207d9c | ||
![]() |
e68fff58e8 | ||
![]() |
63c06e02b2 | ||
![]() |
bf70539221 | ||
![]() |
5949c73a30 | ||
![]() |
cc6c9bf7a0 | ||
![]() |
6b1aab9c67 | ||
![]() |
8f4d3d418d | ||
![]() |
aa086a2800 | ||
![]() |
3b3f8e4e44 | ||
![]() |
b0763d6429 | ||
![]() |
f94ca10adf | ||
![]() |
4471bc7a5d | ||
![]() |
5689ac60ff | ||
![]() |
045bc4e5ee | ||
![]() |
17a18fc9ba | ||
![]() |
41152d1722 | ||
![]() |
f1af9f8f6e | ||
![]() |
0517a7014f | ||
![]() |
3e6d608a2f | ||
![]() |
5de954baa4 | ||
![]() |
add1e61b6f | ||
![]() |
9f978cc9b0 | ||
![]() |
89dd4b8131 | ||
![]() |
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 | ||
![]() |
4dc7543bb6 | ||
![]() |
d8e7945f9f | ||
![]() |
2fd1fdcb32 | ||
![]() |
86da3cedc2 | ||
![]() |
44ba945a12 | ||
![]() |
2680101872 | ||
![]() |
1c2e27613c | ||
![]() |
3e7a2336b0 | ||
![]() |
022d5182d7 | ||
![]() |
329a176a5c | ||
![]() |
41962ef380 | ||
![]() |
9003df713c | ||
![]() |
ebb4738be7 | ||
![]() |
ad7c33a7d6 | ||
![]() |
a114335a56 | ||
![]() |
9379093a4f | ||
![]() |
c9014d0338 | ||
![]() |
b8ec8edb38 | ||
![]() |
ed26dca64e | ||
![]() |
08c6496e24 | ||
![]() |
a8c5df38e9 | ||
![]() |
5b9f647cfd | ||
![]() |
ae6bf6ee53 | ||
![]() |
77894d5df4 | ||
![]() |
ba8f36a2c0 | ||
![]() |
133abe9ded | ||
![]() |
ef390ae636 | ||
![]() |
6d2f4e8486 | ||
![]() |
c4962aaf85 | ||
![]() |
f7128b099e | ||
![]() |
5510b6dea4 | ||
![]() |
98f658d46f | ||
![]() |
e307db2f3d | ||
![]() |
e6dab8300d | ||
![]() |
eb9f278e7f | ||
![]() |
34e5812de9 | ||
![]() |
07587c0faf | ||
![]() |
88316d7498 | ||
![]() |
53e02d46c2 | ||
![]() |
5e1de6fc79 | ||
![]() |
7463df053a | ||
![]() |
1286b52135 | ||
![]() |
92fe406ae9 | ||
![]() |
5b72d9b79d | ||
![]() |
b97fe47afd | ||
![]() |
4f2354b53a | ||
![]() |
8f9006c96d | ||
![]() |
71e8d12b70 |
@@ -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
|
||||||
|
@@ -1,6 +0,0 @@
|
|||||||
echo Start running commit-msg hook...
|
|
||||||
|
|
||||||
# Check whether the git commit information is standardized
|
|
||||||
pnpm exec commitlint --edit "$1"
|
|
||||||
|
|
||||||
echo Run commit-msg hook done.
|
|
@@ -1,3 +0,0 @@
|
|||||||
# 每次 git pull 之后, 安装依赖
|
|
||||||
|
|
||||||
pnpm install
|
|
@@ -1,7 +0,0 @@
|
|||||||
# update `.vscode/vben-admin.code-workspace` file
|
|
||||||
pnpm vsh code-workspace --auto-commit
|
|
||||||
|
|
||||||
# Format and submit code according to lintstagedrc.js configuration
|
|
||||||
pnpm exec lint-staged
|
|
||||||
|
|
||||||
echo Run pre-commit hook done.
|
|
@@ -1,20 +0,0 @@
|
|||||||
export default {
|
|
||||||
'*.md': ['prettier --cache --ignore-unknown --write'],
|
|
||||||
'*.vue': [
|
|
||||||
'prettier --write',
|
|
||||||
'eslint --cache --fix',
|
|
||||||
'stylelint --fix --allow-empty-input',
|
|
||||||
],
|
|
||||||
'*.{js,jsx,ts,tsx}': [
|
|
||||||
'prettier --cache --ignore-unknown --write',
|
|
||||||
'eslint --cache --fix',
|
|
||||||
],
|
|
||||||
'*.{scss,less,styl,html,vue,css}': [
|
|
||||||
'prettier --cache --ignore-unknown --write',
|
|
||||||
'stylelint --fix --allow-empty-input',
|
|
||||||
],
|
|
||||||
'package.json': ['prettier --cache --write'],
|
|
||||||
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
|
||||||
'prettier --cache --write--parser json',
|
|
||||||
],
|
|
||||||
};
|
|
@@ -1 +1 @@
|
|||||||
20.14.0
|
22.1.0
|
||||||
|
2
.npmrc
2
.npmrc
@@ -1,5 +1,5 @@
|
|||||||
registry = "https://registry.npmmirror.com"
|
registry = "https://registry.npmmirror.com"
|
||||||
public-hoist-pattern[]=husky
|
public-hoist-pattern[]=lefthook
|
||||||
public-hoist-pattern[]=eslint
|
public-hoist-pattern[]=eslint
|
||||||
public-hoist-pattern[]=prettier
|
public-hoist-pattern[]=prettier
|
||||||
public-hoist-pattern[]=prettier-plugin-tailwindcss
|
public-hoist-pattern[]=prettier-plugin-tailwindcss
|
||||||
|
21
.vscode/settings.json
vendored
21
.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,
|
||||||
@@ -217,17 +220,27 @@
|
|||||||
"*.env": "$(capture).env.*",
|
"*.env": "$(capture).env.*",
|
||||||
"README.md": "README*,CHANGELOG*,LICENSE,CNAME",
|
"README.md": "README*,CHANGELOG*,LICENSE,CNAME",
|
||||||
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json",
|
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json",
|
||||||
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json",
|
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml",
|
||||||
"tailwind.config.mjs": "postcss.*"
|
"tailwind.config.mjs": "postcss.*"
|
||||||
},
|
},
|
||||||
"commentTranslate.hover.enabled": false,
|
"commentTranslate.hover.enabled": false,
|
||||||
"commentTranslate.multiLineMerge": true,
|
"commentTranslate.multiLineMerge": true,
|
||||||
"vue.server.hybridMode": true,
|
"vue.server.hybridMode": true,
|
||||||
"vitest.disableWorkspaceWarning": true,
|
"vitest.disableWorkspaceWarning": true,
|
||||||
"cSpell.words": ["tinymce", "vditor"],
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"editor.linkedEditing": true, // 自动同步更改html标签,
|
"editor.linkedEditing": true, // 自动同步更改html标签,
|
||||||
"vscodeCustomCodeColor.highlightValue": "v-access", // v-access显示的颜色
|
"vscodeCustomCodeColor.highlightValue": "v-access", // v-access显示的颜色
|
||||||
"vscodeCustomCodeColor.highlightValueColor": "#CCFFFF",
|
"vscodeCustomCodeColor.highlightValueColor": "#CCFFFF",
|
||||||
"oxc.enable": false
|
"oxc.enable": false,
|
||||||
|
"cSpell.words": [
|
||||||
|
"archiver",
|
||||||
|
"axios",
|
||||||
|
"dotenv",
|
||||||
|
"isequal",
|
||||||
|
"jspm",
|
||||||
|
"napi",
|
||||||
|
"nolebase",
|
||||||
|
"rollup",
|
||||||
|
"vitest"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
64
CHANGELOG.md
64
CHANGELOG.md
@@ -1,3 +1,67 @@
|
|||||||
|
# 1.3.6
|
||||||
|
|
||||||
|
**BUG FIX**
|
||||||
|
|
||||||
|
- oss配置switch切换 导致报错`存储类型找不到`
|
||||||
|
- 文件上传无法正确清除(innerList)
|
||||||
|
|
||||||
|
# 1.3.5
|
||||||
|
|
||||||
|
**BUG FIX**
|
||||||
|
|
||||||
|
- 某些带Vxe表格弹窗 关闭后没有正常清理表格数据的问题
|
||||||
|
|
||||||
|
# 1.3.4
|
||||||
|
|
||||||
|
**BUG FIX**
|
||||||
|
|
||||||
|
- 文件上传多次触发导致数据不一致 https://gitee.com/dapppp/ruoyi-plus-vben5/issues/IC3BK6
|
||||||
|
|
||||||
|
**PREFORMANCE**
|
||||||
|
|
||||||
|
- 浏览器返回按钮/手势操作时 弹窗不会被关闭(keepAlive导致)
|
||||||
|
|
||||||
|
# 1.3.3
|
||||||
|
|
||||||
|
**BUG FIX**
|
||||||
|
|
||||||
|
- 工作流list展示在开启缩放会有误差导致触底逻辑不会触发
|
||||||
|
|
||||||
|
**OTHER**
|
||||||
|
|
||||||
|
- 代码生成预览对模板的提示...(下载都懒得点一下吗)
|
||||||
|
|
||||||
|
# 1.3.2
|
||||||
|
|
||||||
|
**REFACTOR**
|
||||||
|
|
||||||
|
- 所有表格操作列宽度调整为'auto', 这样会根据子元素宽度适配(比如没有分配权限的情况)
|
||||||
|
- 菜单图标更新了一部分 sql同步更新
|
||||||
|
|
||||||
|
**OTHER**
|
||||||
|
|
||||||
|
- 暂时锁死vite依赖 i18n会报错
|
||||||
|
|
||||||
|
# 1.3.1
|
||||||
|
|
||||||
|
**REFACTOR**
|
||||||
|
|
||||||
|
- 所有Modal/Drawer表单关闭前会进行表单数据对比来弹出提示框
|
||||||
|
- 字典项颜色选择从`原生input type=color`改为`vue3-colorpicker`组件
|
||||||
|
- 全局Header: ClientID 更改大小写 [spring的问题导致](https://gitee.com/dapppp/ruoyi-plus-vben5/issues/IC0BDS)
|
||||||
|
|
||||||
|
**BUG FIX**
|
||||||
|
|
||||||
|
- getVxePopupContainer逻辑调整 解决表格固定高度展开不全的问题
|
||||||
|
|
||||||
|
**FEATURES**
|
||||||
|
|
||||||
|
- 字典渲染支持loading(length为0情况)
|
||||||
|
|
||||||
|
**OTHERS**
|
||||||
|
|
||||||
|
- useForm的组件改为异步导入(官方更新) bootstrap.js体积从2M降到600K 首屏加载速度提升
|
||||||
|
|
||||||
# 1.3.0
|
# 1.3.0
|
||||||
|
|
||||||
注意: 如果你使用老版本的`文件上传`/`图片上传` 可暂时使用
|
注意: 如果你使用老版本的`文件上传`/`图片上传` 可暂时使用
|
||||||
|
@@ -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.6",
|
||||||
"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": {
|
||||||
@@ -54,7 +54,8 @@
|
|||||||
"tinymce": "^7.3.0",
|
"tinymce": "^7.3.0",
|
||||||
"unplugin-vue-components": "^0.27.3",
|
"unplugin-vue-components": "^0.27.3",
|
||||||
"vue": "catalog:",
|
"vue": "catalog:",
|
||||||
"vue-router": "catalog:"
|
"vue-router": "catalog:",
|
||||||
|
"vue3-colorpicker": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
|
@@ -8,58 +8,92 @@ import type { Component } from 'vue';
|
|||||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||||
import type { Recordable } from '@vben/types';
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import { computed, defineComponent, getCurrentInstance, h, ref } from 'vue';
|
import {
|
||||||
|
defineAsyncComponent,
|
||||||
|
defineComponent,
|
||||||
|
getCurrentInstance,
|
||||||
|
h,
|
||||||
|
ref,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {
|
import { notification } from 'ant-design-vue';
|
||||||
AutoComplete,
|
|
||||||
Button,
|
|
||||||
Checkbox,
|
|
||||||
CheckboxGroup,
|
|
||||||
DatePicker,
|
|
||||||
Divider,
|
|
||||||
Input,
|
|
||||||
InputNumber,
|
|
||||||
InputPassword,
|
|
||||||
Mentions,
|
|
||||||
notification,
|
|
||||||
Radio,
|
|
||||||
RadioGroup,
|
|
||||||
RangePicker,
|
|
||||||
Rate,
|
|
||||||
Select,
|
|
||||||
Space,
|
|
||||||
Switch,
|
|
||||||
Textarea,
|
|
||||||
TimePicker,
|
|
||||||
TreeSelect,
|
|
||||||
Upload,
|
|
||||||
} from 'ant-design-vue';
|
|
||||||
|
|
||||||
import { Tinymce as RichTextarea } from '#/components/tinymce';
|
|
||||||
import { FileUpload, ImageUpload } from '#/components/upload';
|
|
||||||
import { FileUploadOld, ImageUploadOld } from '#/components/upload-old';
|
import { FileUploadOld, ImageUploadOld } from '#/components/upload-old';
|
||||||
|
|
||||||
|
const RichTextarea = defineAsyncComponent(() =>
|
||||||
|
import('#/components/tinymce/index').then((res) => res.Tinymce),
|
||||||
|
);
|
||||||
|
|
||||||
|
const FileUpload = defineAsyncComponent(() =>
|
||||||
|
import('#/components/upload').then((res) => res.FileUpload),
|
||||||
|
);
|
||||||
|
|
||||||
|
const ImageUpload = defineAsyncComponent(() =>
|
||||||
|
import('#/components/upload').then((res) => res.ImageUpload),
|
||||||
|
);
|
||||||
|
|
||||||
|
const AutoComplete = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/auto-complete'),
|
||||||
|
);
|
||||||
|
const Button = defineAsyncComponent(() => import('ant-design-vue/es/button'));
|
||||||
|
const Checkbox = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/checkbox'),
|
||||||
|
);
|
||||||
|
const CheckboxGroup = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/checkbox').then((res) => res.CheckboxGroup),
|
||||||
|
);
|
||||||
|
const DatePicker = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/date-picker'),
|
||||||
|
);
|
||||||
|
const Divider = defineAsyncComponent(() => import('ant-design-vue/es/divider'));
|
||||||
|
const Input = defineAsyncComponent(() => import('ant-design-vue/es/input'));
|
||||||
|
const InputNumber = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/input-number'),
|
||||||
|
);
|
||||||
|
const InputPassword = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/input').then((res) => res.InputPassword),
|
||||||
|
);
|
||||||
|
const Mentions = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/mentions'),
|
||||||
|
);
|
||||||
|
const Radio = defineAsyncComponent(() => import('ant-design-vue/es/radio'));
|
||||||
|
const RadioGroup = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/radio').then((res) => res.RadioGroup),
|
||||||
|
);
|
||||||
|
const RangePicker = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/date-picker').then((res) => res.RangePicker),
|
||||||
|
);
|
||||||
|
const Rate = defineAsyncComponent(() => import('ant-design-vue/es/rate'));
|
||||||
|
const Select = defineAsyncComponent(() => import('ant-design-vue/es/select'));
|
||||||
|
const Space = defineAsyncComponent(() => import('ant-design-vue/es/space'));
|
||||||
|
const Switch = defineAsyncComponent(() => import('ant-design-vue/es/switch'));
|
||||||
|
const Textarea = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/input').then((res) => res.Textarea),
|
||||||
|
);
|
||||||
|
const TimePicker = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/time-picker'),
|
||||||
|
);
|
||||||
|
const TreeSelect = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/tree-select'),
|
||||||
|
);
|
||||||
|
const Upload = defineAsyncComponent(() => import('ant-design-vue/es/upload'));
|
||||||
|
|
||||||
const withDefaultPlaceholder = <T extends Component>(
|
const withDefaultPlaceholder = <T extends Component>(
|
||||||
component: T,
|
component: T,
|
||||||
type: 'input' | 'select',
|
type: 'input' | 'select',
|
||||||
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();
|
||||||
@@ -78,7 +112,7 @@ const withDefaultPlaceholder = <T extends Component>(
|
|||||||
component,
|
component,
|
||||||
{
|
{
|
||||||
...componentProps,
|
...componentProps,
|
||||||
placeholder: placeholder.value,
|
placeholder,
|
||||||
...props,
|
...props,
|
||||||
...attrs,
|
...attrs,
|
||||||
ref: innerRef,
|
ref: innerRef,
|
||||||
@@ -128,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,
|
||||||
|
@@ -93,9 +93,12 @@ function createRequestClient(baseURL: string) {
|
|||||||
const language = preferences.app.locale.replace('-', '_');
|
const language = preferences.app.locale.replace('-', '_');
|
||||||
config.headers['Accept-Language'] = language;
|
config.headers['Accept-Language'] = language;
|
||||||
config.headers['Content-Language'] = language;
|
config.headers['Content-Language'] = language;
|
||||||
// 添加全局clientId
|
/**
|
||||||
config.headers.clientId = clientId;
|
* 添加全局clientId
|
||||||
|
* 关于header的clientId被错误绑定到实体类
|
||||||
|
* https://gitee.com/dapppp/ruoyi-plus-vben5/issues/IC0BDS
|
||||||
|
*/
|
||||||
|
config.headers.ClientID = clientId;
|
||||||
/**
|
/**
|
||||||
* 格式化get/delete参数
|
* 格式化get/delete参数
|
||||||
* 如果包含自定义的paramsSerializer则不走此逻辑
|
* 如果包含自定义的paramsSerializer则不走此逻辑
|
||||||
|
@@ -40,6 +40,7 @@ export function ossConfigChangeStatus(data: any) {
|
|||||||
const requestData = {
|
const requestData = {
|
||||||
ossConfigId: data.ossConfigId,
|
ossConfigId: data.ossConfigId,
|
||||||
status: data.status,
|
status: data.status,
|
||||||
|
configKey: data.configKey,
|
||||||
};
|
};
|
||||||
return requestClient.putWithMsg(Api.ossConfigChangeStatus, requestData);
|
return requestClient.putWithMsg(Api.ossConfigChangeStatus, requestData);
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import { createApp, watchEffect } from 'vue';
|
import { createApp, watchEffect } from 'vue';
|
||||||
|
|
||||||
import { registerAccessDirective } from '@vben/access';
|
import { registerAccessDirective } from '@vben/access';
|
||||||
import { initTippy, registerLoadingDirective } from '@vben/common-ui';
|
import { registerLoadingDirective } from '@vben/common-ui/es/loading';
|
||||||
import { MotionPlugin } from '@vben/plugins/motion';
|
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import { initStores } from '@vben/stores';
|
import { initStores } from '@vben/stores';
|
||||||
import '@vben/styles';
|
import '@vben/styles';
|
||||||
@@ -50,12 +49,14 @@ async function bootstrap(namespace: string) {
|
|||||||
registerAccessDirective(app);
|
registerAccessDirective(app);
|
||||||
|
|
||||||
// 初始化 tippy
|
// 初始化 tippy
|
||||||
|
const { initTippy } = await import('@vben/common-ui/es/tippy');
|
||||||
initTippy(app);
|
initTippy(app);
|
||||||
|
|
||||||
// 配置路由及路由守卫
|
// 配置路由及路由守卫
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
// 配置Motion插件
|
// 配置Motion插件
|
||||||
|
const { MotionPlugin } = await import('@vben/plugins/motion');
|
||||||
app.use(MotionPlugin);
|
app.use(MotionPlugin);
|
||||||
|
|
||||||
// 动态更新标题
|
// 动态更新标题
|
||||||
|
@@ -4,7 +4,7 @@ import type { DictData } from '#/api/system/dict/dict-data-model';
|
|||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
import { Tag } from 'ant-design-vue';
|
import { Spin, Tag } from 'ant-design-vue';
|
||||||
|
|
||||||
import { tagTypes } from './data';
|
import { tagTypes } from './data';
|
||||||
|
|
||||||
@@ -41,12 +41,22 @@ const label = computed<number | string>(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const tagComponent = computed(() => (color.value ? Tag : 'div'));
|
const tagComponent = computed(() => (color.value ? Tag : 'div'));
|
||||||
|
|
||||||
|
const loading = computed(() => {
|
||||||
|
return props.dicts?.length === 0;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<component :is="tagComponent" :class="cssClass" :color="color">
|
<component
|
||||||
|
v-if="!loading"
|
||||||
|
:is="tagComponent"
|
||||||
|
:class="cssClass"
|
||||||
|
:color="color"
|
||||||
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</component>
|
</component>
|
||||||
|
<Spin v-else :spinning="true" size="small" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -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>
|
||||||
|
@@ -160,6 +160,8 @@ export function useUpload(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 用来标识是否为上传 这样在watch内部不需要请求api
|
||||||
|
let isUpload = false;
|
||||||
function handleChange(info: UploadChangeParam) {
|
function handleChange(info: UploadChangeParam) {
|
||||||
/**
|
/**
|
||||||
* 移除当前文件
|
* 移除当前文件
|
||||||
@@ -199,12 +201,18 @@ export function useUpload(
|
|||||||
currentFile.fileName = transformFilename(cb);
|
currentFile.fileName = transformFilename(cb);
|
||||||
currentFile.name = transformFilename(cb);
|
currentFile.name = transformFilename(cb);
|
||||||
currentFile.thumbUrl = transformThumbUrl(cb);
|
currentFile.thumbUrl = transformThumbUrl(cb);
|
||||||
|
// 标记为上传 watch根据值做处理
|
||||||
|
isUpload = true;
|
||||||
// ossID添加 单个文件会被当做string
|
// ossID添加 单个文件会被当做string
|
||||||
if (props.maxCount === 1) {
|
if (props.maxCount === 1) {
|
||||||
bindValue.value = ossId;
|
bindValue.value = ossId;
|
||||||
} else {
|
} else {
|
||||||
(bindValue.value as string[]).push(ossId);
|
// 给默认值
|
||||||
|
if (!Array.isArray(bindValue.value)) {
|
||||||
|
bindValue.value = [];
|
||||||
|
}
|
||||||
|
// 直接使用.value无法触发useForm的更新(原生是正常的) 需要修改地址
|
||||||
|
bindValue.value = [...bindValue.value, ossId];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -314,8 +322,18 @@ export function useUpload(
|
|||||||
() => bindValue.value,
|
() => bindValue.value,
|
||||||
async (value) => {
|
async (value) => {
|
||||||
if (value.length === 0) {
|
if (value.length === 0) {
|
||||||
|
// 清空绑定值时,同时清空innerFileList,避免外部使用时还能读取到
|
||||||
|
innerFileList.value = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 上传完毕 不需要调用获取信息接口
|
||||||
|
if (isUpload) {
|
||||||
|
// 清理 使下一次状态可用
|
||||||
|
isUpload = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const resp = await ossInfo(value);
|
const resp = await ossInfo(value);
|
||||||
function transformFile(info: OssFile) {
|
function transformFile(info: OssFile) {
|
||||||
const cb = { type: 'info', response: info } as const;
|
const cb = { type: 'info', response: info } as const;
|
||||||
@@ -344,12 +362,16 @@ export function useUpload(
|
|||||||
!props.keepMissingId &&
|
!props.keepMissingId &&
|
||||||
props.maxCount !== 1
|
props.maxCount !== 1
|
||||||
) {
|
) {
|
||||||
bindValue.value = (bindValue.value as string[]).filter((ossId) =>
|
// 给默认值
|
||||||
|
if (!Array.isArray(bindValue.value)) {
|
||||||
|
bindValue.value = [];
|
||||||
|
}
|
||||||
|
bindValue.value = bindValue.value.filter((ossId) =>
|
||||||
resp.map((res) => res.ossId).includes(ossId),
|
resp.map((res) => res.ossId).includes(ossId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: props.deepWatch },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -87,13 +87,6 @@ export interface BaseUploadProps {
|
|||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
enableDragUpload?: boolean;
|
enableDragUpload?: boolean;
|
||||||
/**
|
|
||||||
* 是否开启深度监听
|
|
||||||
* 默认外部的数组地址重新改变才会触发watch 不会监听内部元素的变化
|
|
||||||
* 开启后 无论内部还是外部改变都会触发查询信息接口(包括上传后, 删除等操作都会触发)
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
deepWatch?: boolean;
|
|
||||||
/**
|
/**
|
||||||
* 当ossId查询不到文件信息时 比如被删除了
|
* 当ossId查询不到文件信息时 比如被删除了
|
||||||
* 是否保留列表对应的ossId 默认不保留
|
* 是否保留列表对应的ossId 默认不保留
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"tip": "Tip",
|
"tip": "Tip",
|
||||||
"enable": "On",
|
"enable": "On",
|
||||||
"disable": "Off"
|
"disable": "Off",
|
||||||
|
"beforeCloseTip": "You have unsaved changes. Are you sure you want to exit?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
"preview": "预览",
|
"preview": "预览",
|
||||||
"tip": "提示",
|
"tip": "提示",
|
||||||
"enable": "启用",
|
"enable": "启用",
|
||||||
"disable": "禁用"
|
"disable": "禁用",
|
||||||
|
"beforeCloseTip": "您有未保存的更改,确认要退出吗?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import type { Router } from 'vue-router';
|
import type { Router } from 'vue-router';
|
||||||
|
|
||||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
import { LOGIN_PATH } from '@vben/constants';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import { useAccessStore, useUserStore } from '@vben/stores';
|
import { useAccessStore, useUserStore } from '@vben/stores';
|
||||||
import { startProgress, stopProgress } from '@vben/utils';
|
import { startProgress, stopProgress } from '@vben/utils';
|
||||||
@@ -18,7 +18,7 @@ function setupCommonGuard(router: Router) {
|
|||||||
// 记录已经加载的页面
|
// 记录已经加载的页面
|
||||||
const loadedPaths = new Set<string>();
|
const loadedPaths = new Set<string>();
|
||||||
|
|
||||||
router.beforeEach(async (to) => {
|
router.beforeEach((to) => {
|
||||||
to.meta.loaded = loadedPaths.has(to.path);
|
to.meta.loaded = loadedPaths.has(to.path);
|
||||||
|
|
||||||
// 页面加载进度条
|
// 页面加载进度条
|
||||||
@@ -56,7 +56,7 @@ function setupAccessGuard(router: Router) {
|
|||||||
return decodeURIComponent(
|
return decodeURIComponent(
|
||||||
(to.query?.redirect as string) ||
|
(to.query?.redirect as string) ||
|
||||||
userStore.userInfo?.homePath ||
|
userStore.userInfo?.homePath ||
|
||||||
DEFAULT_HOME_PATH,
|
preferences.app.defaultHomePath,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -75,7 +75,7 @@ function setupAccessGuard(router: Router) {
|
|||||||
path: LOGIN_PATH,
|
path: LOGIN_PATH,
|
||||||
// 如不需要,直接删除 query
|
// 如不需要,直接删除 query
|
||||||
query:
|
query:
|
||||||
to.fullPath === DEFAULT_HOME_PATH
|
to.fullPath === preferences.app.defaultHomePath
|
||||||
? {}
|
? {}
|
||||||
: { redirect: encodeURIComponent(to.fullPath) },
|
: { redirect: encodeURIComponent(to.fullPath) },
|
||||||
// 携带当前跳转的页面,登录后重新跳转该页面
|
// 携带当前跳转的页面,登录后重新跳转该页面
|
||||||
@@ -108,8 +108,8 @@ function setupAccessGuard(router: Router) {
|
|||||||
accessStore.setAccessRoutes(accessibleRoutes);
|
accessStore.setAccessRoutes(accessibleRoutes);
|
||||||
accessStore.setIsAccessChecked(true);
|
accessStore.setIsAccessChecked(true);
|
||||||
const redirectPath = (from.query.redirect ??
|
const redirectPath = (from.query.redirect ??
|
||||||
(to.path === DEFAULT_HOME_PATH
|
(to.path === preferences.app.defaultHomePath
|
||||||
? userInfo.homePath || DEFAULT_HOME_PATH
|
? userInfo.homePath || preferences.app.defaultHomePath
|
||||||
: to.fullPath)) as string;
|
: to.fullPath)) as string;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
import type { RouteRecordRaw } from 'vue-router';
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
import { LOGIN_PATH } from '@vben/constants';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
import { AuthPageLayout, BasicLayout } from '#/layouts';
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import Login from '#/views/_core/authentication/login.vue';
|
|
||||||
|
|
||||||
|
const BasicLayout = () => import('#/layouts/basic.vue');
|
||||||
|
const AuthPageLayout = () => import('#/layouts/auth.vue');
|
||||||
/** 全局404页面 */
|
/** 全局404页面 */
|
||||||
const fallbackNotFoundRoute: RouteRecordRaw = {
|
const fallbackNotFoundRoute: RouteRecordRaw = {
|
||||||
component: () => import('#/views/_core/fallback/not-found.vue'),
|
component: () => import('#/views/_core/fallback/not-found.vue'),
|
||||||
@@ -34,7 +35,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
name: 'Root',
|
name: 'Root',
|
||||||
path: '/',
|
path: '/',
|
||||||
redirect: DEFAULT_HOME_PATH,
|
redirect: preferences.app.defaultHomePath,
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -58,7 +59,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
path: 'login',
|
path: 'login',
|
||||||
component: Login,
|
component: () => import('#/views/_core/authentication/login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.auth.login'),
|
title: $t('page.auth.login'),
|
||||||
},
|
},
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -4,7 +4,8 @@ import type { UserInfo } from '@vben/types';
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
import { LOGIN_PATH } from '@vben/constants';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
|
import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
|
||||||
|
|
||||||
import { notification } from 'ant-design-vue';
|
import { notification } from 'ant-design-vue';
|
||||||
@@ -55,7 +56,9 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
if (accessStore.loginExpired) {
|
if (accessStore.loginExpired) {
|
||||||
accessStore.setLoginExpired(false);
|
accessStore.setLoginExpired(false);
|
||||||
} else {
|
} else {
|
||||||
onSuccess ? await onSuccess?.() : await router.push(DEFAULT_HOME_PATH);
|
onSuccess
|
||||||
|
? await onSuccess?.()
|
||||||
|
: await router.push(preferences.app.defaultHomePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userInfo?.realName) {
|
if (userInfo?.realName) {
|
||||||
|
@@ -59,6 +59,7 @@ export const useDictStore = defineStore('app-dict', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetCache() {
|
function resetCache() {
|
||||||
|
dictRequestCache.clear();
|
||||||
dictOptionsMap.clear();
|
dictOptionsMap.clear();
|
||||||
/**
|
/**
|
||||||
* 不需要清空dictRequestCache 每次请求成功/失败都清空key
|
* 不需要清空dictRequestCache 每次请求成功/失败都清空key
|
||||||
|
@@ -29,43 +29,52 @@ interface BeforeCloseDiffProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated 注意为实验性功能 可能有api变动/被移除
|
* 用于Drawer/Modal使用 判断表单是否有变动来决定是否弹窗提示
|
||||||
* @param props props
|
* @param props props
|
||||||
* @returns hook
|
* @returns hook
|
||||||
*
|
|
||||||
* 待解决问题: 网速慢情况直接关闭 会导致数据不一致问题
|
|
||||||
* 但是使用api.lock会导致在报错情况无法关闭(因为目前代码没有finally)
|
|
||||||
*/
|
*/
|
||||||
export function useBeforeCloseDiff(props: BeforeCloseDiffProps) {
|
export function useBeforeCloseDiff(props: BeforeCloseDiffProps) {
|
||||||
const { initializedGetter, currentGetter, compare } = props;
|
const { initializedGetter, currentGetter, compare } = props;
|
||||||
|
/**
|
||||||
|
* 记录初始值 json
|
||||||
|
*/
|
||||||
const initialized = ref<string>('');
|
const initialized = ref<string>('');
|
||||||
|
/**
|
||||||
|
* 是否已经初始化了 通过这个值判断是否需要进行对比 为false直接关闭 不弹窗
|
||||||
|
*/
|
||||||
const isInitialized = ref(false);
|
const isInitialized = ref(false);
|
||||||
const isSubmitted = ref(false);
|
|
||||||
|
|
||||||
async function updateInitialized(data?: string) {
|
/**
|
||||||
|
* 标记是否已经完成初始化 后续需要进行对比
|
||||||
|
* @param data 自定义初始化数据 可选
|
||||||
|
*/
|
||||||
|
async function markInitialized(data?: string) {
|
||||||
initialized.value = data || (await initializedGetter());
|
initialized.value = data || (await initializedGetter());
|
||||||
isInitialized.value = true;
|
isInitialized.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSubmitted() {
|
/**
|
||||||
isSubmitted.value = true;
|
* 重置初始化状态 需要在closed前调用 或者打开窗口时
|
||||||
|
*/
|
||||||
|
function resetInitialized() {
|
||||||
|
initialized.value = '';
|
||||||
|
isInitialized.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供给useVbenForm/useVbenDrawer使用
|
||||||
|
* @returns 是否允许关闭
|
||||||
|
*/
|
||||||
async function onBeforeClose(): Promise<boolean> {
|
async function onBeforeClose(): Promise<boolean> {
|
||||||
// 如果还未初始化,直接允许关闭
|
// 如果还未初始化,直接允许关闭
|
||||||
if (!isInitialized.value) {
|
if (!isInitialized.value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// 如果已经提交过,直接允许关闭
|
|
||||||
if (isSubmitted.value) {
|
|
||||||
// 重置状态
|
|
||||||
isSubmitted.value = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 获取当前表单数据
|
||||||
const current = await currentGetter();
|
const current = await currentGetter();
|
||||||
|
// 自定义比较的情况
|
||||||
if (isFunction(compare) && compare(initialized.value, current)) {
|
if (isFunction(compare) && compare(initialized.value, current)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -79,7 +88,7 @@ export function useBeforeCloseDiff(props: BeforeCloseDiffProps) {
|
|||||||
return new Promise<boolean>((resolve) => {
|
return new Promise<boolean>((resolve) => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: $t('pages.common.tip'),
|
title: $t('pages.common.tip'),
|
||||||
content: $t('您有未保存的更改,确认要退出吗?'),
|
content: $t('pages.common.beforeCloseTip'),
|
||||||
centered: true,
|
centered: true,
|
||||||
okButtonProps: { danger: true },
|
okButtonProps: { danger: true },
|
||||||
cancelText: $t('common.cancel'),
|
cancelText: $t('common.cancel'),
|
||||||
@@ -99,8 +108,8 @@ export function useBeforeCloseDiff(props: BeforeCloseDiffProps) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
onBeforeClose,
|
onBeforeClose,
|
||||||
updateInitialized,
|
markInitialized,
|
||||||
setSubmitted,
|
resetInitialized,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,7 +18,7 @@ import { useAuthStore } from '#/store';
|
|||||||
defineOptions({ name: 'CodeLogin' });
|
defineOptions({ name: 'CodeLogin' });
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const CODE_LENGTH = 6;
|
const CODE_LENGTH = 4;
|
||||||
|
|
||||||
const tenantInfo = ref<TenantResp>({
|
const tenantInfo = ref<TenantResp>({
|
||||||
tenantEnabled: false,
|
tenantEnabled: false,
|
||||||
@@ -85,8 +85,8 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
: $t('authentication.sendCode');
|
: $t('authentication.sendCode');
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
// 验证码长度 在这设置
|
// 验证码长度
|
||||||
codeLength: 4,
|
codeLength: CODE_LENGTH,
|
||||||
placeholder: $t('authentication.code'),
|
placeholder: $t('authentication.code'),
|
||||||
handleSendCode: async () => {
|
handleSendCode: async () => {
|
||||||
const { valid, value } = await form.validateField('phoneNumber');
|
const { valid, value } = await form.validateField('phoneNumber');
|
||||||
|
@@ -4,7 +4,8 @@ import type { AuthApi } from '#/api';
|
|||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { DEFAULT_HOME_PATH, DEFAULT_TENANT_ID } from '@vben/constants';
|
import { DEFAULT_TENANT_ID } from '@vben/constants';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
import { useAccessStore } from '@vben/stores';
|
import { useAccessStore } from '@vben/stores';
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
@@ -70,7 +71,7 @@ onMounted(async () => {
|
|||||||
// 500 你还没有绑定第三方账号,绑定后才可以登录!
|
// 500 你还没有绑定第三方账号,绑定后才可以登录!
|
||||||
} finally {
|
} finally {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
router.push(DEFAULT_HOME_PATH);
|
router.push(preferences.app.defaultHomePath);
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@@ -7,6 +7,7 @@ import { cloneDeep } from '@vben/utils';
|
|||||||
|
|
||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import { clientAdd, clientInfo, clientUpdate } from '#/api/system/client';
|
import { clientAdd, clientInfo, clientUpdate } from '#/api/system/client';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
import SecretInput from './secret-input.vue';
|
import SecretInput from './secret-input.vue';
|
||||||
@@ -55,6 +56,13 @@ function setupForm(update: boolean) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
|
{
|
||||||
|
initializedGetter: defaultFormValueGetter(formApi),
|
||||||
|
currentGetter: defaultFormValueGetter(formApi),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// 提取生成状态字段Schema的函数
|
// 提取生成状态字段Schema的函数
|
||||||
const getStatusSchema = (disabled: boolean) => [
|
const getStatusSchema = (disabled: boolean) => [
|
||||||
{
|
{
|
||||||
@@ -64,13 +72,15 @@ const getStatusSchema = (disabled: boolean) => [
|
|||||||
];
|
];
|
||||||
|
|
||||||
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) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
drawerApi.drawerLoading(true);
|
drawerApi.drawerLoading(true);
|
||||||
|
|
||||||
const { id } = drawerApi.getData() as { id?: number | string };
|
const { id } = drawerApi.getData() as { id?: number | string };
|
||||||
isUpdate.value = !!id;
|
isUpdate.value = !!id;
|
||||||
// 初始化
|
// 初始化
|
||||||
@@ -84,36 +94,39 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
// 新增模式: 确保状态字段可用
|
// 新增模式: 确保状态字段可用
|
||||||
formApi.updateSchema(getStatusSchema(false));
|
formApi.updateSchema(getStatusSchema(false));
|
||||||
}
|
}
|
||||||
|
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 ? clientUpdate(data) : clientAdd(data));
|
await (isUpdate.value ? clientUpdate(data) : clientAdd(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>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
|
<BasicDrawer :title="title" class="w-[600px]">
|
||||||
<BasicForm>
|
<BasicForm>
|
||||||
<template #clientSecret="slotProps">
|
<template #clientSecret="slotProps">
|
||||||
<SecretInput v-bind="slotProps" :disabled="isUpdate" />
|
<SecretInput v-bind="slotProps" :disabled="isUpdate" />
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -26,10 +26,12 @@ const [BasicForm, formApi] = useVbenForm({
|
|||||||
showDefaultActions: false,
|
showDefaultActions: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { onBeforeClose, updateInitialized, setSubmitted } = useBeforeCloseDiff({
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
initializedGetter: defaultFormValueGetter(formApi),
|
{
|
||||||
currentGetter: defaultFormValueGetter(formApi),
|
initializedGetter: defaultFormValueGetter(formApi),
|
||||||
});
|
currentGetter: defaultFormValueGetter(formApi),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [BasicModal, modalApi] = useVbenModal({
|
const [BasicModal, modalApi] = useVbenModal({
|
||||||
fullscreenButton: false,
|
fullscreenButton: false,
|
||||||
@@ -40,22 +42,18 @@ const [BasicModal, modalApi] = useVbenModal({
|
|||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
modalApi.modalLoading(true);
|
||||||
modalApi.lock(true);
|
|
||||||
|
|
||||||
const { id } = modalApi.getData() as { id?: number | string };
|
const { id } = modalApi.getData() as { id?: number | string };
|
||||||
isUpdate.value = !!id;
|
isUpdate.value = !!id;
|
||||||
|
|
||||||
if (isUpdate.value && id) {
|
if (isUpdate.value && id) {
|
||||||
const record = await configInfo(id);
|
const record = await configInfo(id);
|
||||||
await formApi.setValues(record);
|
await formApi.setValues(record);
|
||||||
}
|
|
||||||
await updateInitialized();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
} finally {
|
|
||||||
modalApi.lock(false);
|
|
||||||
}
|
}
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
|
modalApi.modalLoading(false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,7 +66,7 @@ async function handleConfirm() {
|
|||||||
}
|
}
|
||||||
const data = cloneDeep(await formApi.getValues());
|
const data = cloneDeep(await formApi.getValues());
|
||||||
await (isUpdate.value ? configUpdate(data) : configAdd(data));
|
await (isUpdate.value ? configUpdate(data) : configAdd(data));
|
||||||
setSubmitted();
|
resetInitialized();
|
||||||
emit('reload');
|
emit('reload');
|
||||||
modalApi.close();
|
modalApi.close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -80,6 +78,7 @@ async function handleConfirm() {
|
|||||||
|
|
||||||
async function handleClosed() {
|
async function handleClosed() {
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
|
resetInitialized();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -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 }) => {
|
||||||
|
@@ -16,6 +16,7 @@ import {
|
|||||||
deptUpdate,
|
deptUpdate,
|
||||||
} from '#/api/system/dept';
|
} from '#/api/system/dept';
|
||||||
import { listUserByDeptId } from '#/api/system/user';
|
import { listUserByDeptId } from '#/api/system/user';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
|
|
||||||
@@ -107,8 +108,16 @@ async function setLeaderOptions() {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
@@ -130,6 +139,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
await (update && id ? initDeptUsers(id) : setLeaderOptions());
|
await (update && id ? initDeptUsers(id) : setLeaderOptions());
|
||||||
/** 部门选择 下拉框 */
|
/** 部门选择 下拉框 */
|
||||||
await initDeptSelect(id);
|
await initDeptSelect(id);
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
drawerApi.drawerLoading(false);
|
drawerApi.drawerLoading(false);
|
||||||
},
|
},
|
||||||
@@ -137,30 +147,31 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
|
|
||||||
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 ? deptUpdate(data) : deptAdd(data));
|
await (isUpdate.value ? deptUpdate(data) : deptAdd(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>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
|
<BasicDrawer :title="title" class="w-[600px]">
|
||||||
<BasicForm />
|
<BasicForm />
|
||||||
</BasicDrawer>
|
</BasicDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -12,6 +12,7 @@ import {
|
|||||||
dictDetailInfo,
|
dictDetailInfo,
|
||||||
} from '#/api/system/dict/dict-data';
|
} from '#/api/system/dict/dict-data';
|
||||||
import { tagTypes } from '#/components/dict';
|
import { tagTypes } from '#/components/dict';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
import TagStylePicker from './tag-style-picker.vue';
|
import TagStylePicker from './tag-style-picker.vue';
|
||||||
@@ -57,8 +58,16 @@ function setupSelectType(listClass: string) {
|
|||||||
selectType.value = isDefault ? 'default' : 'custom';
|
selectType.value = isDefault ? 'default' : 'custom';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
@@ -68,13 +77,14 @@ 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);
|
||||||
setupSelectType(record.listClass);
|
setupSelectType(record.listClass);
|
||||||
await formApi.setValues(record);
|
await formApi.setValues(record);
|
||||||
}
|
}
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
drawerApi.drawerLoading(false);
|
drawerApi.drawerLoading(false);
|
||||||
},
|
},
|
||||||
@@ -82,7 +92,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
|
|
||||||
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;
|
||||||
@@ -93,19 +103,20 @@ async function handleConfirm() {
|
|||||||
data.listClass = '';
|
data.listClass = '';
|
||||||
}
|
}
|
||||||
await (isUpdate.value ? dictDataUpdate(data) : dictDataAdd(data));
|
await (isUpdate.value ? dictDataUpdate(data) : dictDataAdd(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();
|
||||||
selectType.value = 'default';
|
selectType.value = 'default';
|
||||||
|
resetInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,7 +128,7 @@ async function handleDeSelect() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
|
<BasicDrawer :title="title" class="w-[600px]">
|
||||||
<BasicForm>
|
<BasicForm>
|
||||||
<template #listClass="slotProps">
|
<template #listClass="slotProps">
|
||||||
<TagStylePicker
|
<TagStylePicker
|
||||||
|
@@ -1,14 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { RadioChangeEvent } from 'ant-design-vue';
|
import type { RadioChangeEvent } from 'ant-design-vue';
|
||||||
|
|
||||||
import type { PropType } from 'vue';
|
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
import { Input, RadioGroup, Select } from 'ant-design-vue';
|
import { usePreferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import { RadioGroup, Select } from 'ant-design-vue';
|
||||||
|
import { ColorPicker } from 'vue3-colorpicker';
|
||||||
|
|
||||||
import { tagSelectOptions } from '#/components/dict';
|
import { tagSelectOptions } from '#/components/dict';
|
||||||
|
|
||||||
|
import 'vue3-colorpicker/style.css';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 需要禁止透传
|
* 需要禁止透传
|
||||||
* 不禁止会有奇怪的bug 会绑定到selectType上
|
* 不禁止会有奇怪的bug 会绑定到selectType上
|
||||||
@@ -32,23 +35,26 @@ const computedOptions = computed(
|
|||||||
|
|
||||||
type SelectType = (typeof options)[number]['value'];
|
type SelectType = (typeof options)[number]['value'];
|
||||||
|
|
||||||
const selectType = defineModel('selectType', {
|
const selectType = defineModel<SelectType>('selectType', {
|
||||||
default: 'default',
|
default: 'default',
|
||||||
type: String as PropType<SelectType>,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* color必须为hex颜色或者undefined
|
* color必须为hex颜色或者undefined
|
||||||
*/
|
*/
|
||||||
const color = defineModel('value', {
|
const color = defineModel<string | undefined>('value', {
|
||||||
default: undefined,
|
default: undefined,
|
||||||
type: String as PropType<string | undefined>,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleSelectTypeChange(e: RadioChangeEvent) {
|
function handleSelectTypeChange(e: RadioChangeEvent) {
|
||||||
// 必须给默认hex颜色 不能为空字符串
|
// 必须给默认hex颜色 不能为空字符串
|
||||||
color.value = e.target.value === 'custom' ? '#000000' : undefined;
|
color.value = e.target.value === 'custom' ? '#1677ff' : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { isDark } = usePreferences();
|
||||||
|
const theme = computed(() => {
|
||||||
|
return isDark.value ? 'black' : 'white';
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -69,15 +75,12 @@ function handleSelectTypeChange(e: RadioChangeEvent) {
|
|||||||
placeholder="请选择标签样式"
|
placeholder="请选择标签样式"
|
||||||
@deselect="$emit('deselect')"
|
@deselect="$emit('deselect')"
|
||||||
/>
|
/>
|
||||||
<Input
|
<ColorPicker
|
||||||
v-if="selectType === 'custom'"
|
v-if="selectType === 'custom'"
|
||||||
v-model:value="color"
|
disable-alpha
|
||||||
class="flex-1"
|
format="hex"
|
||||||
disabled
|
v-model:pure-color="color"
|
||||||
>
|
:theme="theme"
|
||||||
<template #addonAfter>
|
/>
|
||||||
<input v-model="color" class="rounded-lg" type="color" />
|
|
||||||
</template>
|
|
||||||
</Input>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -71,7 +72,6 @@ export const modalSchema: FormSchemaGetter = () => [
|
|||||||
{
|
{
|
||||||
component: 'Textarea',
|
component: 'Textarea',
|
||||||
fieldName: 'remark',
|
fieldName: 'remark',
|
||||||
formItemClass: 'items-start',
|
|
||||||
label: '备注',
|
label: '备注',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@@ -11,6 +11,7 @@ import {
|
|||||||
dictTypeInfo,
|
dictTypeInfo,
|
||||||
dictTypeUpdate,
|
dictTypeUpdate,
|
||||||
} from '#/api/system/dict/dict-type';
|
} from '#/api/system/dict/dict-type';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { modalSchema } from './data';
|
import { modalSchema } from './data';
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ const title = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [BasicForm, formApi] = useVbenForm({
|
const [BasicForm, formApi] = useVbenForm({
|
||||||
|
layout: 'vertical',
|
||||||
commonConfig: {
|
commonConfig: {
|
||||||
labelWidth: 100,
|
labelWidth: 100,
|
||||||
},
|
},
|
||||||
@@ -29,51 +31,63 @@ const [BasicForm, formApi] = useVbenForm({
|
|||||||
showDefaultActions: false,
|
showDefaultActions: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
|
{
|
||||||
|
initializedGetter: defaultFormValueGetter(formApi),
|
||||||
|
currentGetter: defaultFormValueGetter(formApi),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [BasicModal, modalApi] = useVbenModal({
|
const [BasicModal, modalApi] = useVbenModal({
|
||||||
fullscreenButton: false,
|
fullscreenButton: false,
|
||||||
onCancel: handleCancel,
|
onBeforeClose,
|
||||||
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
onOpenChange: async (isOpen) => {
|
onOpenChange: async (isOpen) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
modalApi.modalLoading(true);
|
modalApi.modalLoading(true);
|
||||||
|
|
||||||
const { id } = modalApi.getData() as { id?: number | string };
|
const { id } = modalApi.getData() as { id?: number | string };
|
||||||
isUpdate.value = !!id;
|
isUpdate.value = !!id;
|
||||||
if (isUpdate.value && id) {
|
if (isUpdate.value && id) {
|
||||||
const record = await dictTypeInfo(id);
|
const record = await dictTypeInfo(id);
|
||||||
await formApi.setValues(record);
|
await formApi.setValues(record);
|
||||||
}
|
}
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
modalApi.modalLoading(false);
|
modalApi.modalLoading(false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handleConfirm() {
|
async function handleConfirm() {
|
||||||
try {
|
try {
|
||||||
modalApi.modalLoading(true);
|
modalApi.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 ? dictTypeUpdate(data) : dictTypeAdd(data));
|
await (isUpdate.value ? dictTypeUpdate(data) : dictTypeAdd(data));
|
||||||
|
resetInitialized();
|
||||||
emit('reload');
|
emit('reload');
|
||||||
await handleCancel();
|
modalApi.close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.modalLoading(false);
|
modalApi.lock(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCancel() {
|
async function handleClosed() {
|
||||||
modalApi.close();
|
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
|
resetInitialized();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicModal :close-on-click-modal="false" :title="title">
|
<BasicModal :title="title">
|
||||||
<BasicForm />
|
<BasicForm />
|
||||||
</BasicModal>
|
</BasicModal>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -12,6 +12,7 @@ import {
|
|||||||
|
|
||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import { menuAdd, menuInfo, menuList, menuUpdate } from '#/api/system/menu';
|
import { menuAdd, menuInfo, menuList, menuUpdate } from '#/api/system/menu';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
|
|
||||||
@@ -88,14 +89,23 @@ async function setupMenuSelect() {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
drawerApi.drawerLoading(true);
|
drawerApi.drawerLoading(true);
|
||||||
|
|
||||||
const { id, update } = drawerApi.getData() as ModalProps;
|
const { id, update } = drawerApi.getData() as ModalProps;
|
||||||
isUpdate.value = update;
|
isUpdate.value = update;
|
||||||
|
|
||||||
@@ -108,36 +118,39 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
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 ? menuUpdate(data) : menuAdd(data));
|
await (isUpdate.value ? menuUpdate(data) : menuAdd(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>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
|
<BasicDrawer :title="title" class="w-[600px]">
|
||||||
<BasicForm />
|
<BasicForm />
|
||||||
</BasicDrawer>
|
</BasicDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@ import { pick } from 'lodash-es';
|
|||||||
import { noticeAdd, noticeInfo, noticeUpdate } from '#/api/system/notice';
|
import { noticeAdd, noticeInfo, noticeUpdate } from '#/api/system/notice';
|
||||||
import { Tinymce } from '#/components/tinymce';
|
import { Tinymce } from '#/components/tinymce';
|
||||||
import { getDictOptions } from '#/utils/dict';
|
import { getDictOptions } from '#/utils/dict';
|
||||||
|
import { useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
const emit = defineEmits<{ reload: [] }>();
|
const emit = defineEmits<{ reload: [] }>();
|
||||||
|
|
||||||
@@ -74,17 +75,29 @@ const { validate, validateInfos, resetFields } = Form.useForm(
|
|||||||
formRules,
|
formRules,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function customFormValueGetter() {
|
||||||
|
return JSON.stringify(formData.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
|
{
|
||||||
|
initializedGetter: customFormValueGetter,
|
||||||
|
currentGetter: customFormValueGetter,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [BasicModal, modalApi] = useVbenModal({
|
const [BasicModal, modalApi] = useVbenModal({
|
||||||
class: 'w-[800px]',
|
class: 'w-[800px]',
|
||||||
fullscreenButton: true,
|
fullscreenButton: true,
|
||||||
closeOnClickModal: false,
|
onBeforeClose,
|
||||||
onClosed: handleCancel,
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
onOpenChange: async (isOpen) => {
|
onOpenChange: async (isOpen) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
modalApi.modalLoading(true);
|
modalApi.modalLoading(true);
|
||||||
|
|
||||||
const { id } = modalApi.getData() as { id?: number | string };
|
const { id } = modalApi.getData() as { id?: number | string };
|
||||||
isUpdate.value = !!id;
|
isUpdate.value = !!id;
|
||||||
if (isUpdate.value && id) {
|
if (isUpdate.value && id) {
|
||||||
@@ -93,30 +106,33 @@ const [BasicModal, modalApi] = useVbenModal({
|
|||||||
const filterRecord = pick(record, Object.keys(defaultValues));
|
const filterRecord = pick(record, Object.keys(defaultValues));
|
||||||
formData.value = filterRecord;
|
formData.value = filterRecord;
|
||||||
}
|
}
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
modalApi.modalLoading(false);
|
modalApi.modalLoading(false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handleConfirm() {
|
async function handleConfirm() {
|
||||||
try {
|
try {
|
||||||
modalApi.modalLoading(true);
|
modalApi.lock(true);
|
||||||
await validate();
|
await validate();
|
||||||
// 可能会做数据处理 使用cloneDeep深拷贝
|
// 可能会做数据处理 使用cloneDeep深拷贝
|
||||||
const data = cloneDeep(formData.value);
|
const data = cloneDeep(formData.value);
|
||||||
await (isUpdate.value ? noticeUpdate(data) : noticeAdd(data));
|
await (isUpdate.value ? noticeUpdate(data) : noticeAdd(data));
|
||||||
|
resetInitialized();
|
||||||
emit('reload');
|
emit('reload');
|
||||||
await handleCancel();
|
modalApi.close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.modalLoading(false);
|
modalApi.lock(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCancel() {
|
async function handleClosed() {
|
||||||
modalApi.close();
|
|
||||||
formData.value = defaultValues;
|
formData.value = defaultValues;
|
||||||
resetFields();
|
resetFields();
|
||||||
|
resetInitialized();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -13,6 +13,7 @@ import {
|
|||||||
ossConfigInfo,
|
ossConfigInfo,
|
||||||
ossConfigUpdate,
|
ossConfigUpdate,
|
||||||
} from '#/api/system/oss-config';
|
} from '#/api/system/oss-config';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
|
|
||||||
@@ -33,27 +34,38 @@ const [BasicForm, formApi] = useVbenForm({
|
|||||||
wrapperClass: 'grid-cols-3',
|
wrapperClass: 'grid-cols-3',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
drawerApi.drawerLoading(true);
|
drawerApi.drawerLoading(true);
|
||||||
|
|
||||||
const { id } = drawerApi.getData() as { id?: number | string };
|
const { id } = drawerApi.getData() as { id?: number | string };
|
||||||
isUpdate.value = !!id;
|
isUpdate.value = !!id;
|
||||||
if (isUpdate.value && id) {
|
if (isUpdate.value && id) {
|
||||||
const record = await ossConfigInfo(id);
|
const record = await ossConfigInfo(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);
|
||||||
/**
|
/**
|
||||||
* 这里解构出来的values只能获取到自定义校验参数的值
|
* 这里解构出来的values只能获取到自定义校验参数的值
|
||||||
* 需要自行调用formApi.getValues()获取表单值
|
* 需要自行调用formApi.getValues()获取表单值
|
||||||
@@ -64,23 +76,24 @@ async function handleConfirm() {
|
|||||||
}
|
}
|
||||||
const data = cloneDeep(await formApi.getValues());
|
const data = cloneDeep(await formApi.getValues());
|
||||||
await (isUpdate.value ? ossConfigUpdate(data) : ossConfigAdd(data));
|
await (isUpdate.value ? ossConfigUpdate(data) : ossConfigAdd(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>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[650px]">
|
<BasicDrawer :title="title" class="w-[650px]">
|
||||||
<BasicForm>
|
<BasicForm>
|
||||||
<template #tip>
|
<template #tip>
|
||||||
<div class="ml-7 w-full">
|
<div class="ml-7 w-full">
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -83,6 +83,9 @@ const gridOptions: VxeGridProps = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
headerCellConfig: {
|
||||||
|
height: 44,
|
||||||
|
},
|
||||||
cellConfig: {
|
cellConfig: {
|
||||||
height: 65,
|
height: 65,
|
||||||
},
|
},
|
||||||
|
@@ -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,36 +76,38 @@ 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>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
|
<BasicDrawer :title="title" class="w-[600px]">
|
||||||
<BasicForm />
|
<BasicForm />
|
||||||
</BasicDrawer>
|
</BasicDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@@ -17,6 +17,7 @@ const emit = defineEmits<{ reload: [] }>();
|
|||||||
const [BasicDrawer, drawerApi] = useVbenDrawer({
|
const [BasicDrawer, drawerApi] = useVbenDrawer({
|
||||||
onConfirm: handleSubmit,
|
onConfirm: handleSubmit,
|
||||||
onCancel: handleReset,
|
onCancel: handleReset,
|
||||||
|
destroyOnClose: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@@ -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>
|
||||||
|
@@ -9,6 +9,7 @@ import { cloneDeep } from '@vben/utils';
|
|||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import { roleDataScope, roleDeptTree, roleInfo } from '#/api/system/role';
|
import { roleDataScope, roleDeptTree, roleInfo } from '#/api/system/role';
|
||||||
import { TreeSelectPanel } from '#/components/tree';
|
import { TreeSelectPanel } from '#/components/tree';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { authModalSchemas } from './data';
|
import { authModalSchemas } from './data';
|
||||||
|
|
||||||
@@ -33,9 +34,25 @@ async function setupDeptTree(id: number | string) {
|
|||||||
deptTree.value = resp.depts;
|
deptTree.value = resp.depts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function customFormValueGetter() {
|
||||||
|
const v = await defaultFormValueGetter(formApi)();
|
||||||
|
// 获取勾选信息
|
||||||
|
const menuIds = deptSelectRef.value?.[0]?.getCheckedKeys() ?? [];
|
||||||
|
const mixStr = v + menuIds.join(',');
|
||||||
|
return mixStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
|
{
|
||||||
|
initializedGetter: customFormValueGetter,
|
||||||
|
currentGetter: customFormValueGetter,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [BasicModal, modalApi] = useVbenModal({
|
const [BasicModal, modalApi] = useVbenModal({
|
||||||
fullscreenButton: false,
|
fullscreenButton: false,
|
||||||
onCancel: handleCancel,
|
onBeforeClose,
|
||||||
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
onOpenChange: async (isOpen) => {
|
onOpenChange: async (isOpen) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
@@ -48,6 +65,7 @@ const [BasicModal, modalApi] = useVbenModal({
|
|||||||
setupDeptTree(id);
|
setupDeptTree(id);
|
||||||
const record = await roleInfo(id);
|
const record = await roleInfo(id);
|
||||||
await formApi.setValues(record);
|
await formApi.setValues(record);
|
||||||
|
markInitialized();
|
||||||
|
|
||||||
modalApi.modalLoading(false);
|
modalApi.modalLoading(false);
|
||||||
},
|
},
|
||||||
@@ -60,7 +78,7 @@ const deptSelectRef = ref();
|
|||||||
|
|
||||||
async function handleConfirm() {
|
async function handleConfirm() {
|
||||||
try {
|
try {
|
||||||
modalApi.modalLoading(true);
|
modalApi.lock(true);
|
||||||
const { valid } = await formApi.validate();
|
const { valid } = await formApi.validate();
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return;
|
return;
|
||||||
@@ -75,18 +93,19 @@ async function handleConfirm() {
|
|||||||
data.deptIds = [];
|
data.deptIds = [];
|
||||||
}
|
}
|
||||||
await roleDataScope(data);
|
await roleDataScope(data);
|
||||||
|
resetInitialized();
|
||||||
emit('reload');
|
emit('reload');
|
||||||
await handleCancel();
|
modalApi.close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.modalLoading(false);
|
modalApi.lock(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCancel() {
|
async function handleClosed() {
|
||||||
modalApi.close();
|
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
|
resetInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,11 +118,7 @@ function handleCheckStrictlyChange(value: boolean) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicModal
|
<BasicModal class="min-h-[600px] w-[550px]" title="分配权限">
|
||||||
:close-on-click-modal="false"
|
|
||||||
class="min-h-[600px] w-[550px]"
|
|
||||||
title="分配权限"
|
|
||||||
>
|
|
||||||
<BasicForm>
|
<BasicForm>
|
||||||
<template #deptIds="slotProps">
|
<template #deptIds="slotProps">
|
||||||
<TreeSelectPanel
|
<TreeSelectPanel
|
||||||
|
@@ -1,3 +1,6 @@
|
|||||||
|
<!--
|
||||||
|
TODO: 这个页面要优化逻辑
|
||||||
|
-->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MenuOption } from '#/api/system/menu/model';
|
import type { MenuOption } from '#/api/system/menu/model';
|
||||||
|
|
||||||
@@ -11,6 +14,7 @@ import { useVbenForm } from '#/adapter/form';
|
|||||||
import { menuTreeSelect, roleMenuTreeSelect } from '#/api/system/menu';
|
import { menuTreeSelect, roleMenuTreeSelect } from '#/api/system/menu';
|
||||||
import { roleAdd, roleInfo, roleUpdate } from '#/api/system/role';
|
import { roleAdd, roleInfo, roleUpdate } from '#/api/system/role';
|
||||||
import { MenuSelectTable } from '#/components/tree';
|
import { MenuSelectTable } from '#/components/tree';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
|
|
||||||
@@ -62,14 +66,32 @@ async function setupMenuTree(id?: number | string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function customFormValueGetter() {
|
||||||
|
const v = await defaultFormValueGetter(formApi)();
|
||||||
|
// 获取勾选信息
|
||||||
|
const menuIds = menuSelectRef.value?.getCheckedKeys?.() ?? [];
|
||||||
|
const mixStr = v + menuIds.join(',');
|
||||||
|
return mixStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
|
{
|
||||||
|
initializedGetter: customFormValueGetter,
|
||||||
|
currentGetter: customFormValueGetter,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [BasicDrawer, drawerApi] = useVbenDrawer({
|
const [BasicDrawer, drawerApi] = useVbenDrawer({
|
||||||
onCancel: handleCancel,
|
onBeforeClose,
|
||||||
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
|
destroyOnClose: true,
|
||||||
async onOpenChange(isOpen) {
|
async onOpenChange(isOpen) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
drawerApi.drawerLoading(true);
|
drawerApi.drawerLoading(true);
|
||||||
|
|
||||||
const { id } = drawerApi.getData() as { id?: number | string };
|
const { id } = drawerApi.getData() as { id?: number | string };
|
||||||
isUpdate.value = !!id;
|
isUpdate.value = !!id;
|
||||||
|
|
||||||
@@ -79,6 +101,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
}
|
}
|
||||||
// init菜单 注意顺序要放在赋值record之后 内部watch会依赖record
|
// init菜单 注意顺序要放在赋值record之后 内部watch会依赖record
|
||||||
await setupMenuTree(id);
|
await setupMenuTree(id);
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
drawerApi.drawerLoading(false);
|
drawerApi.drawerLoading(false);
|
||||||
},
|
},
|
||||||
@@ -87,7 +110,8 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
const menuSelectRef = ref<InstanceType<typeof MenuSelectTable>>();
|
const menuSelectRef = ref<InstanceType<typeof MenuSelectTable>>();
|
||||||
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;
|
||||||
@@ -99,17 +123,18 @@ async function handleConfirm() {
|
|||||||
data.menuIds = menuIds;
|
data.menuIds = menuIds;
|
||||||
await (isUpdate.value ? roleUpdate(data) : roleAdd(data));
|
await (isUpdate.value ? roleUpdate(data) : roleAdd(data));
|
||||||
emit('reload');
|
emit('reload');
|
||||||
await handleCancel();
|
resetInitialized();
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,7 +147,7 @@ function handleMenuCheckStrictlyChange(value: boolean) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[800px]">
|
<BasicDrawer :title="title" class="w-[800px]">
|
||||||
<BasicForm>
|
<BasicForm>
|
||||||
<template #menuIds="slotProps">
|
<template #menuIds="slotProps">
|
||||||
<div class="h-[600px] w-full">
|
<div class="h-[600px] w-full">
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@ import { useVbenForm } from '#/adapter/form';
|
|||||||
import { tenantAdd, tenantInfo, tenantUpdate } from '#/api/system/tenant';
|
import { tenantAdd, tenantInfo, tenantUpdate } from '#/api/system/tenant';
|
||||||
import { packageSelectList } from '#/api/system/tenant-package';
|
import { packageSelectList } from '#/api/system/tenant-package';
|
||||||
import { useTenantStore } from '#/store/tenant';
|
import { useTenantStore } from '#/store/tenant';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
|
|
||||||
@@ -51,22 +52,33 @@ async function setupPackageSelect() {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
drawerApi.drawerLoading(true);
|
drawerApi.drawerLoading(true);
|
||||||
|
|
||||||
const { id } = drawerApi.getData() as { id?: number | string };
|
const { id } = drawerApi.getData() as { id?: number | string };
|
||||||
isUpdate.value = !!id;
|
isUpdate.value = !!id;
|
||||||
// 初始化
|
// 初始化
|
||||||
await setupPackageSelect();
|
await setupPackageSelect();
|
||||||
|
|
||||||
if (isUpdate.value && id) {
|
if (isUpdate.value && id) {
|
||||||
const record = await tenantInfo(id);
|
const record = await tenantInfo(id);
|
||||||
await formApi.setValues(record);
|
await formApi.setValues(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
formApi.updateSchema([
|
formApi.updateSchema([
|
||||||
{
|
{
|
||||||
fieldName: 'packageId',
|
fieldName: 'packageId',
|
||||||
@@ -75,6 +87,8 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
drawerApi.drawerLoading(false);
|
drawerApi.drawerLoading(false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -82,32 +96,33 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
const tenantStore = useTenantStore();
|
const tenantStore = useTenantStore();
|
||||||
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 ? tenantUpdate(data) : tenantAdd(data));
|
await (isUpdate.value ? tenantUpdate(data) : tenantAdd(data));
|
||||||
|
resetInitialized();
|
||||||
emit('reload');
|
emit('reload');
|
||||||
await handleCancel();
|
drawerApi.close();
|
||||||
// 重新加载租户信息
|
// 重新加载租户信息
|
||||||
tenantStore.initTenant();
|
tenantStore.initTenant();
|
||||||
} 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>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
|
<BasicDrawer :title="title" class="w-[600px]">
|
||||||
<BasicForm />
|
<BasicForm />
|
||||||
</BasicDrawer>
|
</BasicDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -65,12 +66,6 @@ export const drawerSchema: FormSchemaGetter = () => [
|
|||||||
{
|
{
|
||||||
component: 'Textarea',
|
component: 'Textarea',
|
||||||
fieldName: 'remark',
|
fieldName: 'remark',
|
||||||
formItemClass: 'items-start',
|
|
||||||
label: '备注',
|
label: '备注',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// 租户管理 不可分配 只有superadmin有权限操作 分配了也没用
|
|
||||||
export const excludeIds = [
|
|
||||||
6, 121, 122, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615,
|
|
||||||
];
|
|
||||||
|
@@ -17,6 +17,7 @@ import {
|
|||||||
packageUpdate,
|
packageUpdate,
|
||||||
} from '#/api/system/tenant-package';
|
} from '#/api/system/tenant-package';
|
||||||
import { MenuSelectTable } from '#/components/tree';
|
import { MenuSelectTable } from '#/components/tree';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
|
|
||||||
@@ -65,9 +66,26 @@ async function setupMenuTree(id?: number | string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function customFormValueGetter() {
|
||||||
|
const v = await defaultFormValueGetter(formApi)();
|
||||||
|
// 获取勾选信息
|
||||||
|
const menuIds = menuSelectRef.value?.getCheckedKeys?.() ?? [];
|
||||||
|
const mixStr = v + menuIds.join(',');
|
||||||
|
return mixStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
|
{
|
||||||
|
initializedGetter: customFormValueGetter,
|
||||||
|
currentGetter: customFormValueGetter,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [BasicDrawer, drawerApi] = useVbenDrawer({
|
const [BasicDrawer, drawerApi] = useVbenDrawer({
|
||||||
onCancel: handleCancel,
|
onBeforeClose,
|
||||||
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
|
destroyOnClose: true,
|
||||||
async onOpenChange(isOpen) {
|
async onOpenChange(isOpen) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return null;
|
return null;
|
||||||
@@ -84,6 +102,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
}
|
}
|
||||||
// init菜单 注意顺序要放在赋值record之后 内部watch会依赖record
|
// init菜单 注意顺序要放在赋值record之后 内部watch会依赖record
|
||||||
await setupMenuTree(id);
|
await setupMenuTree(id);
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
drawerApi.drawerLoading(false);
|
drawerApi.drawerLoading(false);
|
||||||
},
|
},
|
||||||
@@ -103,8 +122,9 @@ async function handleConfirm() {
|
|||||||
const data = cloneDeep(await formApi.getValues());
|
const data = cloneDeep(await formApi.getValues());
|
||||||
data.menuIds = menuIds;
|
data.menuIds = menuIds;
|
||||||
await (isUpdate.value ? packageUpdate(data) : packageAdd(data));
|
await (isUpdate.value ? packageUpdate(data) : packageAdd(data));
|
||||||
|
resetInitialized();
|
||||||
emit('reload');
|
emit('reload');
|
||||||
await handleCancel();
|
drawerApi.close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -112,9 +132,9 @@ async function handleConfirm() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCancel() {
|
async function handleClosed() {
|
||||||
drawerApi.close();
|
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
|
resetInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,7 +147,7 @@ function handleMenuCheckStrictlyChange(value: boolean) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[800px]">
|
<BasicDrawer :title="title" class="w-[800px]">
|
||||||
<BasicForm>
|
<BasicForm>
|
||||||
<template #menuIds="slotProps">
|
<template #menuIds="slotProps">
|
||||||
<div class="h-[600px] w-full">
|
<div class="h-[600px] w-full">
|
||||||
|
@@ -1,44 +0,0 @@
|
|||||||
import type { PropType } from 'vue';
|
|
||||||
|
|
||||||
import type { Menu } from '#/api/system/menu/model';
|
|
||||||
|
|
||||||
import { computed, defineComponent } from 'vue';
|
|
||||||
|
|
||||||
import { Tag } from 'ant-design-vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'TreeItem',
|
|
||||||
props: {
|
|
||||||
data: {
|
|
||||||
required: true,
|
|
||||||
type: Object as PropType<Menu>,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props, { expose }) {
|
|
||||||
expose();
|
|
||||||
|
|
||||||
interface TagProp {
|
|
||||||
color: string;
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const menuTagProp = computed<TagProp>(() => {
|
|
||||||
// 正则判断是否为链接
|
|
||||||
if (/^https?:\/\/[^\s/$.?#].\S*$/i.test(props.data.path)) {
|
|
||||||
return { color: 'pink', text: '外链' };
|
|
||||||
}
|
|
||||||
const type = props.data.menuType;
|
|
||||||
if (type === 'M') return { color: 'green', text: '目录' };
|
|
||||||
if (type === 'C') return { color: 'blue', text: '菜单' };
|
|
||||||
if (type === 'F') return { color: '', text: '按钮' };
|
|
||||||
return { color: 'error', text: '未知' };
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div class="flex gap-[6px]">
|
|
||||||
<span>{props.data.menuName}</span>
|
|
||||||
<Tag color={menuTagProp.value.color}>{menuTagProp.value.text}</Tag>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -113,6 +113,9 @@ const gridOptions: VxeGridProps = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
headerCellConfig: {
|
||||||
|
height: 44,
|
||||||
|
},
|
||||||
cellConfig: {
|
cellConfig: {
|
||||||
height: 48,
|
height: 48,
|
||||||
},
|
},
|
||||||
|
@@ -18,6 +18,7 @@ import {
|
|||||||
userAdd,
|
userAdd,
|
||||||
userUpdate,
|
userUpdate,
|
||||||
} from '#/api/system/user';
|
} from '#/api/system/user';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
import { authScopeOptions } from '#/views/system/role/data';
|
import { authScopeOptions } from '#/views/system/role/data';
|
||||||
|
|
||||||
import { drawerSchema } from './data';
|
import { drawerSchema } from './data';
|
||||||
@@ -134,8 +135,16 @@ async function loadDefaultPassword(update: boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
@@ -149,6 +158,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
drawerApi.drawerLoading(true);
|
drawerApi.drawerLoading(true);
|
||||||
|
|
||||||
const { id } = drawerApi.getData() as { id?: number | string };
|
const { id } = drawerApi.getData() as { id?: number | string };
|
||||||
isUpdate.value = !!id;
|
isUpdate.value = !!id;
|
||||||
/** update时 禁用用户名修改 不显示密码框 */
|
/** update时 禁用用户名修改 不显示密码框 */
|
||||||
@@ -186,10 +196,11 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
fieldName: 'postIds',
|
fieldName: 'postIds',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
// 部门选择 && 初始密码
|
|
||||||
await Promise.all([setupDeptSelect(), loadDefaultPassword(isUpdate.value)]);
|
// 部门选择、初始密码及用户相关操作并行处理
|
||||||
|
const promises = [setupDeptSelect(), loadDefaultPassword(isUpdate.value)];
|
||||||
if (user) {
|
if (user) {
|
||||||
await Promise.all([
|
promises.push(
|
||||||
// 添加基础信息
|
// 添加基础信息
|
||||||
formApi.setValues(user),
|
formApi.setValues(user),
|
||||||
// 添加角色和岗位
|
// 添加角色和岗位
|
||||||
@@ -197,38 +208,43 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
|||||||
formApi.setFieldValue('roleIds', roleIds),
|
formApi.setFieldValue('roleIds', roleIds),
|
||||||
// 更新时不会触发onSelect 需要手动调用
|
// 更新时不会触发onSelect 需要手动调用
|
||||||
setupPostOptions(user.deptId),
|
setupPostOptions(user.deptId),
|
||||||
]);
|
);
|
||||||
}
|
}
|
||||||
|
// 并行处理 重构后会带来10-50ms的优化
|
||||||
|
await Promise.all(promises);
|
||||||
|
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 ? userUpdate(data) : userAdd(data));
|
await (isUpdate.value ? userUpdate(data) : userAdd(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();
|
formApi.resetForm();
|
||||||
await formApi.resetForm();
|
resetInitialized();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
|
<BasicDrawer :title="title" class="w-[600px]">
|
||||||
<BasicForm />
|
<BasicForm />
|
||||||
</BasicDrawer>
|
</BasicDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -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"
|
||||||
|
@@ -17,6 +17,7 @@ import {
|
|||||||
categoryList,
|
categoryList,
|
||||||
categoryUpdate,
|
categoryUpdate,
|
||||||
} from '#/api/workflow/category';
|
} from '#/api/workflow/category';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { modalSchema } from './data';
|
import { modalSchema } from './data';
|
||||||
|
|
||||||
@@ -65,9 +66,17 @@ async function setupCategorySelect() {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
|
{
|
||||||
|
initializedGetter: defaultFormValueGetter(formApi),
|
||||||
|
currentGetter: defaultFormValueGetter(formApi),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [BasicModal, modalApi] = useVbenModal({
|
const [BasicModal, modalApi] = useVbenModal({
|
||||||
fullscreenButton: false,
|
fullscreenButton: false,
|
||||||
onCancel: handleCancel,
|
onBeforeClose,
|
||||||
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
onOpenChange: async (isOpen) => {
|
onOpenChange: async (isOpen) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
@@ -89,6 +98,7 @@ const [BasicModal, modalApi] = useVbenModal({
|
|||||||
await formApi.setValues({ parentId });
|
await formApi.setValues({ parentId });
|
||||||
}
|
}
|
||||||
await setupCategorySelect();
|
await setupCategorySelect();
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
modalApi.modalLoading(false);
|
modalApi.modalLoading(false);
|
||||||
},
|
},
|
||||||
@@ -96,7 +106,7 @@ const [BasicModal, modalApi] = useVbenModal({
|
|||||||
|
|
||||||
async function handleConfirm() {
|
async function handleConfirm() {
|
||||||
try {
|
try {
|
||||||
modalApi.modalLoading(true);
|
modalApi.lock(true);
|
||||||
const { valid } = await formApi.validate();
|
const { valid } = await formApi.validate();
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return;
|
return;
|
||||||
@@ -104,27 +114,24 @@ async function handleConfirm() {
|
|||||||
// getValues获取为一个readonly的对象 需要修改必须先深拷贝一次
|
// getValues获取为一个readonly的对象 需要修改必须先深拷贝一次
|
||||||
const data = cloneDeep(await formApi.getValues());
|
const data = cloneDeep(await formApi.getValues());
|
||||||
await (isUpdate.value ? categoryUpdate(data) : categoryAdd(data));
|
await (isUpdate.value ? categoryUpdate(data) : categoryAdd(data));
|
||||||
|
resetInitialized();
|
||||||
emit('reload');
|
emit('reload');
|
||||||
await handleCancel();
|
modalApi.close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.modalLoading(false);
|
modalApi.lock(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCancel() {
|
async function handleClosed() {
|
||||||
modalApi.close();
|
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
|
resetInitialized();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicModal
|
<BasicModal :title="title" class="min-h-[500px]">
|
||||||
:close-on-click-modal="false"
|
|
||||||
:title="title"
|
|
||||||
class="min-h-[500px]"
|
|
||||||
>
|
|
||||||
<BasicForm />
|
<BasicForm />
|
||||||
</BasicModal>
|
</BasicModal>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -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',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -93,9 +93,14 @@ const gridOptions: VxeGridProps = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
headerCellConfig: {
|
||||||
|
height: 44,
|
||||||
|
},
|
||||||
|
cellConfig: {
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
height: 100,
|
|
||||||
},
|
},
|
||||||
id: 'workflow-definition-index',
|
id: 'workflow-definition-index',
|
||||||
};
|
};
|
||||||
|
@@ -12,6 +12,7 @@ import {
|
|||||||
workflowDefinitionInfo,
|
workflowDefinitionInfo,
|
||||||
workflowDefinitionUpdate,
|
workflowDefinitionUpdate,
|
||||||
} from '#/api/workflow/definition';
|
} from '#/api/workflow/definition';
|
||||||
|
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||||
|
|
||||||
import { modalSchema } from './data';
|
import { modalSchema } from './data';
|
||||||
|
|
||||||
@@ -65,8 +66,16 @@ async function setupCategorySelect() {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||||
|
{
|
||||||
|
initializedGetter: defaultFormValueGetter(formApi),
|
||||||
|
currentGetter: defaultFormValueGetter(formApi),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [BasicDrawer, modalApi] = useVbenModal({
|
const [BasicDrawer, modalApi] = useVbenModal({
|
||||||
onCancel: handleCancel,
|
onBeforeClose,
|
||||||
|
onClosed: handleClosed,
|
||||||
onConfirm: handleConfirm,
|
onConfirm: handleConfirm,
|
||||||
async onOpenChange(isOpen) {
|
async onOpenChange(isOpen) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
@@ -83,6 +92,7 @@ const [BasicDrawer, modalApi] = useVbenModal({
|
|||||||
const record = await workflowDefinitionInfo(id);
|
const record = await workflowDefinitionInfo(id);
|
||||||
await formApi.setValues(record);
|
await formApi.setValues(record);
|
||||||
}
|
}
|
||||||
|
await markInitialized();
|
||||||
|
|
||||||
modalApi.modalLoading(false);
|
modalApi.modalLoading(false);
|
||||||
},
|
},
|
||||||
@@ -90,7 +100,7 @@ const [BasicDrawer, modalApi] = useVbenModal({
|
|||||||
|
|
||||||
async function handleConfirm() {
|
async function handleConfirm() {
|
||||||
try {
|
try {
|
||||||
modalApi.modalLoading(true);
|
modalApi.lock(true);
|
||||||
const { valid } = await formApi.validate();
|
const { valid } = await formApi.validate();
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return;
|
return;
|
||||||
@@ -103,27 +113,23 @@ async function handleConfirm() {
|
|||||||
await workflowDefinitionAdd(data);
|
await workflowDefinitionAdd(data);
|
||||||
emit('reload', 'add');
|
emit('reload', 'add');
|
||||||
}
|
}
|
||||||
await handleCancel();
|
resetInitialized();
|
||||||
|
modalApi.close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.modalLoading(false);
|
modalApi.lock(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCancel() {
|
async function handleClosed() {
|
||||||
modalApi.close();
|
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
|
resetInitialized();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicDrawer
|
<BasicDrawer :fullscreen-button="false" :title="title" class="w-[550px]">
|
||||||
:close-on-click-modal="false"
|
|
||||||
:fullscreen-button="false"
|
|
||||||
:title="title"
|
|
||||||
class="w-[550px]"
|
|
||||||
>
|
|
||||||
<div class="min-h-[400px]">
|
<div class="min-h-[400px]">
|
||||||
<BasicForm />
|
<BasicForm />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -41,7 +41,7 @@ const formOptions: VbenFormProps = {
|
|||||||
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
|
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
|
||||||
handleReset: async () => {
|
handleReset: async () => {
|
||||||
selectedCode.value = [];
|
selectedCode.value = [];
|
||||||
// eslint-disable-next-line no-use-before-define
|
|
||||||
const { formApi, reload } = tableApi;
|
const { formApi, reload } = tableApi;
|
||||||
await formApi.resetForm();
|
await formApi.resetForm();
|
||||||
const formValues = formApi.form.values;
|
const formValues = formApi.form.values;
|
||||||
@@ -68,7 +68,7 @@ async function handleTypeChange(e: RadioChangeEvent) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line no-use-before-define
|
|
||||||
await tableApi.reload();
|
await tableApi.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,9 +103,14 @@ const gridOptions: VxeGridProps = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
headerCellConfig: {
|
||||||
|
height: 44,
|
||||||
|
},
|
||||||
|
cellConfig: {
|
||||||
|
height: 66,
|
||||||
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
height: 66,
|
|
||||||
},
|
},
|
||||||
id: 'workflow-definition-index',
|
id: 'workflow-definition-index',
|
||||||
};
|
};
|
||||||
|
@@ -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',
|
||||||
|
@@ -5,7 +5,7 @@ import type { CustomGetter } from '#/components/upload/src/props';
|
|||||||
|
|
||||||
import { h, ref } from 'vue';
|
import { h, ref } from 'vue';
|
||||||
|
|
||||||
import { CodeMirror, Page } from '@vben/common-ui';
|
import { CodeMirror, Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
import { useClipboard } from '@vueuse/core';
|
import { useClipboard } from '@vueuse/core';
|
||||||
import { Alert, Card, Modal, RadioGroup, Switch } from 'ant-design-vue';
|
import { Alert, Card, Modal, RadioGroup, Switch } from 'ant-design-vue';
|
||||||
@@ -14,6 +14,7 @@ import { FileUpload, ImageUpload } from '#/components/upload';
|
|||||||
|
|
||||||
import { useFileType, useImageType } from './hook';
|
import { useFileType, useImageType } from './hook';
|
||||||
import sql from './insert.sql?raw';
|
import sql from './insert.sql?raw';
|
||||||
|
import uploadModal from './upload-modal.vue';
|
||||||
|
|
||||||
const singleImageId = ref('1905537674682916865');
|
const singleImageId = ref('1905537674682916865');
|
||||||
const singleFileId = ref('1905191167882518529');
|
const singleFileId = ref('1905191167882518529');
|
||||||
@@ -53,6 +54,10 @@ const customThumbnailUrl: CustomGetter<undefined> = () => {
|
|||||||
const { copy } = useClipboard({ legacy: true });
|
const { copy } = useClipboard({ legacy: true });
|
||||||
|
|
||||||
const animationEnable = ref(false);
|
const animationEnable = ref(false);
|
||||||
|
|
||||||
|
const [UploadModal, uploadModalApi] = useVbenModal({
|
||||||
|
connectedComponent: uploadModal,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -63,6 +68,10 @@ const animationEnable = ref(false);
|
|||||||
<CodeMirror class="mt-2" v-model="sql" language="sql" readonly />
|
<CodeMirror class="mt-2" v-model="sql" language="sql" readonly />
|
||||||
</Card>
|
</Card>
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<Card title="表单上传">
|
||||||
|
<a-button @click="uploadModalApi.open()">打开</a-button>
|
||||||
|
<UploadModal />
|
||||||
|
</Card>
|
||||||
<Card title="单上传, 会绑定为string" size="small">
|
<Card title="单上传, 会绑定为string" size="small">
|
||||||
<ImageUpload v-model:value="singleImageId" />
|
<ImageUpload v-model:value="singleImageId" />
|
||||||
当前绑定值: {{ singleImageId }}
|
当前绑定值: {{ singleImageId }}
|
||||||
|
70
apps/web-antd/src/views/演示使用自行删除/upload/upload-modal.vue
Normal file
70
apps/web-antd/src/views/演示使用自行删除/upload/upload-modal.vue
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { JsonPreview, useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { Modal, Space } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
|
||||||
|
const [BasicForm, formApi] = useVbenForm({
|
||||||
|
layout: 'vertical',
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
label: '图片上传多图',
|
||||||
|
component: 'ImageUpload',
|
||||||
|
fieldName: 'ossIds',
|
||||||
|
componentProps: {
|
||||||
|
maxCount: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '图片上传单图',
|
||||||
|
component: 'ImageUpload',
|
||||||
|
fieldName: 'ossId',
|
||||||
|
componentProps: {
|
||||||
|
maxCount: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showDefaultActions: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getValues() {
|
||||||
|
try {
|
||||||
|
const v = await formApi.getValues();
|
||||||
|
console.log(v);
|
||||||
|
|
||||||
|
Modal.info({
|
||||||
|
content: () => h(JsonPreview, { data: v }),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleAssign() {
|
||||||
|
const ids = ['1908761290673315841', '1907738568539332610'];
|
||||||
|
await formApi.setValues({
|
||||||
|
ossIds: ids,
|
||||||
|
ossId: ids[0],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const [BasicModal] = useVbenModal({
|
||||||
|
title: '上传',
|
||||||
|
footer: false,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<BasicModal>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<Space>
|
||||||
|
<a-button @click="handleAssign">赋值</a-button>
|
||||||
|
<a-button @click="getValues">获取值</a-button>
|
||||||
|
</Space>
|
||||||
|
<BasicForm />
|
||||||
|
</div>
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
@@ -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.6",
|
||||||
"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
|
||||||
@@ -43,6 +66,9 @@ export type BeforeCloseScope = {
|
|||||||
isConfirm: boolean;
|
isConfirm: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* alert 属性
|
||||||
|
*/
|
||||||
export type AlertProps = {
|
export type AlertProps = {
|
||||||
/** 关闭前的回调,如果返回false,则终止关闭 */
|
/** 关闭前的回调,如果返回false,则终止关闭 */
|
||||||
beforeClose?: (
|
beforeClose?: (
|
||||||
@@ -50,6 +76,8 @@ export type AlertProps = {
|
|||||||
) => boolean | Promise<boolean | undefined> | undefined;
|
) => boolean | Promise<boolean | undefined> | undefined;
|
||||||
/** 边框 */
|
/** 边框 */
|
||||||
bordered?: boolean;
|
bordered?: boolean;
|
||||||
|
/** 按钮对齐方式 */
|
||||||
|
buttonAlign?: 'center' | 'end' | 'start';
|
||||||
/** 取消按钮的标题 */
|
/** 取消按钮的标题 */
|
||||||
cancelText?: string;
|
cancelText?: string;
|
||||||
/** 是否居中显示 */
|
/** 是否居中显示 */
|
||||||
@@ -62,14 +90,41 @@ export type AlertProps = {
|
|||||||
content: Component | string;
|
content: Component | string;
|
||||||
/** 弹窗内容的额外样式 */
|
/** 弹窗内容的额外样式 */
|
||||||
contentClass?: string;
|
contentClass?: string;
|
||||||
|
/** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/
|
||||||
|
contentMasking?: boolean;
|
||||||
|
/** 弹窗底部内容(与按钮在同一个容器中) */
|
||||||
|
footer?: Component | string;
|
||||||
/** 弹窗的图标(在标题的前面) */
|
/** 弹窗的图标(在标题的前面) */
|
||||||
icon?: Component | IconType;
|
icon?: Component | IconType;
|
||||||
|
/**
|
||||||
|
* 弹窗遮罩模糊效果
|
||||||
|
*/
|
||||||
|
overlayBlur?: number;
|
||||||
/** 是否显示取消按钮 */
|
/** 是否显示取消按钮 */
|
||||||
showCancel?: boolean;
|
showCancel?: boolean;
|
||||||
/** 弹窗标题 */
|
/** 弹窗标题 */
|
||||||
title?: string;
|
title?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** prompt 属性 */
|
||||||
|
export type PromptProps<T = any> = {
|
||||||
|
/** 关闭前的回调,如果返回false,则终止关闭 */
|
||||||
|
beforeClose?: (scope: {
|
||||||
|
isConfirm: boolean;
|
||||||
|
value: T | undefined;
|
||||||
|
}) => boolean | Promise<boolean | undefined> | undefined;
|
||||||
|
/** 用于接受用户输入的组件 */
|
||||||
|
component?: Component;
|
||||||
|
/** 输入组件的属性 */
|
||||||
|
componentProps?: Recordable<any>;
|
||||||
|
/** 输入组件的插槽 */
|
||||||
|
componentSlots?: Recordable<Component>;
|
||||||
|
/** 默认值 */
|
||||||
|
defaultValue?: T;
|
||||||
|
/** 输入组件的值属性名 */
|
||||||
|
modelPropName?: string;
|
||||||
|
} & Omit<AlertProps, 'beforeClose'>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 函数签名
|
* 函数签名
|
||||||
* alert和confirm的函数签名相同。
|
* alert和confirm的函数签名相同。
|
||||||
|
@@ -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 |
|
||||||
|
@@ -78,7 +78,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| 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` | - |
|
||||||
@@ -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
|
||||||
|
|
||||||
|
@@ -167,6 +167,23 @@ vxeUI.renderer.add('CellLink', {
|
|||||||
|
|
||||||
当启用了表单搜索时,可以在toolbarConfig中配置`search`为`true`来让表格在工具栏区域显示一个搜索表单控制按钮。表格的所有以`form-`开头的命名插槽都会被传递给搜索表单。
|
当启用了表单搜索时,可以在toolbarConfig中配置`search`为`true`来让表格在工具栏区域显示一个搜索表单控制按钮。表格的所有以`form-`开头的命名插槽都会被传递给搜索表单。
|
||||||
|
|
||||||
|
### 定制分隔条
|
||||||
|
|
||||||
|
当你启用表单搜索时,在表单和表格之间会显示一个分隔条。这个分隔条使用了默认的组件背景色,并且横向贯穿整个Vben Vxe Table在视觉上融入了页面的默认背景中。如果你在Vben Vxe Table的外层包裹了一个不同背景色的容器(如将其放在一个Card内),默认的表单和表格之间的分隔条可能就显得格格不入了,下面的代码演示了如何定制这个分隔条。
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {},
|
||||||
|
gridOptions: {},
|
||||||
|
// 完全移除分隔条
|
||||||
|
separator: false,
|
||||||
|
// 你也可以使用下面的代码来移除分隔条
|
||||||
|
// separator: { show: false },
|
||||||
|
// 或者使用下面的代码来改变分隔条的颜色
|
||||||
|
// separator: { backgroundColor: 'rgba(100,100,0,0.5)' },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
<DemoPreview dir="demos/vben-vxe-table/form" />
|
<DemoPreview dir="demos/vben-vxe-table/form" />
|
||||||
|
|
||||||
## 单元格编辑
|
## 单元格编辑
|
||||||
@@ -231,15 +248,16 @@ useVbenVxeGrid 返回的第二个参数,是一个对象,包含了一些表
|
|||||||
|
|
||||||
所有属性都可以传入 `useVbenVxeGrid` 的第一个参数中。
|
所有属性都可以传入 `useVbenVxeGrid` 的第一个参数中。
|
||||||
|
|
||||||
| 属性名 | 描述 | 类型 |
|
| 属性名 | 描述 | 类型 | 版本要求 |
|
||||||
| -------------- | -------------------- | ------------------- |
|
| --- | --- | --- | --- |
|
||||||
| tableTitle | 表格标题 | `string` |
|
| tableTitle | 表格标题 | `string` | - |
|
||||||
| tableTitleHelp | 表格标题帮助信息 | `string` |
|
| tableTitleHelp | 表格标题帮助信息 | `string` | - |
|
||||||
| gridClass | grid组件的class | `string` |
|
| gridClass | grid组件的class | `string` | - |
|
||||||
| gridOptions | grid组件的参数 | `VxeTableGridProps` |
|
| gridOptions | grid组件的参数 | `VxeTableGridProps` | - |
|
||||||
| gridEvents | grid组件的触发的事件 | `VxeGridListeners` |
|
| gridEvents | grid组件的触发的事件 | `VxeGridListeners` | - |
|
||||||
| formOptions | 表单参数 | `VbenFormProps` |
|
| formOptions | 表单参数 | `VbenFormProps` | - |
|
||||||
| showSearchForm | 是否显示搜索表单 | `boolean` |
|
| showSearchForm | 是否显示搜索表单 | `boolean` | - |
|
||||||
|
| separator | 搜索表单与表格主体之间的分隔条 | `boolean\|SeparatorOptions` | >5.5.4 |
|
||||||
|
|
||||||
## Slots
|
## Slots
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ import { h } from 'vue';
|
|||||||
|
|
||||||
import { alert, VbenButton } from '@vben/common-ui';
|
import { alert, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
import { Empty } from 'ant-design-vue';
|
import { Result } from 'ant-design-vue';
|
||||||
|
|
||||||
function showAlert() {
|
function showAlert() {
|
||||||
alert('This is an alert message');
|
alert('This is an alert message');
|
||||||
@@ -18,7 +18,12 @@ function showIconAlert() {
|
|||||||
|
|
||||||
function showCustomAlert() {
|
function showCustomAlert() {
|
||||||
alert({
|
alert({
|
||||||
content: h(Empty, { description: '什么都没有' }),
|
buttonAlign: 'center',
|
||||||
|
content: h(Result, {
|
||||||
|
status: 'success',
|
||||||
|
subTitle: '已成功创建订单。订单ID:2017182818828182881',
|
||||||
|
title: '操作成功',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user