!288 修复vue3 部分bug 重构axios 完善vxe demo

Merge pull request !288 from xingyu/dev
This commit is contained in:
芋道源码 2022-11-07 05:35:51 +00:00 committed by Gitee
commit cb6a9a1282
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
68 changed files with 1352 additions and 1335 deletions

View File

@ -46,8 +46,8 @@
<jasypt-spring-boot-starter.version>3.0.4</jasypt-spring-boot-starter.version>
<lombok.version>1.18.24</lombok.version>
<mapstruct.version>1.5.3.Final</mapstruct.version>
<hutool.version>5.8.8</hutool.version>
<easyexcel.verion>3.1.1</easyexcel.verion>
<hutool.version>5.8.9</hutool.version>
<easyexcel.verion>3.1.2</easyexcel.verion>
<velocity.version>2.3</velocity.version>
<screw.version>1.0.5</screw.version>
<fastjson.version>1.2.83</fastjson.version>

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import { ${simpleClassName}VO, ${simpleClassName}PageReqVO, ${simpleClassName}ExcelReqVO } from './types'
const request = useAxios()
#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
// 查询${table.classComment}列表
export const get${simpleClassName}PageApi = async (params: ${simpleClassName}PageReqVO) => {

View File

@ -6,9 +6,9 @@ import { DICT_TYPE } from '@/utils/dict'
import { useTable } from '@/hooks/web/useTable'
import { useI18n } from '@/hooks/web/useI18n'
import { FormExpose } from '@/components/Form'
import type { ${simpleClassName}VO } from '@/api/${table.moduleName}/${simpleClassName}/types'
import { rules, allSchemas } from './${simpleClassName}.data'
import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${simpleClassName}'
import type { ${simpleClassName}VO } from '@/api/${table.moduleName}/${classNameVar}/types'
import { rules, allSchemas } from './${classNameVar}.data'
import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${classNameVar}'
const { t } = useI18n() // 国际化
// ========== 列表相关 ==========

View File

@ -11,4 +11,4 @@ VITE_OPEN=true
VITE_APP_TENANT_ENABLE=true
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=false
VITE_APP_CAPTCHA_ENABLE=true

View File

@ -36,9 +36,9 @@
"dayjs": "^1.11.6",
"echarts": "^5.4.0",
"echarts-wordcloud": "^2.0.0",
"element-plus": "2.2.19",
"element-plus": "2.2.20",
"intro.js": "^6.0.0",
"jsencrypt": "^3.3.0",
"jsencrypt": "^3.3.1",
"js-cookie": "^3.0.1",
"lodash-es": "^4.17.21",
"mitt": "^3.0.0",
@ -59,7 +59,7 @@
"devDependencies": {
"@commitlint/cli": "^17.2.0",
"@commitlint/config-conventional": "^17.2.0",
"@iconify/json": "^2.1.131",
"@iconify/json": "^2.1.134",
"@intlify/vite-plugin-vue-i18n": "^6.0.3",
"@purge-icons/generated": "^0.9.0",
"@types/intro.js": "^5.1.0",
@ -73,7 +73,7 @@
"@vitejs/plugin-vue": "^3.2.0",
"@vitejs/plugin-vue-jsx": "^2.1.0",
"autoprefixer": "^10.4.13",
"eslint": "^8.26.0",
"eslint": "^8.27.0",
"eslint-config-prettier": "^8.5.0",
"eslint-define-config": "^1.11.0",
"eslint-plugin-prettier": "^4.2.1",
@ -86,15 +86,15 @@
"postcss-less": "^6.0.0",
"prettier": "^2.7.1",
"rimraf": "^3.0.2",
"rollup": "^2.79.1",
"stylelint": "^14.14.0",
"rollup": "^3.2.5",
"stylelint": "^14.14.1",
"stylelint-config-html": "^1.1.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended": "^9.0.0",
"stylelint-config-standard": "^29.0.0",
"stylelint-order": "^5.0.0",
"typescript": "4.8.4",
"unplugin-vue-macros": "^0.15.2",
"unplugin-vue-macros": "^0.16.0",
"vite": "3.2.2",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-eslint": "^1.8.1",

View File

@ -4,7 +4,7 @@ specifiers:
'@commitlint/cli': ^17.2.0
'@commitlint/config-conventional': ^17.2.0
'@iconify/iconify': ^3.0.0
'@iconify/json': ^2.1.131
'@iconify/json': ^2.1.134
'@intlify/vite-plugin-vue-i18n': ^6.0.3
'@purge-icons/generated': ^0.9.0
'@types/intro.js': ^5.1.0
@ -28,14 +28,15 @@ specifiers:
dayjs: ^1.11.6
echarts: ^5.4.0
echarts-wordcloud: ^2.0.0
element-plus: 2.2.19
eslint: ^8.26.0
element-plus: 2.2.20
eslint: ^8.27.0
eslint-config-prettier: ^8.5.0
eslint-define-config: ^1.11.0
eslint-plugin-prettier: ^4.2.1
eslint-plugin-vue: ^9.7.0
intro.js: ^6.0.0
jsencrypt: ^3.3.0
js-cookie: ^3.0.1
jsencrypt: ^3.3.1
less: ^4.1.3
lint-staged: ^13.0.3
lodash-es: ^4.17.21
@ -50,15 +51,15 @@ specifiers:
qrcode: ^1.5.1
qs: ^6.11.0
rimraf: ^3.0.2
rollup: ^2.79.1
stylelint: ^14.14.0
rollup: ^3.2.5
stylelint: ^14.14.1
stylelint-config-html: ^1.1.0
stylelint-config-prettier: ^9.0.3
stylelint-config-recommended: ^9.0.0
stylelint-config-standard: ^29.0.0
stylelint-order: ^5.0.0
typescript: 4.8.4
unplugin-vue-macros: ^0.15.2
unplugin-vue-macros: ^0.16.0
url: ^0.11.0
vite: 3.2.2
vite-plugin-compression: ^0.5.1
@ -91,9 +92,10 @@ dependencies:
dayjs: 1.11.6
echarts: 5.4.0
echarts-wordcloud: 2.0.0_echarts@5.4.0
element-plus: 2.2.19_vue@3.2.41
element-plus: 2.2.20_vue@3.2.41
intro.js: 6.0.0
jsencrypt: 3.3.0
js-cookie: 3.0.1
jsencrypt: 3.3.1
lodash-es: 4.17.21
mitt: 3.0.0
nprogress: 0.2.0
@ -113,7 +115,7 @@ dependencies:
devDependencies:
'@commitlint/cli': 17.2.0
'@commitlint/config-conventional': 17.2.0
'@iconify/json': 2.1.131
'@iconify/json': 2.1.134
'@intlify/vite-plugin-vue-i18n': 6.0.3_vite@3.2.2+vue-i18n@9.2.2
'@purge-icons/generated': 0.9.0
'@types/intro.js': 5.1.0
@ -122,16 +124,16 @@ devDependencies:
'@types/nprogress': 0.2.0
'@types/qrcode': 1.5.0
'@types/qs': 6.9.7
'@typescript-eslint/eslint-plugin': 5.42.0_6xw5wg2354iw4zujk2f3vyfrzu
'@typescript-eslint/parser': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
'@typescript-eslint/eslint-plugin': 5.42.0_ofgjrzjuekeo7s3hdyz2yuzw34
'@typescript-eslint/parser': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
'@vitejs/plugin-vue': 3.2.0_vite@3.2.2+vue@3.2.41
'@vitejs/plugin-vue-jsx': 2.1.0_vite@3.2.2+vue@3.2.41
autoprefixer: 10.4.13_postcss@8.4.18
eslint: 8.26.0
eslint-config-prettier: 8.5.0_eslint@8.26.0
eslint: 8.27.0
eslint-config-prettier: 8.5.0_eslint@8.27.0
eslint-define-config: 1.11.0
eslint-plugin-prettier: 4.2.1_aniwkeyvlpmwkidetuytnokvcm
eslint-plugin-vue: 9.7.0_eslint@8.26.0
eslint-plugin-prettier: 4.2.1_v7o5sx5x3wbs57ifz6wc4f76we
eslint-plugin-vue: 9.7.0_eslint@8.27.0
less: 4.1.3
lint-staged: 13.0.3
plop: 3.1.1
@ -140,18 +142,18 @@ devDependencies:
postcss-less: 6.0.0_postcss@8.4.18
prettier: 2.7.1
rimraf: 3.0.2
rollup: 2.79.1
stylelint: 14.14.0
stylelint-config-html: 1.1.0_2wb2ag3ubchp7fdo72hd6rkeve
stylelint-config-prettier: 9.0.3_stylelint@14.14.0
stylelint-config-recommended: 9.0.0_stylelint@14.14.0
stylelint-config-standard: 29.0.0_stylelint@14.14.0
stylelint-order: 5.0.0_stylelint@14.14.0
rollup: 3.2.5
stylelint: 14.14.1
stylelint-config-html: 1.1.0_a4i6jbpfaxelx4fvjhtlgvxx6i
stylelint-config-prettier: 9.0.3_stylelint@14.14.1
stylelint-config-recommended: 9.0.0_stylelint@14.14.1
stylelint-config-standard: 29.0.0_stylelint@14.14.1
stylelint-order: 5.0.0_stylelint@14.14.1
typescript: 4.8.4
unplugin-vue-macros: 0.15.2_mbqnf624zbi2ta2juc2wzs2gvm
unplugin-vue-macros: 0.16.0_f6zolwli45a6dkgzq5zhbk2c7e
vite: 3.2.2_less@4.1.3
vite-plugin-compression: 0.5.1_vite@3.2.2
vite-plugin-eslint: 1.8.1_eslint@8.26.0+vite@3.2.2
vite-plugin-eslint: 1.8.1_eslint@8.27.0+vite@3.2.2
vite-plugin-html: 3.2.0_vite@3.2.2
vite-plugin-purge-icons: 0.9.1_vite@3.2.2
vite-plugin-style-import: 2.0.0_vite@3.2.2
@ -785,8 +787,8 @@ packages:
dependencies:
'@iconify/types': 2.0.0
/@iconify/json/2.1.131:
resolution: {integrity: sha512-VUQLJ2IHEcB0Is9K/grtLEC0OuLV3Fj0h3t04fKMN9hXwJkn2SKounQr44w7LzcBlu2RRYA7/npGlovtWV0iXQ==}
/@iconify/json/2.1.134:
resolution: {integrity: sha512-vqz3quli1lcFogDyu3H7mHyPjub+bZv8dJaR1lRk1gzblKBPlKoHhC0VaAjSMiA5L6toqXQRp7osjulB4JkA5Q==}
dependencies:
'@iconify/types': 2.0.0
pathe: 0.3.9
@ -1118,7 +1120,7 @@ packages:
/@types/web-bluetooth/0.0.16:
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
/@typescript-eslint/eslint-plugin/5.42.0_6xw5wg2354iw4zujk2f3vyfrzu:
/@typescript-eslint/eslint-plugin/5.42.0_ofgjrzjuekeo7s3hdyz2yuzw34:
resolution: {integrity: sha512-5TJh2AgL6+wpL8H/GTSjNb4WrjKoR2rqvFxR/DDTqYNk6uXn8BJMEcncLSpMbf/XV1aS0jAjYwn98uvVCiAywQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -1129,12 +1131,12 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/parser': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
'@typescript-eslint/parser': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
'@typescript-eslint/scope-manager': 5.42.0
'@typescript-eslint/type-utils': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
'@typescript-eslint/utils': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
'@typescript-eslint/type-utils': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
'@typescript-eslint/utils': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
debug: 4.3.4
eslint: 8.26.0
eslint: 8.27.0
ignore: 5.2.0
natural-compare-lite: 1.4.0
regexpp: 3.2.0
@ -1145,7 +1147,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/parser/5.42.0_wyqvi574yv7oiwfeinomdzmc3m:
/@typescript-eslint/parser/5.42.0_rmayb2veg2btbq6mbmnyivgasy:
resolution: {integrity: sha512-Ixh9qrOTDRctFg3yIwrLkgf33AHyEIn6lhyf5cCfwwiGtkWhNpVKlEZApi3inGQR/barWnY7qY8FbGKBO7p3JA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -1159,7 +1161,7 @@ packages:
'@typescript-eslint/types': 5.42.0
'@typescript-eslint/typescript-estree': 5.42.0_typescript@4.8.4
debug: 4.3.4
eslint: 8.26.0
eslint: 8.27.0
typescript: 4.8.4
transitivePeerDependencies:
- supports-color
@ -1173,7 +1175,7 @@ packages:
'@typescript-eslint/visitor-keys': 5.42.0
dev: true
/@typescript-eslint/type-utils/5.42.0_wyqvi574yv7oiwfeinomdzmc3m:
/@typescript-eslint/type-utils/5.42.0_rmayb2veg2btbq6mbmnyivgasy:
resolution: {integrity: sha512-HW14TXC45dFVZxnVW8rnUGnvYyRC0E/vxXShFCthcC9VhVTmjqOmtqj6H5rm9Zxv+ORxKA/1aLGD7vmlLsdlOg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -1184,9 +1186,9 @@ packages:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 5.42.0_typescript@4.8.4
'@typescript-eslint/utils': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
'@typescript-eslint/utils': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
debug: 4.3.4
eslint: 8.26.0
eslint: 8.27.0
tsutils: 3.21.0_typescript@4.8.4
typescript: 4.8.4
transitivePeerDependencies:
@ -1219,7 +1221,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/utils/5.42.0_wyqvi574yv7oiwfeinomdzmc3m:
/@typescript-eslint/utils/5.42.0_rmayb2veg2btbq6mbmnyivgasy:
resolution: {integrity: sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -1230,9 +1232,9 @@ packages:
'@typescript-eslint/scope-manager': 5.42.0
'@typescript-eslint/types': 5.42.0
'@typescript-eslint/typescript-estree': 5.42.0_typescript@4.8.4
eslint: 8.26.0
eslint: 8.27.0
eslint-scope: 5.1.1
eslint-utils: 3.0.0_eslint@8.26.0
eslint-utils: 3.0.0_eslint@8.27.0
semver: 7.3.8
transitivePeerDependencies:
- supports-color
@ -1355,26 +1357,26 @@ packages:
'@volar/vue-language-core': 1.0.9
dev: true
/@vue-macros/api/0.1.1:
resolution: {integrity: sha512-SZHwR1zsmgTi3HmqvcjUEiI+pdSLSOj1pW2g1rdXK8s2hpI9EdxPRPv+r3MSb+513rBZ8ZRsIs8HkDYszqi3vg==}
/@vue-macros/api/0.1.2:
resolution: {integrity: sha512-NuhWgOmxwmdHtGVhucLaRSJPymmr8Phabw2PuV8mLp7pRmjibp+99+RWF6IwXCNqVnEDYt3MwcEzTvPSzRcqjA==}
engines: {node: '>=14.19.0'}
dependencies:
'@babel/types': 7.20.0
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
dev: true
/@vue-macros/better-define/0.0.3:
resolution: {integrity: sha512-X3J6PEC2iNps1TCs1g/po15UMfW/6HG8VvRbDbGPCzjZ7MyQCYJE/aZ1vF5aqrU/+1xiGCCXkJv+5no6RAmq0Q==}
/@vue-macros/better-define/0.0.4:
resolution: {integrity: sha512-wp4C7Oom1P80oKvCtrpEDtotdIsTHXt0OqgBn47xUqdj4S/baG7Ji6qQ3MUtHxBTQ+nyC+7g9nxSADl+s1Px/Q==}
engines: {node: '>=14.19.0'}
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/api': 0.1.1
'@vue-macros/common': 0.13.2
'@vue-macros/api': 0.1.2
'@vue-macros/common': 0.13.3
unplugin: 0.10.2
dev: true
/@vue-macros/common/0.13.2:
resolution: {integrity: sha512-2pv9aUIw21IzdcROpo4FgMjEFR4zdV0ikHVF/kE/HX0xHcCteG4Pm7rHYK/JvIBhtiLDLWrcD31Y0B0XBaE0PQ==}
/@vue-macros/common/0.13.3:
resolution: {integrity: sha512-pV9UFwGZs7ddbAmJI5PZvNA2ZlNESQD81jOJM1wmwLnta66K8m0RQuJK2X/tdy7Avpm/p62M8opwbWyfXsBk4w==}
engines: {node: '>=14.19.0'}
dependencies:
'@babel/types': 7.20.0
@ -1382,8 +1384,8 @@ packages:
magic-string: 0.26.7
dev: true
/@vue-macros/define-model/0.13.7_@vueuse+core@9.4.0:
resolution: {integrity: sha512-nJXeeh94C49to6k6eGiSnaQGwWVqVpdLxHGFnJynzGCUIbaDAsrVfLWZWG1sbKNlGHHJRAFam4VqBavvqR7zAg==}
/@vue-macros/define-model/0.13.8_@vueuse+core@9.4.0:
resolution: {integrity: sha512-B/cV6n8wRRxwY4GLNo2q/YUzwYDR3Kbt2cI7Bxb51VRCxZexRqOazn5ZB8I9LdBwBLEenOX/3gRrHW3FApkRww==}
engines: {node: '>=14.19.0'}
peerDependencies:
'@vueuse/core': ^9.0.0
@ -1392,55 +1394,67 @@ packages:
optional: true
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
'@vueuse/core': 9.4.0_vue@3.2.41
ast-walker-scope: 0.3.0
unplugin: 0.10.2
dev: true
/@vue-macros/define-render/0.13.7_vue@3.2.41:
resolution: {integrity: sha512-7+9PDVVwKp9wtxFhsYDKIBTyr1qJYPOg5aMeG5btJ9zN2zVtNVVO+tx7yAE0xQBkHI7ml2bwy2PG3dFK/phmrQ==}
/@vue-macros/define-props/0.0.1_vue@3.2.41:
resolution: {integrity: sha512-LyB8k4JSwyBmjlVicxogD3N6+qGgpm7KJOeONNQJnHDo0bkdyssVXDIOEnqfgo+RdqJiqXKdFuBpw/lSvMp0KQ==}
engines: {node: '>=14.19.0'}
peerDependencies:
vue: ^3.2.25
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/common': 0.13.3
unplugin: 0.10.2
vue: 3.2.41
dev: true
/@vue-macros/define-render/0.13.8_vue@3.2.41:
resolution: {integrity: sha512-a1FIGgTuvLN5gfbh7/dtYLSaXPBYLu33l2sc0auc55uNXqB6qXGzxu/12bEskONhYcx1BBwsqjWiSFbSJ3yLbg==}
engines: {node: '>=14.19.0'}
peerDependencies:
vue: ^2.7.0 || ^3.0.0
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
unplugin: 0.10.2
vue: 3.2.41
dev: true
/@vue-macros/define-slots/0.0.4_vue@3.2.41:
resolution: {integrity: sha512-AAUz85J1ZL9Lsq3omhwkBe1p95L/VHixdcBonmt1jTCpjpCMmDWUmzl4iPh5pRMkk0D1lfvm5V/m0D68YqKZIg==}
/@vue-macros/define-slots/0.0.5_vue@3.2.41:
resolution: {integrity: sha512-+CS2MTH1fBeHbiZzThn6aXmrcbPRiuG29yWI6ugMarpIxwvVWXrPcvrWSE2BK/FSwcCR9Xfz7rETIo+BSjdo6A==}
engines: {node: '>=14.19.0'}
peerDependencies:
vue: ^2.7.0 || ^3.0.0
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
unplugin: 0.10.2
vue: 3.2.41
dev: true
/@vue-macros/hoist-static/0.12.6:
resolution: {integrity: sha512-TeF6Dbug2PHaDuHd4VaiL+wPx58yJwaZyPhHA7IvGeo7xHh/nyXSj64aRMj3g5ovi38zXHHLvSb+yMNc3PYhBg==}
/@vue-macros/hoist-static/0.12.7:
resolution: {integrity: sha512-YRQ1zcy/sH13dgURk4PtazrLzMMnRDwg2N5xyeozaUWNczJmC65OWA5W9wK60WcxHu4pHRR/p7x+jive/bui9w==}
engines: {node: '>=14.19.0'}
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
unplugin: 0.10.2
dev: true
/@vue-macros/named-template/0.0.4_4bxfdhtj34q3f6vjmpq7dc2fyi:
resolution: {integrity: sha512-Hq2QSfglJD73boibJvKXUyiQyI/Gd5bSIF74mzY1Ec1CXhoUO4nupZE+0Ue/qk8Fq5XjUXYDGd2c/SCckBlK9w==}
/@vue-macros/named-template/0.0.5_kfit4shdbw4ah6piqfzpu53piy:
resolution: {integrity: sha512-bZRUljNyvOOqeE9dyqXvKPQCLUCcPt1EkThmXqSbxagV29ohyviF8+CCs/8OdmNygLTBIChjP8DexQ3nUIFzUg==}
engines: {node: '>=14.19.0'}
dependencies:
'@rollup/pluginutils': 4.2.1
'@vitejs/plugin-vue': 3.2.0_vite@3.2.2+vue@3.2.41
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
'@vue/compiler-dom': 3.2.41
unplugin: 0.10.2
unplugin-combine: 0.2.8_rollup@2.79.1+vite@3.2.2
unplugin-combine: 0.2.8_rollup@3.2.5+vite@3.2.2
transitivePeerDependencies:
- esbuild
- rollup
@ -1449,14 +1463,14 @@ packages:
- webpack
dev: true
/@vue-macros/setup-component/0.12.6_rollup@2.79.1+vite@3.2.2:
resolution: {integrity: sha512-oYknsBrC13HzZI9xvFz51KeQ8eOzCuki8wpT7BFOub33d9gJuvynpmqLl45FHSw2AJo8UA6vaKi6ef74JMukRg==}
/@vue-macros/setup-component/0.12.7_rollup@3.2.5+vite@3.2.2:
resolution: {integrity: sha512-L0WkJgw0QDwZh4tOjjKIDR0DMIybiOunsaxVqkJjicTb2YaiRUSLq4Wadl8Ttrsd0IEfI51CSlg7Sx0/dKLrlQ==}
engines: {node: '>=14.19.0'}
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
unplugin: 0.10.2
unplugin-combine: 0.2.8_rollup@2.79.1+vite@3.2.2
unplugin-combine: 0.2.8_rollup@3.2.5+vite@3.2.2
transitivePeerDependencies:
- esbuild
- rollup
@ -1464,21 +1478,21 @@ packages:
- webpack
dev: true
/@vue-macros/setup-sfc/0.12.6:
resolution: {integrity: sha512-V7jXQ2sTTcxE4c+BTaWExd2kMi5w5qnJMjyzRPbQUTDf/KWNtPznTCxcmb9wVyJQRrrKfg61VYqpQ1jAtiDFoQ==}
/@vue-macros/setup-sfc/0.12.7:
resolution: {integrity: sha512-2RyeAEanl2MLeIoyfBKWSpJgXHjAazubF02hO7nbDCf2FgKqWB0Vu8NwyMZaQPNCWJuE4azLEodg8XBMJxyUKg==}
engines: {node: '>=14.19.0'}
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
unplugin: 0.10.2
dev: true
/@vue-macros/short-emits/0.12.7:
resolution: {integrity: sha512-CGy7Za/caP3WejNsHg4Si2bHeqxl2piv7Bf7TAMI/+948FuizQUFI8dRiv6dEQ8rGvegsU2BuTYH8JNDyzAK1A==}
/@vue-macros/short-emits/0.12.8:
resolution: {integrity: sha512-av/dqgPtU4EomWHHJPbGeuoYavCX2QTE0cd7Ka0dSuXuGgMhTN7feOv+sqsN8003yfOaPSBt6R+N/fy+T99GMA==}
engines: {node: '>=14.19.0'}
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
unplugin: 0.10.2
dev: true
@ -2951,8 +2965,8 @@ packages:
resolution: {integrity: sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ==}
dev: true
/element-plus/2.2.19_vue@3.2.41:
resolution: {integrity: sha512-uN0gt9lUus/IHzu5J6vkbYoYJgUtU05osdtFv9RO27bHKOG5GN7dH6uA3OKfkQQ6R2sV8ZxY1rc9PH1X8Dgrow==}
/element-plus/2.2.20_vue@3.2.41:
resolution: {integrity: sha512-ludShd3f5kNRY4FLzeoNitLcwZ4qs2M/zwKeyeE7rUzZJAQ0BZtcT3SvZoEoBLmgxw9jHoonl4WIwon4UzhyRA==}
peerDependencies:
vue: ^3.2.0
dependencies:
@ -3278,13 +3292,13 @@ packages:
engines: {node: '>=10'}
dev: true
/eslint-config-prettier/8.5.0_eslint@8.26.0:
/eslint-config-prettier/8.5.0_eslint@8.27.0:
resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
dependencies:
eslint: 8.26.0
eslint: 8.27.0
dev: true
/eslint-define-config/1.11.0:
@ -3292,7 +3306,7 @@ packages:
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13', pnpm: '>= 7.0.0'}
dev: true
/eslint-plugin-prettier/4.2.1_aniwkeyvlpmwkidetuytnokvcm:
/eslint-plugin-prettier/4.2.1_v7o5sx5x3wbs57ifz6wc4f76we:
resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==}
engines: {node: '>=12.0.0'}
peerDependencies:
@ -3303,25 +3317,25 @@ packages:
eslint-config-prettier:
optional: true
dependencies:
eslint: 8.26.0
eslint-config-prettier: 8.5.0_eslint@8.26.0
eslint: 8.27.0
eslint-config-prettier: 8.5.0_eslint@8.27.0
prettier: 2.7.1
prettier-linter-helpers: 1.0.0
dev: true
/eslint-plugin-vue/9.7.0_eslint@8.26.0:
/eslint-plugin-vue/9.7.0_eslint@8.27.0:
resolution: {integrity: sha512-DrOO3WZCZEwcLsnd3ohFwqCoipGRSTKTBTnLwdhqAbYZtzWl0o7D+D8ZhlmiZvABKTEl8AFsqH1GHGdybyoQmw==}
engines: {node: ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
dependencies:
eslint: 8.26.0
eslint-utils: 3.0.0_eslint@8.26.0
eslint: 8.27.0
eslint-utils: 3.0.0_eslint@8.27.0
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 6.0.10
semver: 7.3.8
vue-eslint-parser: 9.1.0_eslint@8.26.0
vue-eslint-parser: 9.1.0_eslint@8.27.0
xml-name-validator: 4.0.0
transitivePeerDependencies:
- supports-color
@ -3350,13 +3364,13 @@ packages:
eslint-visitor-keys: 1.3.0
dev: true
/eslint-utils/3.0.0_eslint@8.26.0:
/eslint-utils/3.0.0_eslint@8.27.0:
resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
peerDependencies:
eslint: '>=5'
dependencies:
eslint: 8.26.0
eslint: 8.27.0
eslint-visitor-keys: 2.1.0
dev: true
@ -3375,8 +3389,8 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/eslint/8.26.0:
resolution: {integrity: sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==}
/eslint/8.27.0:
resolution: {integrity: sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
@ -3391,7 +3405,7 @@ packages:
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.1.1
eslint-utils: 3.0.0_eslint@8.26.0
eslint-utils: 3.0.0_eslint@8.27.0
eslint-visitor-keys: 3.3.0
espree: 9.4.0
esquery: 1.4.0
@ -4471,6 +4485,11 @@ packages:
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
dev: true
/js-cookie/3.0.1:
resolution: {integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==}
engines: {node: '>=12'}
dev: false
/js-sdsl/4.1.5:
resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==}
dev: true
@ -4490,8 +4509,8 @@ packages:
argparse: 2.0.1
dev: true
/jsencrypt/3.3.0:
resolution: {integrity: sha512-OPfQMUU3pJ6KKTvKzr83jePHfyD1zWiWeLtRU9yIWjcbaRWm8MuXIu1/eQlDXPwDZrUjZy+9HVamS2Kmwrvz/Q==}
/jsencrypt/3.3.1:
resolution: {integrity: sha512-dVvV54GdFuJgmEKn+oBiaifDMen4p6o6j/lJh0OVMcouME8sST0bJ7bldIgKBQk4za0zyGn0/pm4vOznR25mLw==}
dev: false
/jsesc/2.5.2:
@ -4577,8 +4596,8 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/known-css-properties/0.25.0:
resolution: {integrity: sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==}
/known-css-properties/0.26.0:
resolution: {integrity: sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==}
dev: true
/kolorist/1.6.0:
@ -5940,6 +5959,14 @@ packages:
fsevents: 2.3.2
dev: true
/rollup/3.2.5:
resolution: {integrity: sha512-/Ha7HhVVofduy+RKWOQJrxe4Qb3xyZo+chcpYiD8SoQa4AG7llhupUtyfKSSrdBM2mWJjhM8wZwmbY23NmlIYw==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
fsevents: 2.3.2
dev: true
/run-async/2.4.1:
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
engines: {node: '>=0.12.0'}
@ -6325,7 +6352,7 @@ packages:
resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==}
dev: true
/stylelint-config-html/1.1.0_2wb2ag3ubchp7fdo72hd6rkeve:
/stylelint-config-html/1.1.0_a4i6jbpfaxelx4fvjhtlgvxx6i:
resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==}
engines: {node: ^12 || >=14}
peerDependencies:
@ -6333,48 +6360,48 @@ packages:
stylelint: '>=14.0.0'
dependencies:
postcss-html: 1.5.0
stylelint: 14.14.0
stylelint: 14.14.1
dev: true
/stylelint-config-prettier/9.0.3_stylelint@14.14.0:
/stylelint-config-prettier/9.0.3_stylelint@14.14.1:
resolution: {integrity: sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==}
engines: {node: '>= 12'}
hasBin: true
peerDependencies:
stylelint: '>=11.0.0'
dependencies:
stylelint: 14.14.0
stylelint: 14.14.1
dev: true
/stylelint-config-recommended/9.0.0_stylelint@14.14.0:
/stylelint-config-recommended/9.0.0_stylelint@14.14.1:
resolution: {integrity: sha512-9YQSrJq4NvvRuTbzDsWX3rrFOzOlYBmZP+o513BJN/yfEmGSr0AxdvrWs0P/ilSpVV/wisamAHu5XSk8Rcf4CQ==}
peerDependencies:
stylelint: ^14.10.0
dependencies:
stylelint: 14.14.0
stylelint: 14.14.1
dev: true
/stylelint-config-standard/29.0.0_stylelint@14.14.0:
/stylelint-config-standard/29.0.0_stylelint@14.14.1:
resolution: {integrity: sha512-uy8tZLbfq6ZrXy4JKu3W+7lYLgRQBxYTUUB88vPgQ+ZzAxdrvcaSUW9hOMNLYBnwH+9Kkj19M2DHdZ4gKwI7tg==}
peerDependencies:
stylelint: ^14.14.0
dependencies:
stylelint: 14.14.0
stylelint-config-recommended: 9.0.0_stylelint@14.14.0
stylelint: 14.14.1
stylelint-config-recommended: 9.0.0_stylelint@14.14.1
dev: true
/stylelint-order/5.0.0_stylelint@14.14.0:
/stylelint-order/5.0.0_stylelint@14.14.1:
resolution: {integrity: sha512-OWQ7pmicXufDw5BlRqzdz3fkGKJPgLyDwD1rFY3AIEfIH/LQY38Vu/85v8/up0I+VPiuGRwbc2Hg3zLAsJaiyw==}
peerDependencies:
stylelint: ^14.0.0
dependencies:
postcss: 8.4.18
postcss-sorting: 7.0.1_postcss@8.4.18
stylelint: 14.14.0
stylelint: 14.14.1
dev: true
/stylelint/14.14.0:
resolution: {integrity: sha512-yUI+4xXfPHVnueYddSQ/e1GuEA/2wVhWQbGj16AmWLtQJtn28lVxfS4b0CsWyVRPgd3Auzi0NXOthIEUhtQmmA==}
/stylelint/14.14.1:
resolution: {integrity: sha512-Jnftu+lSD8cSpcV/+Z2nfgfgFpTIS1FcujezXPngtoIQ6wtwutL22MsNE0dJuMiM1h1790g2qIjAyUZCMrX4sw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
hasBin: true
dependencies:
@ -6395,7 +6422,7 @@ packages:
import-lazy: 4.0.0
imurmurhash: 0.1.4
is-plain-object: 5.0.0
known-css-properties: 0.25.0
known-css-properties: 0.26.0
mathml-tag-names: 2.1.3
meow: 9.0.0
micromatch: 4.0.5
@ -6413,7 +6440,7 @@ packages:
style-search: 0.1.0
supports-hyperlinks: 2.3.0
svg-tags: 1.0.0
table: 6.8.0
table: 6.8.1
v8-compile-cache: 2.3.0
write-file-atomic: 4.0.2
transitivePeerDependencies:
@ -6497,8 +6524,8 @@ packages:
stable: 0.1.8
dev: true
/table/6.8.0:
resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==}
/table/6.8.1:
resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==}
engines: {node: '>=10.0.0'}
dependencies:
ajv: 8.11.0
@ -6730,7 +6757,7 @@ packages:
engines: {node: '>= 10.0.0'}
dev: true
/unplugin-combine/0.2.8_rollup@2.79.1+vite@3.2.2:
/unplugin-combine/0.2.8_rollup@3.2.5+vite@3.2.2:
resolution: {integrity: sha512-Z38AC/TEjXbVyZ5HjVqo+lADj0/dcfwWC0Z4y0LNhybJzJQwmcMxm+ZsqHY3faauj4YigmlRMdptR5JEW9RuLg==}
engines: {node: '>=14.19.0'}
peerDependencies:
@ -6749,40 +6776,41 @@ packages:
optional: true
dependencies:
'@antfu/utils': 0.6.0
rollup: 2.79.1
rollup: 3.2.5
unplugin: 0.10.2
vite: 3.2.2_less@4.1.3
dev: true
/unplugin-vue-define-options/0.12.6:
resolution: {integrity: sha512-uD4JTJVTDTRUTwLaT39VZkQ3gMlCba166oweU3jZJeIk8ku4Fh7N7UFazVzRch4THHFBVQ6dHvqvs4oPuCcJjg==}
/unplugin-vue-define-options/0.12.7:
resolution: {integrity: sha512-relFORVPLDs4dd3ogEti5YyAqQ62XEieRgLu/OrDIRZdSZQ942pydf9ilfLFiFwCUt+EES/2Xl4EERtgP1T/og==}
engines: {node: '>=14.19.0'}
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/common': 0.13.2
'@vue-macros/common': 0.13.3
ast-walker-scope: 0.3.0
unplugin: 0.10.2
dev: true
/unplugin-vue-macros/0.15.2_mbqnf624zbi2ta2juc2wzs2gvm:
resolution: {integrity: sha512-hFtUJRk9JE534S+mflAFh8QITOspACNCshVgn21NhdECRDAjyrp4xu+CzSIgKAob/xOgS7J2flLaOHSyqePSCQ==}
/unplugin-vue-macros/0.16.0_f6zolwli45a6dkgzq5zhbk2c7e:
resolution: {integrity: sha512-GdSJT+8Anc5zrGtZTvEnya2XVOneG0MEyyRrWWhVtn5xYEsUkrB0IxsHiT/mGjR5U+tjyQ/9sNL+FhhC54NFnQ==}
engines: {node: '>=14.19.0'}
peerDependencies:
vue: ^2.7.0 || ^3.2.25
dependencies:
'@rollup/pluginutils': 4.2.1
'@vue-macros/better-define': 0.0.3
'@vue-macros/define-model': 0.13.7_@vueuse+core@9.4.0
'@vue-macros/define-render': 0.13.7_vue@3.2.41
'@vue-macros/define-slots': 0.0.4_vue@3.2.41
'@vue-macros/hoist-static': 0.12.6
'@vue-macros/named-template': 0.0.4_4bxfdhtj34q3f6vjmpq7dc2fyi
'@vue-macros/setup-component': 0.12.6_rollup@2.79.1+vite@3.2.2
'@vue-macros/setup-sfc': 0.12.6
'@vue-macros/short-emits': 0.12.7
'@vue-macros/better-define': 0.0.4
'@vue-macros/define-model': 0.13.8_@vueuse+core@9.4.0
'@vue-macros/define-props': 0.0.1_vue@3.2.41
'@vue-macros/define-render': 0.13.8_vue@3.2.41
'@vue-macros/define-slots': 0.0.5_vue@3.2.41
'@vue-macros/hoist-static': 0.12.7
'@vue-macros/named-template': 0.0.5_kfit4shdbw4ah6piqfzpu53piy
'@vue-macros/setup-component': 0.12.7_rollup@3.2.5+vite@3.2.2
'@vue-macros/setup-sfc': 0.12.7
'@vue-macros/short-emits': 0.12.8
local-pkg: 0.4.2
unplugin-combine: 0.2.8_rollup@2.79.1+vite@3.2.2
unplugin-vue-define-options: 0.12.6
unplugin-combine: 0.2.8_rollup@3.2.5+vite@3.2.2
unplugin-vue-define-options: 0.12.7
vue: 3.2.41
transitivePeerDependencies:
- '@vueuse/core'
@ -6897,7 +6925,7 @@ packages:
- supports-color
dev: true
/vite-plugin-eslint/1.8.1_eslint@8.26.0+vite@3.2.2:
/vite-plugin-eslint/1.8.1_eslint@8.27.0+vite@3.2.2:
resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==}
peerDependencies:
eslint: '>=7'
@ -6905,7 +6933,7 @@ packages:
dependencies:
'@rollup/pluginutils': 4.2.1
'@types/eslint': 8.4.6
eslint: 8.26.0
eslint: 8.27.0
rollup: 2.79.1
vite: 3.2.2_less@4.1.3
dev: true
@ -7041,14 +7069,14 @@ packages:
dependencies:
vue: 3.2.41
/vue-eslint-parser/9.1.0_eslint@8.26.0:
/vue-eslint-parser/9.1.0_eslint@8.27.0:
resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==}
engines: {node: ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: '>=6.0.0'
dependencies:
debug: 4.3.4
eslint: 8.26.0
eslint: 8.27.0
eslint-scope: 7.1.1
eslint-visitor-keys: 3.3.0
espree: 9.4.0

View File

@ -1,6 +1,5 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import { FormVO } from './types'
const request = useAxios()
// 创建工作流的表单定义
export const createFormApi = async (data: FormVO) => {

View File

@ -1,6 +1,5 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import { LeaveVO } from './types'
const request = useAxios()
// 创建请假申请
export const createLeaveApi = async (data: LeaveVO) => {

View File

@ -1,6 +1,5 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import { ModelVO } from './types'
const request = useAxios()
export const getModelPageApi = async (params) => {
return await request.get({ url: '/bpm/model/page', params })

View File

@ -1,6 +1,5 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import { ProcessInstanceVO } from './types'
const request = useAxios()
export const getMyProcessInstancePageApi = async (params) => {
return await request.get({ url: '/bpm/process-instance/my-page', params })

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
export const getTodoTaskPage = async (params) => {
return await request.get({ url: '/bpm/task/todo-page', params })

View File

@ -1,6 +1,5 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import { TaskAssignVO } from './types'
const request = useAxios()
export const getTaskAssignRuleList = async (params) => {
return await request.get({ url: '/bpm/task-assign-rule/list', params })

View File

@ -1,6 +1,5 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import { UserGroupVO } from './types'
const request = useAxios()
// 创建用户组
export const createUserGroupApi = async (data: UserGroupVO) => {

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 查询列表API 访问日志
export const getApiAccessLogPageApi = (params) => {

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 查询列表API 访问日志
export const getApiErrorLogPageApi = (params) => {

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { CodegenUpdateReqVO, CodegenCreateListReqVO } from './types'
const request = useAxios()
// 查询列表代码生成表定义
export const getCodegenTablePageApi = (params) => {
return request.get({ url: '/infra/codegen/table/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { ConfigVO } from './types'
const request = useAxios()
// 查询参数列表
export const getConfigPageApi = (params) => {
return request.get({ url: '/infra/config/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { DataSourceConfigVO } from './types'
const request = useAxios()
// 查询数据源配置列表
export const getDataSourceConfigListApi = () => {
return request.get({ url: '/infra/data-source-config/list' })

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 导出Html
export const exportHtmlApi = () => {

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { FileConfigVO } from './types'
const request = useAxios()
// 查询文件配置列表
export const getFileConfigPageApi = (params) => {
return request.get({ url: '/infra/file-config/page', params })

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 查询文件列表
export const getFilePageApi = (params) => {

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { JobVO } from './types'
const request = useAxios()
// 任务列表
export const getJobPageApi = (params) => {
return request.get({ url: '/infra/job/page', params })

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 任务日志列表
export const getJobLogPageApi = (params) => {

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
/**
* redis

View File

@ -1,9 +1,7 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import { getRefreshToken } from '@/utils/auth'
import type { UserLoginVO } from './types'
const request = useAxios()
export interface CodeImgResult {
captchaOnOff: boolean
img: string

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { AppVO } from './types'
const request = useAxios()
// 查询列表支付应用
export const getAppPageApi = (params) => {
return request.get({ url: '/pay/app/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { ChannelVO } from './types'
const request = useAxios()
// 查询列表支付渠道
export const getChannelPageApi = (params) => {
return request.get({ url: '/pay/channel/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { MerchantVO } from './types'
const request = useAxios()
// 查询列表支付商户
export const getMerchantPageApi = (params) => {
return request.get({ url: '/pay/merchant/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { OrderVO } from './types'
const request = useAxios()
// 查询列表支付订单
export const getOrderPageApi = async (params) => {
return await request.get({ url: '/pay/order/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { RefundVO } from './types'
const request = useAxios()
// 查询列表退款订单
export const getRefundPageApi = (params) => {
return request.get({ url: '/pay/refund/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { DeptVO, DeptListReqVO } from './types'
const request = useAxios()
// 查询部门(精简)列表
export const listSimpleDeptApi = () => {
return request.get({ url: '/system/dept/list-all-simple' })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { DictDataVO, DictDataPageReqVO, DictDataExportReqVO } from './types'
const request = useAxios()
// 查询字典数据(精简)列表
export const listSimpleDictDataApi = () => {
return request.get({ url: '/system/dict-data/list-all-simple' })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { DictTypeVO, DictTypePageReqVO, DictTypeExportReqVO } from './types'
const request = useAxios()
// 查询字典(精简)列表
export const listSimpleDictTypeApi = () => {
return request.get({ url: '/system/dict-type/list-all-simple' })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { ErrorCodeVO } from './types'
const request = useAxios()
// 查询错误码列表
export const getErrorCodePageApi = (params) => {
return request.get({ url: '/system/error-code/page', params })

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 查询登录日志列表
export const getLoginLogPageApi = (params) => {

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { MenuVO } from './types'
const request = useAxios()
// 查询菜单(精简)列表
export const listSimpleMenusApi = () => {
return request.get({ url: '/system/menu/list-all-simple' })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { NoticeVO } from './types'
const request = useAxios()
// 查询公告列表
export const getNoticePageApi = (params) => {
return request.get({ url: '/system/notice/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import { OAuth2ClientVo } from './client.types'
const request = useAxios()
// 查询 OAuth2列表
export const getOAuth2ClientPageApi = (params) => {
return request.get({ url: '/system/oauth2-client/page', params })

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 查询 token列表
export const getAccessTokenPageApi = (params) => {

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 查询操作日志列表
export const getOperateLogPageApi = (params) => {

View File

@ -1,12 +1,10 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type {
PermissionAssignRoleDataScopeReqVO,
PermissionAssignRoleMenuReqVO,
PermissionAssignUserRoleReqVO
} from './types'
const request = useAxios()
// 查询角色拥有的菜单权限
export const listRoleMenusApi = async (roleId: number) => {
return await request.get({ url: '/system/permission/list-role-resources?roleId=' + roleId })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { PostVO, PostPageReqVO, PostExportReqVO } from './types'
const request = useAxios()
// 查询岗位列表
export const getPostPageApi = async (params: PostPageReqVO) => {
return await request.get({ url: '/system/post/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { RoleVO } from './types'
const request = useAxios()
// 查询角色列表
export const getRolePageApi = async (params) => {
return await request.get({ url: '/system/role/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { SensitiveWordVO } from './types'
const request = useAxios()
// 查询敏感词列表
export const getSensitiveWordPageApi = (params) => {
return request.get({ url: '/system/sensitive-word/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { SmsChannelVO } from './types'
const request = useAxios()
// 查询短信渠道列表
export const getSmsChannelPageApi = (params) => {
return request.get({ url: '/system/sms-channel/page', params })

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 查询短信日志列表
export const getSmsLogPageApi = (params) => {

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { SmsTemplateVO } from './types'
const request = useAxios()
// 查询短信模板列表
export const getSmsTemplatePageApi = (params) => {
return request.get({ url: '/system/sms-template/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { TenantVO } from './types'
const request = useAxios()
// 查询租户列表
export const getTenantPageApi = (params) => {
return request.get({ url: '/system/tenant/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { TenantPackageVO } from './types'
const request = useAxios()
// 查询租户套餐列表
export const getTenantPackageTypePageApi = (params) => {
return request.get({ url: '/system/tenant-package/page', params })

View File

@ -1,8 +1,6 @@
import { useAxios } from '@/hooks/web/useAxios'
import request from '@/config/axios'
import type { UserVO } from './types'
const request = useAxios()
// 查询用户管理列表
export const getUserPageApi = (params) => {
return request.get({ url: '/system/user/page', params })

View File

@ -1,6 +1,4 @@
import { useAxios } from '@/hooks/web/useAxios'
const request = useAxios()
import request from '@/config/axios'
// 查询用户个人信息
export const getUserProfileApi = () => {

View File

@ -0,0 +1,3 @@
import XButton from './src/XButton.vue'
export { XButton }

View File

@ -0,0 +1,37 @@
<script setup lang="ts">
import { propTypes } from '@/utils/propTypes'
import { computed, useAttrs, PropType } from 'vue'
const props = defineProps({
modelValue: propTypes.bool.def(false),
loading: propTypes.bool.def(false),
preIcon: propTypes.string.def(''),
postIcon: propTypes.string.def(''),
title: propTypes.string.def('按钮'),
type: propTypes.oneOf(['', 'primary', 'success', 'warning', 'danger', 'info']).def(''),
link: propTypes.bool.def(false),
circle: propTypes.bool.def(false),
round: propTypes.bool.def(false),
plain: propTypes.bool.def(false),
onClick: { type: Function as PropType<(...args) => any>, default: null }
})
const getBindValue = computed(() => {
const delArr: string[] = ['title', 'preIcon', 'postIcon', 'onClick']
const attrs = useAttrs()
const obj = { ...attrs, ...props }
for (const key in obj) {
if (delArr.indexOf(key) !== -1) {
delete obj[key]
}
}
return obj
})
</script>
<template>
<el-button v-bind="getBindValue" @click="onClick">
<Icon :icon="preIcon" v-if="preIcon" class="mr-1px" />
{{ title }}
<Icon :icon="postIcon" v-if="postIcon" class="mr-1px" />
</el-button>
</template>

View File

@ -0,0 +1,3 @@
import XModal from './src/XModal.vue'
export { XModal }

View File

@ -0,0 +1,42 @@
<script setup lang="ts">
import { propTypes } from '@/utils/propTypes'
import { computed, useAttrs, useSlots } from 'vue'
const slots = useSlots()
const props = defineProps({
id: propTypes.string.def('model_1'),
modelValue: propTypes.bool.def(false),
fullscreen: propTypes.bool.def(false),
loading: propTypes.bool.def(false),
title: propTypes.string.def('弹窗'),
width: propTypes.string.def('800'),
height: propTypes.string.def('480'),
minWidth: propTypes.string.def('460'),
minHeight: propTypes.string.def('320'),
showFooter: propTypes.bool.def(true)
})
const getBindValue = computed(() => {
const attrs = useAttrs()
const obj = { ...attrs, ...props }
return obj
})
</script>
<template>
<vxe-modal v-bind="getBindValue" destroy-on-close show-zoom resize transfer>
<template v-if="slots.header" #header>
<slot name="header"></slot>
</template>
<template v-if="slots.default" #default>
<slot name="default"></slot>
</template>
<template v-if="slots.corner" #corner>
<slot name="corner"></slot>
</template>
<template v-if="slots.footer" #footer>
<slot name="footer"></slot>
</template>
</vxe-modal>
</template>

View File

@ -4,6 +4,8 @@ import { Form } from '@/components/Form'
import { Table } from '@/components/Table'
import { Search } from '@/components/Search'
import { Dialog } from '@/components/Dialog'
import { XModal } from '@/components/XModal'
import { XButton } from '@/components/XButton'
import { DictTag } from '@/components/DictTag'
import { ContentWrap } from '@/components/ContentWrap'
import { Descriptions } from '@/components/Descriptions'
@ -14,6 +16,8 @@ export const setupGlobCom = (app: App<Element>): void => {
app.component('Table', Table)
app.component('Search', Search)
app.component('Dialog', Dialog)
app.component('XModal', XModal)
app.component('XButton', XButton)
app.component('DictTag', DictTag)
app.component('ContentWrap', ContentWrap)
app.component('Descriptions', Descriptions)

View File

@ -1,229 +1,46 @@
import axios, {
AxiosInstance,
AxiosRequestConfig,
AxiosRequestHeaders,
AxiosResponse,
AxiosError
} from 'axios'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import qs from 'qs'
import { config } from '@/config/axios/config'
import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
import errorCode from './errorCode'
import { useI18n } from '@/hooks/web/useI18n'
import { resetRouter } from '@/router'
import { useCache } from '@/hooks/web/useCache'
import { service } from './service'
const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE
const { result_code, base_url, request_timeout } = config
import { config } from './config'
// 需要忽略的提示。忽略后,自动 Promise.reject('error')
const ignoreMsgs = [
'无效的刷新令牌', // 刷新令牌被删除时,不用提示
'刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401无法跳转到登出界面
]
// 是否显示重新登录
export const isRelogin = { show: false }
// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
// 请求队列
let requestList: any[] = []
// 是否正在刷新中
let isRefreshToken = false
const { default_headers } = config
// 创建axios实例
const service: AxiosInstance = axios.create({
baseURL: base_url, // api 的 base_url
timeout: request_timeout, // 请求超时时间
withCredentials: false // 禁用 Cookie 等信息
})
// request拦截器
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
// 是否需要设置 token
const isToken = (config!.headers || {}).isToken === false
if (getAccessToken() && !isToken) {
;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
const request = (option: any) => {
const { url, method, params, data, headersType, responseType } = option
return service({
url: url,
method,
params,
data,
responseType: responseType,
headers: {
'Content-Type': headersType || default_headers
}
// 设置租户
if (tenantEnable) {
const tenantId = getTenantId()
if (tenantId) (config as Recordable).headers.common['tenant-id'] = tenantId
}
const params = config.params || {}
const data = config.data || false
if (
config.method?.toUpperCase() === 'POST' &&
(config.headers as AxiosRequestHeaders)['Content-Type'] ===
'application/x-www-form-urlencoded'
) {
config.data = qs.stringify(data)
}
// get参数编码
if (config.method?.toUpperCase() === 'GET' && params) {
let url = config.url + '?'
for (const propName of Object.keys(params)) {
const value = params[propName]
if (value !== void 0 && value !== null && typeof value !== 'undefined') {
if (typeof value === 'object') {
for (const val of Object.keys(value)) {
const params = propName + '[' + val + ']'
const subPart = encodeURIComponent(params) + '='
url += subPart + encodeURIComponent(value[val]) + '&'
}
} else {
url += `${propName}=${encodeURIComponent(value)}&`
}
}
}
// 给 get 请求加上时间戳参数,避免从缓存中拿数据
// const now = new Date().getTime()
// params = params.substring(0, url.length - 1) + `?_t=${now}`
url = url.slice(0, -1)
config.params = {}
config.url = url
}
return config
},
(error: AxiosError) => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// response 拦截器
service.interceptors.response.use(
async (response: AxiosResponse<Recordable>) => {
const { data } = response
const config = response.config
if (!data) {
// 返回“[HTTP]请求没有返回值”;
throw new Error()
}
const { t } = useI18n()
// 未设置状态码则默认成功状态
const code = data.code || result_code
// 二进制数据则直接返回
if (
response.request.responseType === 'blob' ||
response.request.responseType === 'arraybuffer'
) {
return response.data
}
// 获取错误信息
const msg = data.msg || errorCode[code] || errorCode['default']
if (ignoreMsgs.indexOf(msg) !== -1) {
// 如果是忽略的错误码,直接返回 msg 异常
return Promise.reject(msg)
} else if (code === 401) {
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
if (!isRefreshToken) {
isRefreshToken = true
// 1. 如果获取不到刷新令牌,则只能执行登出操作
if (!getRefreshToken()) {
return handleAuthorized()
}
// 2. 进行刷新访问令牌
try {
const refreshTokenRes = await refreshToken()
// 2.1 刷新成功,则回放队列的请求 + 当前请求
setToken(refreshTokenRes.data.data)
config.headers!.Authorization = 'Bearer ' + getAccessToken()
requestList.forEach((cb: any) => {
cb()
})
requestList = []
return service(config)
} catch (e) {
// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
// 2.2 刷新失败,只回放队列的请求
requestList.forEach((cb: any) => {
cb()
})
// 提示是否要登出。即不回放当前请求!不然会形成递归
return handleAuthorized()
} finally {
requestList = []
isRefreshToken = false
}
} else {
// 添加到队列,等待刷新获取到新的令牌
return new Promise((resolve) => {
requestList.push(() => {
config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
resolve(service(config))
})
})
}
} else if (code === 500) {
ElMessage.error(t('sys.api.errMsg500'))
return Promise.reject(new Error(msg))
} else if (code === 901) {
ElMessage.error(
'<div>' +
t('sys.api.errMsg901') +
'</div>' +
'<div> &nbsp; </div>' +
'<div>参考 https://doc.iocoder.cn/ 教程</div>' +
'<div> &nbsp; </div>' +
'<div>5 分钟搭建本地环境</div>'
)
return Promise.reject(new Error(msg))
} else if (code !== 200) {
if (msg === '无效的刷新令牌') {
// hard coding忽略这个提示直接登出
console.log(msg)
} else {
ElNotification.error({
title: msg
})
}
return Promise.reject('error')
} else {
return data
}
},
(error: AxiosError) => {
console.log('err' + error) // for debug
let { message } = error
const { t } = useI18n()
if (message === 'Network Error') {
message = t('sys.api.errorMessage')
} else if (message.includes('timeout')) {
message = t('sys.api.apiTimeoutMessage')
} else if (message.includes('Request failed with status code')) {
message = t('sys.api.apiRequestFailed') + message.substr(message.length - 3)
}
ElMessage.error(message)
return Promise.reject(error)
}
)
const refreshToken = async () => {
return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
})
}
const handleAuthorized = () => {
const { t } = useI18n()
if (!isRelogin.show) {
isRelogin.show = true
ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
confirmButtonText: t('login.relogin'),
cancelButtonText: t('common.cancel'),
type: 'warning'
})
.then(() => {
const { wsCache } = useCache()
resetRouter() // 重置静态路由表
wsCache.clear()
removeToken()
isRelogin.show = false
window.location.href = '/'
})
.catch(() => {
isRelogin.show = false
})
export default {
get: async <T = any>(option: any) => {
const res = await request({ method: 'GET', ...option })
return res.data as unknown as T
},
post: async <T = any>(option: any) => {
const res = await request({ method: 'POST', ...option })
return res.data as unknown as T
},
delete: async <T = any>(option: any) => {
const res = await request({ method: 'DELETE', ...option })
return res.data as unknown as T
},
put: async <T = any>(option: any) => {
const res = await request({ method: 'PUT', ...option })
return res.data as unknown as T
},
download: async <T = any>(option: any) => {
const res = await request({ method: 'GET', responseType: 'blob', ...option })
return res as unknown as Promise<T>
},
upload: async <T = any>(option: any) => {
option.headersType = 'multipart/form-data'
const res = await request({ method: 'PUT', ...option })
return res as unknown as Promise<T>
}
return Promise.reject(t('sys.api.timeoutMessage'))
}
export { service }

View File

@ -0,0 +1,229 @@
import axios, {
AxiosInstance,
AxiosRequestConfig,
AxiosRequestHeaders,
AxiosResponse,
AxiosError
} from 'axios'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import qs from 'qs'
import { config } from '@/config/axios/config'
import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
import errorCode from './errorCode'
import { useI18n } from '@/hooks/web/useI18n'
import { resetRouter } from '@/router'
import { useCache } from '@/hooks/web/useCache'
const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE
const { result_code, base_url, request_timeout } = config
// 需要忽略的提示。忽略后,自动 Promise.reject('error')
const ignoreMsgs = [
'无效的刷新令牌', // 刷新令牌被删除时,不用提示
'刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401无法跳转到登出界面
]
// 是否显示重新登录
export const isRelogin = { show: false }
// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
// 请求队列
let requestList: any[] = []
// 是否正在刷新中
let isRefreshToken = false
// 创建axios实例
const service: AxiosInstance = axios.create({
baseURL: base_url, // api 的 base_url
timeout: request_timeout, // 请求超时时间
withCredentials: false // 禁用 Cookie 等信息
})
// request拦截器
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
// 是否需要设置 token
const isToken = (config!.headers || {}).isToken === false
if (getAccessToken() && !isToken) {
;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
}
// 设置租户
if (tenantEnable) {
const tenantId = getTenantId()
if (tenantId) (config as Recordable).headers.common['tenant-id'] = tenantId
}
const params = config.params || {}
const data = config.data || false
if (
config.method?.toUpperCase() === 'POST' &&
(config.headers as AxiosRequestHeaders)['Content-Type'] ===
'application/x-www-form-urlencoded'
) {
config.data = qs.stringify(data)
}
// get参数编码
if (config.method?.toUpperCase() === 'GET' && params) {
let url = config.url + '?'
for (const propName of Object.keys(params)) {
const value = params[propName]
if (value !== void 0 && value !== null && typeof value !== 'undefined') {
if (typeof value === 'object') {
for (const val of Object.keys(value)) {
const params = propName + '[' + val + ']'
const subPart = encodeURIComponent(params) + '='
url += subPart + encodeURIComponent(value[val]) + '&'
}
} else {
url += `${propName}=${encodeURIComponent(value)}&`
}
}
}
// 给 get 请求加上时间戳参数,避免从缓存中拿数据
// const now = new Date().getTime()
// params = params.substring(0, url.length - 1) + `?_t=${now}`
url = url.slice(0, -1)
config.params = {}
config.url = url
}
return config
},
(error: AxiosError) => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// response 拦截器
service.interceptors.response.use(
async (response: AxiosResponse<Recordable>) => {
const { data } = response
const config = response.config
if (!data) {
// 返回“[HTTP]请求没有返回值”;
throw new Error()
}
const { t } = useI18n()
// 未设置状态码则默认成功状态
const code = data.code || result_code
// 二进制数据则直接返回
if (
response.request.responseType === 'blob' ||
response.request.responseType === 'arraybuffer'
) {
return response.data
}
// 获取错误信息
const msg = data.msg || errorCode[code] || errorCode['default']
if (ignoreMsgs.indexOf(msg) !== -1) {
// 如果是忽略的错误码,直接返回 msg 异常
return Promise.reject(msg)
} else if (code === 401) {
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
if (!isRefreshToken) {
isRefreshToken = true
// 1. 如果获取不到刷新令牌,则只能执行登出操作
if (!getRefreshToken()) {
return handleAuthorized()
}
// 2. 进行刷新访问令牌
try {
const refreshTokenRes = await refreshToken()
// 2.1 刷新成功,则回放队列的请求 + 当前请求
setToken(refreshTokenRes.data.data)
config.headers!.Authorization = 'Bearer ' + getAccessToken()
requestList.forEach((cb: any) => {
cb()
})
requestList = []
return service(config)
} catch (e) {
// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
// 2.2 刷新失败,只回放队列的请求
requestList.forEach((cb: any) => {
cb()
})
// 提示是否要登出。即不回放当前请求!不然会形成递归
return handleAuthorized()
} finally {
requestList = []
isRefreshToken = false
}
} else {
// 添加到队列,等待刷新获取到新的令牌
return new Promise((resolve) => {
requestList.push(() => {
config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
resolve(service(config))
})
})
}
} else if (code === 500) {
ElMessage.error(t('sys.api.errMsg500'))
return Promise.reject(new Error(msg))
} else if (code === 901) {
ElMessage.error(
'<div>' +
t('sys.api.errMsg901') +
'</div>' +
'<div> &nbsp; </div>' +
'<div>参考 https://doc.iocoder.cn/ 教程</div>' +
'<div> &nbsp; </div>' +
'<div>5 分钟搭建本地环境</div>'
)
return Promise.reject(new Error(msg))
} else if (code !== 200) {
if (msg === '无效的刷新令牌') {
// hard coding忽略这个提示直接登出
console.log(msg)
} else {
ElNotification.error({
title: msg
})
}
return Promise.reject('error')
} else {
return data
}
},
(error: AxiosError) => {
console.log('err' + error) // for debug
let { message } = error
const { t } = useI18n()
if (message === 'Network Error') {
message = t('sys.api.errorMessage')
} else if (message.includes('timeout')) {
message = t('sys.api.apiTimeoutMessage')
} else if (message.includes('Request failed with status code')) {
message = t('sys.api.apiRequestFailed') + message.substr(message.length - 3)
}
ElMessage.error(message)
return Promise.reject(error)
}
)
const refreshToken = async () => {
return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
}
const handleAuthorized = () => {
const { t } = useI18n()
if (!isRelogin.show) {
isRelogin.show = true
ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
confirmButtonText: t('login.relogin'),
cancelButtonText: t('common.cancel'),
type: 'warning'
})
.then(() => {
const { wsCache } = useCache()
resetRouter() // 重置静态路由表
wsCache.clear()
removeToken()
isRelogin.show = false
window.location.href = '/'
})
.catch(() => {
isRelogin.show = false
})
}
return Promise.reject(t('sys.api.timeoutMessage'))
}
export { service }

View File

@ -1,60 +0,0 @@
import { service } from '@/config/axios'
import { config } from '@/config/axios/config'
const { default_headers } = config
const request = (option: AxiosConfig) => {
const { url, method, params, data, headersType, responseType } = option
return service({
url: url,
method,
params,
data,
responseType: responseType,
headers: {
'Content-Type': headersType || default_headers
}
})
}
async function getFn<T = any>(option: AxiosConfig): Promise<T> {
const res = await request({ method: 'GET', ...option })
return res.data
}
async function postFn<T = any>(option: AxiosConfig): Promise<T> {
const res = await request({ method: 'POST', ...option })
return res.data
}
async function deleteFn<T = any>(option: AxiosConfig): Promise<T> {
const res = await request({ method: 'DELETE', ...option })
return res.data
}
async function putFn<T = any>(option: AxiosConfig): Promise<T> {
const res = await request({ method: 'PUT', ...option })
return res.data
}
async function downloadFn<T = any>(option: AxiosConfig): Promise<T> {
const res = await request({ method: 'GET', responseType: 'blob', ...option })
return res as unknown as Promise<T>
}
async function uploadFn<T = any>(option: AxiosConfig): Promise<T> {
option.headersType = 'multipart/form-data'
const res = await request({ method: 'PUT', ...option })
return res as unknown as Promise<T>
}
export const useAxios = () => {
return {
get: getFn,
post: postFn,
delete: deleteFn,
put: putFn,
download: downloadFn,
upload: uploadFn
}
}

View File

@ -0,0 +1,246 @@
import { DescriptionsSchema } from '@/types/descriptions'
import { getIntDictOptions } from '@/utils/dict'
import { reactive } from 'vue'
import {
FormItemRenderOptions,
VxeFormItemProps,
VxeGridPropTypes,
VxeTableDefines
} from 'vxe-table'
import { eachTree } from 'xe-utils'
import { useI18n } from '@/hooks/web/useI18n'
import { VxeTableColumn } from '@/types/table'
export type VxeCrudSchema = Omit<VxeTableColumn, 'children'> & {
field: string
title?: string
search?: CrudSearchParams
table?: CrudTableParams
form?: CrudFormParams
detail?: CrudDescriptionsParams
print?: boolean
children?: VxeCrudSchema[]
dictType?: string
}
type CrudSearchParams = {
// 是否显示在查询项
show?: boolean
} & Omit<VxeFormItemProps, 'field'>
type CrudTableParams = {
// 是否显示表头
show?: boolean
} & Omit<VxeTableDefines.ColumnOptions, 'field'>
type CrudFormParams = {
// 是否显示表单项
show?: boolean
} & Omit<VxeFormItemProps, 'field'>
type CrudDescriptionsParams = {
// 是否显示表单项
show?: boolean
} & Omit<DescriptionsSchema, 'field'>
interface VxeAllSchemas {
searchSchema: VxeFormItemProps[]
tableSchema: VxeGridPropTypes.Columns
formSchema: VxeFormItemProps[]
detailSchema: DescriptionsSchema[]
printSchema: VxeTableDefines.ColumnInfo[]
}
// 过滤所有结构
export const useVxeCrudSchemas = (
crudSchema: VxeCrudSchema[]
): {
allSchemas: VxeAllSchemas
} => {
// 所有结构数据
const allSchemas = reactive<VxeAllSchemas>({
searchSchema: [],
tableSchema: [],
formSchema: [],
detailSchema: [],
printSchema: []
})
const searchSchema = filterSearchSchema(crudSchema)
allSchemas.searchSchema = searchSchema || []
const tableSchema = filterTableSchema(crudSchema)
allSchemas.tableSchema = tableSchema || []
const formSchema = filterFormSchema(crudSchema)
allSchemas.formSchema = formSchema
const detailSchema = filterDescriptionsSchema(crudSchema)
allSchemas.detailSchema = detailSchema
const printSchema = filterPrintSchema(crudSchema)
allSchemas.printSchema = printSchema
return {
allSchemas
}
}
// 过滤 Search 结构
const filterSearchSchema = (crudSchema: VxeCrudSchema[]): VxeFormItemProps[] => {
const searchSchema: VxeFormItemProps[] = []
const { t } = useI18n()
eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
// 判断是否显示
if (schemaItem?.search?.show) {
let itemRenderName = schemaItem?.search?.itemRender?.name || '$input'
const options: any[] = []
let itemRender: FormItemRenderOptions = {
name: itemRenderName,
props: { placeholder: t('common.inputText') }
}
if (schemaItem.dictType) {
const allOptions = { label: '全部', value: '' }
options.push(allOptions)
getIntDictOptions(schemaItem.dictType).forEach((dict) => {
options.push(dict)
})
itemRender.options = options
if (!schemaItem.search.itemRender?.name) itemRenderName = '$select'
itemRender = {
name: itemRenderName,
options: options,
props: { placeholder: t('common.selectText') }
}
}
const searchSchemaItem = {
// 默认为 input
span: 6,
itemRender: itemRender,
...schemaItem.search,
field: schemaItem.field,
title: schemaItem.search?.title || schemaItem.title
}
// 删除不必要的字段
delete searchSchemaItem.show
searchSchema.push(searchSchemaItem)
}
})
// 添加搜索按钮
const buttons: VxeFormItemProps = {
span: 24,
align: 'center',
collapseNode: true,
itemRender: {
name: '$buttons',
children: [
{ props: { type: 'submit', content: t('common.query'), status: 'primary' } },
{ props: { type: 'reset', content: t('common.reset') } }
]
}
}
searchSchema.push(buttons)
return searchSchema
}
// 过滤 table 结构
const filterTableSchema = (crudSchema: VxeCrudSchema[]): VxeGridPropTypes.Columns => {
const tableSchema: VxeGridPropTypes.Columns = []
eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
// 判断是否显示
if (schemaItem?.table?.show !== false) {
const tableSchemaItem = {
...schemaItem.table,
field: schemaItem.field,
title: schemaItem.table?.title || schemaItem.title
}
// 删除不必要的字段
delete tableSchemaItem.show
tableSchema.push(tableSchemaItem)
}
})
return tableSchema
}
// 过滤 form 结构
const filterFormSchema = (crudSchema: VxeCrudSchema[]): VxeFormItemProps[] => {
const formSchema: VxeFormItemProps[] = []
const { t } = useI18n()
eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
// 判断是否显示
if (schemaItem?.form?.show !== false) {
let itemRenderName = schemaItem?.form?.itemRender?.name || '$input'
let itemRender: FormItemRenderOptions = {
name: itemRenderName,
props: { placeholder: t('common.inputText') }
}
if (schemaItem.dictType) {
if (!(schemaItem.form && schemaItem.form.itemRender?.name)) itemRenderName = '$select'
itemRender = {
name: itemRenderName,
options: getIntDictOptions(schemaItem.dictType),
props: { placeholder: t('common.selectText') }
}
}
const formSchemaItem = {
// 默认为 input
itemRender: itemRender,
...schemaItem.form,
span: schemaItem.form?.span || 12,
field: schemaItem.field,
title: schemaItem.form?.title || schemaItem.title
}
// 删除不必要的字段
delete formSchemaItem.show
formSchema.push(formSchemaItem)
}
})
return formSchema
}
// 过滤 descriptions 结构
const filterDescriptionsSchema = (crudSchema: VxeCrudSchema[]): DescriptionsSchema[] => {
const descriptionsSchema: DescriptionsSchema[] = []
eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
// 判断是否显示
if (schemaItem?.detail?.show !== false) {
const descriptionsSchemaItem = {
...schemaItem.detail,
field: schemaItem.field,
label: schemaItem.detail?.label || schemaItem.title
}
// 删除不必要的字段
delete descriptionsSchemaItem.show
descriptionsSchema.push(descriptionsSchemaItem)
}
})
return descriptionsSchema
}
// 过滤 打印 结构
const filterPrintSchema = (crudSchema: VxeCrudSchema[]): any[] => {
const printSchema: any[] = []
eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
// 判断是否显示
if (schemaItem?.detail?.show !== false) {
const printSchemaItem = {
field: schemaItem.field
}
printSchema.push(printSchemaItem)
}
})
return printSchema
}

View File

@ -0,0 +1,60 @@
import { reactive } from 'vue'
import { VxeGridProps } from 'vxe-table'
export const useVxeGrid = (allSchemas, getPageApi) => {
const gridOptions = reactive<VxeGridProps>({
loading: false,
height: 800,
rowConfig: {
keyField: 'id',
isHover: true
},
toolbarConfig: {
custom: true,
slots: { buttons: 'toolbar_buttons' }
},
printConfig: {
columns: allSchemas.printSchema
},
formConfig: {
titleWidth: 100,
titleAlign: 'right',
items: allSchemas.searchSchema
},
columns: allSchemas.tableSchema,
pagerConfig: {
border: false,
background: false,
perfect: true,
pageSize: 10,
pagerCount: 7,
pageSizes: [5, 10, 15, 20, 50, 100, 200, 500],
layouts: [
'PrevJump',
'PrevPage',
'Jump',
'PageCount',
'NextPage',
'NextJump',
'Sizes',
'Total'
]
},
proxyConfig: {
seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号)
form: true, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为
props: { result: 'list', total: 'total' },
ajax: {
query: ({ page, form }) => {
const queryParams = Object.assign({}, form)
queryParams.pageSize = page.pageSize
queryParams.pageNo = page.currentPage
return new Promise(async (resolve) => {
resolve(await getPageApi(queryParams))
})
}
}
}
})
return gridOptions
}

View File

@ -60,7 +60,14 @@ VXETable.setup({
autoResize: true, // 自动监听父元素的变化去重新计算表格
resizable: true, // 列是否允许拖动列宽调整大小
emptyText: '暂无数据', // 空表单
highlightHoverRow: true // 自动监听父元素的变化去重新计算表格
highlightHoverRow: true, // 自动监听父元素的变化去重新计算表格
treeConfig: {
rowField: 'id',
parentField: 'parentId',
children: 'children',
indent: 20,
showIcon: true
}
},
grid: {
toolbarConfig: {
@ -107,8 +114,10 @@ VXETable.setup({
titleColon: true // 是否显示标题冒号
},
modal: {
width: 600, // 窗口的宽度
height: 400, // 窗口的高度
width: 800, // 窗口的宽度
height: 600, // 窗口的高度
minWidth: 460,
minHeight: 320,
showZoom: true, // 标题是否标显示最大化与还原按钮
resize: true, // 是否允许窗口边缘拖动调整窗口大小
marginSize: 0, // 只对 resize 启用后有效,用于设置可拖动界限范围,如果为负数则允许拖动超出屏幕边界
@ -127,13 +136,28 @@ VXETable.setup({
: XEUtils.toFormatString(XEUtils.get(enUS, key), args)
}
})
// 格式金额默认2位数
VXETable.formats.add('formatAmount', ({ cellValue }, digits = 2) => {
return XEUtils.commafy(XEUtils.toNumber(cellValue), { digits })
})
// 格式日期,默认 yyyy-MM-dd HH:mm:ss
VXETable.formats.add('formatDate', ({ cellValue }, format = 'yyyy-MM-dd HH:mm:ss') => {
return XEUtils.toDateString(cellValue, format)
// 自定义全局的格式化处理函数
VXETable.formats.mixin({
// 格式日期,默认 yyyy-MM-dd HH:mm:ss
formatDate({ cellValue }, format) {
return XEUtils.toDateString(cellValue, format || 'yyyy-MM-dd HH:mm:ss')
},
// 四舍五入金额每隔3位逗号分隔默认2位数
formatAmount({ cellValue }, digits = 2) {
return XEUtils.commafy(Number(cellValue), { digits })
},
// 格式化银行卡默认每4位空格隔开
formatBankcard({ cellValue }) {
return XEUtils.commafy(XEUtils.toValueString(cellValue), { spaceNumber: 4, separator: ' ' })
},
// 四舍五入,默认两位数
formatFixedNumber({ cellValue }, digits = 2) {
return XEUtils.toFixed(XEUtils.round(cellValue, digits), digits)
},
// 向下舍入,默认两位数
formatCutNumber({ cellValue }, digits = 2) {
return XEUtils.toFixed(XEUtils.floor(cellValue, digits), digits)
}
})
export const setupVxeTable = (app: App<Element>) => {
// 表格功能

View File

@ -10,7 +10,7 @@ import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { useDictStoreWithOut } from '@/store/modules/dict'
import { useUserStoreWithOut } from '@/store/modules/user'
import { listSimpleDictDataApi } from '@/api/system/dict/dict.data'
import { isRelogin } from '@/config/axios'
import { isRelogin } from '@/config/axios/service'
import { getInfoApi } from '@/api/login'
const { start, done } = useNProgress()

View File

@ -4,6 +4,12 @@ export type TableColumn = {
children?: TableColumn[]
} & Recordable
export type VxeTableColumn = {
field: string
title?: string
children?: TableColumn[]
} & Recordable
export type TableSlotDefault = {
row: Recordable
column: TableColumn

View File

@ -1,16 +1,262 @@
<template>
<ContentWrap>
<el-form :model="queryParams" ref="queryForm" :inline="true">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择菜单状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<XButton
type="primary"
preIcon="ep:search"
:title="t('common.query')"
@click="handleQuery()"
/>
<XButton preIcon="ep:refresh-right" :title="t('common.reset')" @click="resetQuery()" />
</el-form-item>
</el-form>
<vxe-toolbar>
<template #buttons>
<XButton
type="primary"
preIcon="ep:zoom-in"
:title="t('action.add')"
v-hasPermi="['system:menu:create']"
@click="handleCreate()"
/>
<XButton title="展开所有" @click="xTable?.setAllTreeExpand(true)" />
<XButton title="关闭所有" @click="xTable?.clearTreeExpand()" />
</template>
</vxe-toolbar>
<vxe-table
show-overflow
keep-source
ref="xTable"
:loading="tableLoading"
:row-config="{ keyField: 'id' }"
:column-config="{ resizable: true }"
:tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
:print-config="{}"
:export-config="{}"
:data="tableData"
class="xtable"
>
<vxe-column title="菜单名称" field="name" width="200" tree-node>
<template #default="{ row }">
<Icon :icon="row.icon" />
<span class="ml-3">{{ row.name }}</span>
</template>
</vxe-column>
<vxe-column title="菜单类型" field="type">
<template #default="{ row }">
<DictTag :type="DICT_TYPE.SYSTEM_MENU_TYPE" :value="row.type" />
</template>
</vxe-column>
<vxe-column title="路由地址" field="path" />
<vxe-column title="组件路径" field="component" />
<vxe-column title="权限标识" field="permission" />
<vxe-column title="排序" field="sort" />
<vxe-column title="状态" field="status">
<template #default="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
</vxe-column>
<vxe-column title="创建时间" field="createTime" formatter="formatDate" />
<vxe-column title="操作" width="200">
<template #default="{ row }">
<XButton
link
type="primary"
preIcon="ep:edit"
:title="t('action.edit')"
v-hasPermi="['system:menu:update']"
@click="handleUpdate(row.id)"
/>
<XButton
link
type="primary"
preIcon="ep:delete"
:title="t('action.del')"
v-hasPermi="['system:menu:delete']"
@click="handleDelete(row)"
/>
</template>
</vxe-column>
</vxe-table>
</ContentWrap>
<XModal v-model="dialogVisible" id="menuModel" :title="dialogTitle">
<template #default>
<!-- 对话框(添加 / 修改) -->
<el-form
:model="menuForm"
:rules="rules"
:inline="true"
label-width="120px"
label-position="right"
>
<el-row :gutter="24">
<el-col :span="24">
<el-form-item label="上级菜单">
<el-tree-select
node-key="id"
v-model="menuForm.parentId"
:props="menuProps"
:data="menuOptions"
check-strictly
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单类型" prop="type">
<el-radio-group v-model="menuForm.type">
<el-radio-button
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="menuForm.name" placeholder="请输入菜单名称" clearable />
</el-form-item>
</el-col>
<template v-if="menuForm.type !== 3">
<el-col :span="12">
<el-form-item label="菜单图标">
<IconSelect v-model="menuForm.icon" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="路由地址" prop="path">
<template #label>
<Tooltip
titel="路由地址"
message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
/>
</template>
<el-input v-model="menuForm.path" placeholder="请输入路由地址" clearable />
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type === 2">
<el-col :span="12">
<el-form-item label="路由地址" prop="component">
<el-input v-model="menuForm.component" placeholder="请输入组件地址" clearable />
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type !== 1">
<el-col :span="12">
<el-form-item label="权限标识" prop="permission">
<template #label>
<Tooltip
titel="权限标识"
message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
/>
</template>
<el-input v-model="menuForm.permission" placeholder="请输入权限标识" clearable />
</el-form-item>
</el-col>
</template>
<el-col :span="12">
<el-form-item label="显示排序" prop="sort">
<el-input-number
v-model="menuForm.sort"
controls-position="right"
:min="0"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单状态" prop="status">
<el-radio-group v-model="menuForm.status">
<el-radio-button
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<template v-if="menuForm.type !== 3">
<el-col :span="12">
<el-form-item label="显示状态" prop="status">
<template #label>
<Tooltip
titel="显示状态"
message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问"
/>
</template>
<el-radio-group v-model="menuForm.visible">
<el-radio-button key="true" :label="true">显示</el-radio-button>
<el-radio-button key="false" :label="false">隐藏</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type === 2">
<el-col :span="12">
<el-form-item label="缓存状态" prop="keepAlive">
<template #label>
<Tooltip
titel="缓存状态"
message="选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致"
/>
</template>
<el-radio-group v-model="menuForm.keepAlive">
<el-radio-button key="true" :label="true">缓存</el-radio-button>
<el-radio-button key="false" :label="false">不缓存</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
</template>
<template #footer>
<!-- 操作按钮 -->
<XButton
v-if="['create', 'update'].includes(actionType)"
type="primary"
:loading="actionLoading"
@click="submitForm"
:title="t('action.save')"
/>
<XButton :loading="actionLoading" @click="dialogVisible = false" :title="t('dialog.close')" />
</template>
</XModal>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { handleTree } from '@/utils/tree'
import dayjs from 'dayjs'
import * as MenuApi from '@/api/system/menu'
import { MenuVO } from '@/api/system/menu/types'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { IconSelect } from '@/components/Icon'
import { Tooltip } from '@/components/Tooltip'
import * as MenuApi from '@/api/system/menu'
import { useI18n } from '@/hooks/web/useI18n'
import { required } from '@/utils/formRules.js'
import { onMounted, reactive, ref } from 'vue'
import { VxeTableInstance } from 'vxe-table'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'
import {
ElRow,
ElCol,
ElTable,
ElTableColumn,
ElForm,
ElFormItem,
ElInput,
@ -21,21 +267,32 @@ import {
ElRadioGroup,
ElRadioButton
} from 'element-plus'
import { MenuVO } from '@/api/system/menu/types'
import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { useMessage } from '@/hooks/web/useMessage'
import { required } from '@/utils/formRules.js'
const message = useMessage()
import { handleTree } from '@/utils/tree'
const { t } = useI18n() //
// ========== ==========
const loading = ref(true)
const menuData = ref<any[]>([]) //
const getList = async () => {
const res = await MenuApi.getMenuListApi(queryParams)
menuData.value = handleTree(res)
loading.value = false
}
const message = useMessage()
const xTable = ref<VxeTableInstance>()
const tableLoading = ref(false)
const tableData = ref()
const actionLoading = ref(false) //
const actionType = ref('') //
const dialogVisible = ref(false) //
const dialogTitle = ref('edit') //
const statusOption = ref() //
const menuForm = ref<MenuVO>({
id: 0,
name: '',
permission: '',
type: SystemMenuTypeEnum.DIR,
sort: 1,
parentId: 0,
path: '',
icon: '',
component: '',
status: CommonStatusEnum.ENABLE,
visible: true,
keepAlive: true,
createTime: ''
})
const menuProps = {
checkStrictly: true,
children: 'children',
@ -57,46 +314,16 @@ const getTree = async () => {
}
// ========== ==========
const queryParams = reactive({
name: undefined,
status: undefined
name: null,
status: null
})
//
const handleQuery = async () => {
await getList()
const getList = async () => {
statusOption.value = getIntDictOptions(DICT_TYPE.COMMON_STATUS)
tableLoading.value = true
const res = await MenuApi.getMenuListApi(queryParams)
tableData.value = res
tableLoading.value = false
}
//
const resetQuery = async () => {
queryParams.name = undefined
queryParams.status = undefined
await getList()
}
// ========== CRUD ==========
const actionLoading = ref(false) //
const actionType = ref('') //
const dialogVisible = ref(false) //
const dialogTitle = ref('edit') //
const menuForm = ref<MenuVO>({
id: 0,
name: '',
permission: '',
type: SystemMenuTypeEnum.DIR,
sort: 1,
parentId: 0,
path: '',
icon: '',
component: '',
status: CommonStatusEnum.ENABLE,
visible: true,
keepAlive: true,
createTime: ''
})
//
const rules = reactive({
name: [required],
sort: [required],
path: [required],
status: [required]
})
//
const setDialogTile = (type: string) => {
dialogTitle.value = t('action.' + type)
@ -105,25 +332,39 @@ const setDialogTile = (type: string) => {
}
//
const handleCreate = () => {
//
setDialogTile('create')
}
//
const handleUpdate = async (row: MenuVO) => {
//
const res = await MenuApi.getMenuApi(row.id)
console.log(res)
menuForm.value = res
setDialogTile('update')
}
//
const handleDelete = async (row: MenuVO) => {
message
.confirm(t('common.delDataMessage'), t('common.confirmTitle'))
.then(async () => {
await MenuApi.deleteMenuApi(row.id)
message.success(t('common.delSuccess'))
})
.catch(() => {})
message.confirm(t('common.delDataMessage'), t('common.confirmTitle')).then(async () => {
await MenuApi.deleteMenuApi(row.id)
message.success(t('common.delSuccess'))
await getList()
})
}
//
const rules = reactive({
name: [required],
sort: [required],
path: [required],
status: [required]
})
//
const handleQuery = async () => {
await getList()
}
//
const resetQuery = async () => {
queryParams.name = null
queryParams.status = null
await getList()
}
//
@ -162,248 +403,8 @@ const submitForm = async () => {
actionLoading.value = false
}
}
// ========== ==========
onMounted(async () => {
await getList()
getTree()
})
</script>
<template>
<!-- 搜索工作区 -->
<ContentWrap>
<el-form :model="queryParams" ref="queryForm" :inline="true">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择菜单状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh-right" class="mr-5px" />
{{ t('common.reset') }}
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<div class="mb-10px">
<el-button type="primary" v-hasPermi="['system:notice:create']" @click="handleCreate">
<Icon icon="ep:zoom-in" class="mr-1px" /> {{ t('action.add') }}
</el-button>
</div>
<el-table
v-loading="loading"
table-layout="auto"
row-key="id"
:data="menuData"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="菜单名称" prop="name" width="240px">
<template #default="scope">
<Icon :icon="scope.row.icon" />
<span class="ml-3">{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column label="菜单类型" prop="type">
<template #default="scope">
<DictTag :type="DICT_TYPE.SYSTEM_MENU_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="路由地址" prop="path" />
<el-table-column label="组件路径" prop="component" />
<el-table-column label="权限标识" prop="permission" />
<el-table-column label="排序" prop="sort" />
<el-table-column label="状态" prop="status">
<template #default="scope">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime">
<template #default="scope">
<span>{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button
link
type="primary"
v-hasPermi="['system:menu:update']"
@click="handleUpdate(scope.row)"
>
<Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
</el-button>
<el-button
link
type="primary"
v-hasPermi="['system:menu:delete']"
@click="handleDelete(scope.row)"
>
<Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
</el-button>
</template>
</el-table-column>
</el-table>
</ContentWrap>
<!-- 添加或修改菜单对话框 -->
<Dialog v-model="dialogVisible" :title="dialogTitle" maxHeight="400px" width="45%">
<el-form
:model="menuForm"
:rules="rules"
:inline="true"
label-width="120px"
label-position="right"
>
<el-row :gutter="24">
<el-col :span="24">
<el-form-item label="上级菜单">
<el-tree-select
node-key="id"
v-model="menuForm.parentId"
:props="menuProps"
:data="menuOptions"
check-strictly
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单类型" prop="type">
<el-radio-group v-model="menuForm.type">
<el-radio-button
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="menuForm.name" placeholder="请输入菜单名称" clearable />
</el-form-item>
</el-col>
<template v-if="menuForm.type !== 3">
<el-col :span="12">
<el-form-item label="菜单图标">
<IconSelect v-model="menuForm.icon" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="路由地址" prop="path">
<template #label>
<Tooltip
titel="路由地址"
message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
/>
</template>
<el-input v-model="menuForm.path" placeholder="请输入路由地址" clearable />
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type === 2">
<el-col :span="12">
<el-form-item label="路由地址" prop="component">
<el-input v-model="menuForm.component" placeholder="请输入组件地址" clearable />
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type !== 1">
<el-col :span="12">
<el-form-item label="权限标识" prop="permission">
<template #label>
<Tooltip
titel="权限标识"
message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
/>
</template>
<el-input v-model="menuForm.permission" placeholder="请输入权限标识" clearable />
</el-form-item>
</el-col>
</template>
<el-col :span="12">
<el-form-item label="显示排序" prop="sort">
<el-input-number v-model="menuForm.sort" controls-position="right" :min="0" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单状态" prop="status">
<el-radio-group v-model="menuForm.status">
<el-radio-button
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<template v-if="menuForm.type !== 3">
<el-col :span="12">
<el-form-item label="显示状态" prop="status">
<template #label>
<Tooltip
titel="显示状态"
message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问"
/>
</template>
<el-radio-group v-model="menuForm.visible">
<el-radio-button key="true" :label="true">显示</el-radio-button>
<el-radio-button key="false" :label="false">隐藏</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type === 2">
<el-col :span="12">
<el-form-item label="缓存状态" prop="keepAlive">
<template #label>
<Tooltip
titel="缓存状态"
message="选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致"
/>
</template>
<el-radio-group v-model="menuForm.keepAlive">
<el-radio-button key="true" :label="true">缓存</el-radio-button>
<el-radio-button key="false" :label="false">不缓存</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
<!-- 操作按钮 -->
<template #footer>
<el-button
v-if="['create', 'update'].includes(actionType)"
type="primary"
:loading="actionLoading"
@click="submitForm"
>
{{ t('action.save') }}
</el-button>
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
</template>
</Dialog>
</template>
<style lang="less" scoped>
:deep(.el-button.is-text) {
margin-left: 0;
padding: 8px 10px;
}
</style>

View File

@ -1,24 +1,100 @@
<template>
<ContentWrap>
<vxe-grid ref="xGrid" v-bind="gridOptions" class="xtable-scrollbar">
<template #toolbar_buttons>
<XButton
type="primary"
preIcon="ep:zoom-in"
:title="t('action.add')"
v-hasPermi="['system:post:create']"
@click="handleCreate()"
/>
</template>
<template #status_default="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #action_default="{ row }">
<XButton
link
type="primary"
preIcon="ep:edit"
:title="t('action.edit')"
v-hasPermi="['system:post:update']"
@click="handleUpdate(row.id)"
/>
<XButton
link
type="primary"
preIcon="ep:view"
:title="t('action.detail')"
v-hasPermi="['system:post:update']"
@click="handleDetail(row)"
/>
<XButton
link
type="primary"
preIcon="ep:delete"
:title="t('action.del')"
v-hasPermi="['system:post:delete']"
@click="handleDelete(row.id)"
/>
</template>
</vxe-grid>
</ContentWrap>
<XModal id="postModel" v-model="dialogVisible" :title="dialogTitle">
<template #default>
<!-- 对话框(添加 / 修改) -->
<vxe-form
ref="xForm"
v-if="['create', 'update'].includes(actionType)"
:data="formData"
:items="formItems"
:rules="rules"
/>
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailRef"
>
<template #status="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #createTime="{ row }">
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</template>
</Descriptions>
</template>
<template #footer>
<XButton
v-if="['create', 'update'].includes(actionType)"
:loading="actionLoading"
:title="t('action.save')"
@click="submitForm"
/>
<XButton
v-if="['create', 'update'].includes(actionType)"
:loading="actionLoading"
:title="t('dialog.close')"
@click="dialogVisible = false"
/>
</template>
</XModal>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { ref } from 'vue'
import dayjs from 'dayjs'
import { useI18n } from '@/hooks/web/useI18n'
import {
VxeFormEvents,
VxeFormInstance,
VxeFormItemProps,
VxeGrid,
VxeGridInstance,
VxeGridProps
} from 'vxe-table'
import { VxeFormEvents, VxeFormInstance, VxeFormItemProps, VxeGridInstance } from 'vxe-table'
import * as PostApi from '@/api/system/post'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { DICT_TYPE } from '@/utils/dict'
import { ContentWrap } from '@/components/ContentWrap'
import { PostPageReqVO, PostVO } from '@/api/system/post/types'
import { PostVO } from '@/api/system/post/types'
import { rules, allSchemas } from './post.data'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useMessage } from '@/hooks/web/useMessage'
import { useVxeGrid } from '@/hooks/web/useVxeGrid'
const { t } = useI18n() //
const message = useMessage()
const xGrid = ref<VxeGridInstance>()
const xForm = ref<VxeFormInstance>()
const dialogVisible = ref(false) //
@ -26,158 +102,9 @@ const dialogTitle = ref('edit') // 弹出层标题
const actionType = ref('') //
const actionLoading = ref(false) //
const gridOptions = reactive<VxeGridProps>({
loading: false,
rowConfig: {
keyField: 'id',
isHover: true
},
toolbarConfig: {
custom: true,
slots: { buttons: 'toolbar_buttons' }
},
printConfig: {
columns: [
{ field: 'name' },
{ field: 'code' },
{ field: 'sort' },
{ field: 'status' },
{ field: 'createTime' }
]
},
formConfig: {
titleWidth: 100,
titleAlign: 'right',
items: [
{
field: 'name',
title: '岗位名称',
span: 6,
itemRender: { name: '$input', props: { placeholder: '请输入岗位名称' } }
},
{
field: 'code',
title: '岗位编码',
span: 6,
itemRender: { name: '$input', props: { placeholder: '请输入岗位编码' } }
},
{
field: 'status',
title: t('common.status'),
span: 6,
itemRender: { name: '$select', options: getIntDictOptions(DICT_TYPE.COMMON_STATUS) }
},
{
span: 24,
align: 'center',
collapseNode: true,
itemRender: {
name: '$buttons',
children: [
{ props: { type: 'submit', content: t('common.query'), status: 'primary' } },
{ props: { type: 'reset', content: t('common.reset') } }
]
}
}
]
},
columns: [
{ type: 'seq', title: t('common.index'), width: 100 },
{ field: 'name', title: '岗位名称' },
{ field: 'code', title: '岗位编码' },
{ field: 'sort', title: '岗位顺序' },
{
field: 'status',
title: t('common.status'),
slots: {
default: 'status_default'
}
},
{
field: 'createTime',
title: t('common.createTime'),
width: 160,
sortable: true,
formatter: 'formatDate'
},
{
field: 'action',
title: t('table.action'),
width: '240px',
showOverflow: true,
slots: {
default: 'action_default'
}
}
],
pagerConfig: {
border: false,
background: false,
perfect: true,
pageSize: 10,
pagerCount: 7,
pageSizes: [5, 10, 15, 20, 50, 100, 200, 500],
layouts: ['PrevJump', 'PrevPage', 'Jump', 'PageCount', 'NextPage', 'NextJump', 'Sizes', 'Total']
},
proxyConfig: {
seq: true, //
form: true, // reload
props: { result: 'list', total: 'total' },
ajax: {
query: ({ page, form }) => {
const queryParams: PostPageReqVO = Object.assign({}, form)
queryParams.pageSize = page.pageSize
queryParams.pageNo = page.currentPage
return new Promise(async (resolve) => {
resolve(await PostApi.getPostPageApi(queryParams))
})
}
}
}
})
const gridOptions = useVxeGrid(allSchemas, PostApi.getPostPageApi)
const formData = ref<PostVO>()
const formItems = ref<VxeFormItemProps[]>([
{ field: 'id', title: 'id', visible: false },
{
field: 'name',
title: '岗位名称',
span: 12,
itemRender: { name: '$input', props: { placeholder: '请输入岗位名称' } }
},
{
field: 'code',
title: '岗位编码',
span: 12,
itemRender: { name: '$input', props: { placeholder: '请输入岗位编码' } }
},
{
field: 'sort',
title: '岗位顺序',
span: 12,
itemRender: { name: '$input', props: { type: 'number', placeholder: '请输入岗位顺序' } }
},
{
field: 'status',
title: t('common.status'),
span: 12,
itemRender: {
name: '$select',
options: getIntDictOptions(DICT_TYPE.COMMON_STATUS),
props: { placeholder: '请选择' }
}
},
{
align: 'center',
span: 24,
itemRender: {
name: '$buttons',
children: [
{ props: { type: 'submit', content: t('action.save'), status: 'primary' } },
{ props: { type: 'reset', content: t('common.reset') } }
]
}
}
])
const formItems = ref<VxeFormItemProps[]>(allSchemas.formSchema)
//
const setDialogTile = (type: string) => {
dialogTitle.value = t('action.' + type)
@ -194,7 +121,7 @@ const handleDetail = (row: PostVO) => {
//
const handleCreate = () => {
setDialogTile('create')
xForm.value?.reset()
formData.value = undefined
}
//
@ -206,16 +133,13 @@ const handleUpdate = async (rowId: number) => {
}
//
const handleDelete = (rowId: number) => {
ElMessageBox.confirm(t('common.delMessage'), t('common.confirmTitle'), {
confirmButtonText: t('common.ok'),
cancelButtonText: t('common.cancel'),
type: 'warning'
})
message
.confirm(t('common.delMessage'), t('common.confirmTitle'))
.then(async () => {
await PostApi.deletePostApi(rowId)
message.success(t('common.delSuccess'))
})
.finally(() => {
ElMessage.success(t('common.delSuccess'))
xGrid.value?.commitProxy('query')
})
}
@ -227,10 +151,10 @@ const submitForm: VxeFormEvents.Submit = async () => {
const data = formData.value as PostVO
if (actionType.value === 'create') {
await PostApi.createPostApi(data)
ElMessage.success(t('common.createSuccess'))
message.success(t('common.createSuccess'))
} else {
await PostApi.updatePostApi(data)
ElMessage.success(t('common.updateSuccess'))
message.success(t('common.updateSuccess'))
}
//
dialogVisible.value = false
@ -240,82 +164,3 @@ const submitForm: VxeFormEvents.Submit = async () => {
}
}
</script>
<template>
<ContentWrap>
<vxe-grid ref="xGrid" v-bind="gridOptions">
<template #toolbar_buttons>
<el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
<Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
</el-button>
</template>
<template #status_default="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #action_default="{ row }">
<el-button
link
type="primary"
v-hasPermi="['system:post:update']"
@click="handleUpdate(row.id)"
>
<Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
</el-button>
<el-button
link
type="primary"
v-hasPermi="['system:post:update']"
@click="handleDetail(row)"
>
<Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
</el-button>
<el-button
link
type="primary"
v-hasPermi="['system:post:delete']"
@click="handleDelete(row.id)"
>
<Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
</el-button>
</template>
</vxe-grid>
</ContentWrap>
<vxe-modal
v-model="dialogVisible"
id="myModal6"
:title="dialogTitle"
width="800"
height="400"
min-width="460"
min-height="320"
show-zoom
resize
remember
storage
transfer
show-footer
>
<template #default>
<!-- 对话框(添加 / 修改) -->
<vxe-form
ref="xForm"
v-if="['create', 'update'].includes(actionType)"
:data="formData"
:items="formItems"
:rules="rules"
@submit="submitForm"
/>
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailRef"
>
<template #status="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #createTime="{ row }">
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</template>
</Descriptions>
</template>
</vxe-modal>
</template>

View File

@ -1,194 +0,0 @@
<script setup lang="ts">
import { ref, unref } from 'vue'
import dayjs from 'dayjs'
import { ElMessage } from 'element-plus'
import { DICT_TYPE } from '@/utils/dict'
import { useTable } from '@/hooks/web/useTable'
import { useI18n } from '@/hooks/web/useI18n'
import { FormExpose } from '@/components/Form'
import type { PostVO } from '@/api/system/post/types'
import { rules, allSchemas } from './post.data'
import * as PostApi from '@/api/system/post'
const { t } = useI18n() //
// ========== ==========
const { register, tableObject, methods } = useTable<PostVO>({
getListApi: PostApi.getPostPageApi,
delListApi: PostApi.deletePostApi,
exportListApi: PostApi.exportPostApi
})
const { getList, setSearchParams, delList, exportList } = methods
// ========== CRUD ==========
const actionLoading = ref(false) //
const actionType = ref('') //
const dialogVisible = ref(false) //
const dialogTitle = ref('edit') //
const formRef = ref<FormExpose>() // Ref
//
const setDialogTile = (type: string) => {
dialogTitle.value = t('action.' + type)
actionType.value = type
dialogVisible.value = true
}
//
const handleCreate = () => {
setDialogTile('create')
//
unref(formRef)?.getElFormRef()?.resetFields()
}
//
const handleUpdate = async (rowId: number) => {
setDialogTile('update')
//
const res = await PostApi.getPostApi(rowId)
unref(formRef)?.setValues(res)
}
//
const submitForm = async () => {
const elForm = unref(formRef)?.getElFormRef()
if (!elForm) return
elForm.validate(async (valid) => {
if (valid) {
actionLoading.value = true
//
try {
const data = unref(formRef)?.formModel as PostVO
if (actionType.value === 'create') {
await PostApi.createPostApi(data)
ElMessage.success(t('common.createSuccess'))
} else {
await PostApi.updatePostApi(data)
ElMessage.success(t('common.updateSuccess'))
}
//
dialogVisible.value = false
await getList()
} finally {
actionLoading.value = false
}
}
})
}
// ========== ==========
const detailRef = ref() // Ref
//
const handleDetail = async (row: PostVO) => {
//
detailRef.value = row
setDialogTile('detail')
}
// ========== ==========
getList()
</script>
<template>
<!-- 搜索工作区 -->
<ContentWrap>
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
</ContentWrap>
<ContentWrap>
<!-- 操作工具栏 -->
<div class="mb-10px">
<el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
<Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
</el-button>
<el-button
type="warning"
v-hasPermi="['system:post:export']"
:loading="tableObject.exportLoading"
@click="exportList('岗位数据.xls')"
>
<Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
</el-button>
</div>
<!-- 列表 -->
<Table
:columns="allSchemas.tableColumns"
:selection="false"
:data="tableObject.tableList"
:loading="tableObject.loading"
:pagination="{
total: tableObject.total
}"
v-model:pageSize="tableObject.pageSize"
v-model:currentPage="tableObject.currentPage"
@register="register"
>
<template #status="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #createTime="{ row }">
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</template>
<template #action="{ row }">
<el-button
link
type="primary"
v-hasPermi="['system:post:update']"
@click="handleUpdate(row.id)"
>
<Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
</el-button>
<el-button
link
type="primary"
v-hasPermi="['system:post:update']"
@click="handleDetail(row)"
>
<Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
</el-button>
<el-button
link
type="primary"
v-hasPermi="['system:post:delete']"
@click="delList(row.id, false)"
>
<Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
</el-button>
</template>
</Table>
</ContentWrap>
<Dialog v-model="dialogVisible" :title="dialogTitle">
<!-- 对话框(添加 / 修改) -->
<Form
v-if="['create', 'update'].includes(actionType)"
:schema="allSchemas.formSchema"
:rules="rules"
ref="formRef"
/>
<!-- 对话框(详情) -->
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailRef"
>
<template #status="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #createTime="{ row }">
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</template>
</Descriptions>
<!-- 操作按钮 -->
<template #footer>
<el-button
v-if="['create', 'update'].includes(actionType)"
type="primary"
:loading="actionLoading"
@click="submitForm"
>
{{ t('action.save') }}
</el-button>
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
</template>
</Dialog>
</template>

View File

@ -1,7 +1,7 @@
import { reactive } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { required } from '@/utils/formRules'
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
import { VxeCrudSchema, useVxeCrudSchemas } from '@/hooks/web/useVxeCrudSchemas'
import { DICT_TYPE } from '@/utils/dict'
const { t } = useI18n() // 国际化
@ -13,9 +13,9 @@ export const rules = reactive({
})
// CrudSchema
const crudSchemas = reactive<CrudSchema[]>([
const crudSchemas = reactive<VxeCrudSchema[]>([
{
label: t('common.index'),
title: t('common.index'),
field: 'id',
type: 'index',
form: {
@ -26,42 +26,54 @@ const crudSchemas = reactive<CrudSchema[]>([
}
},
{
label: '岗位名称',
title: '岗位名称',
field: 'name',
search: {
show: true
}
},
{
label: '岗位编码',
title: '岗位编码',
field: 'code',
search: {
show: true
}
},
{
label: '岗位顺序',
title: '岗位顺序',
field: 'sort'
},
{
label: t('common.status'),
title: t('common.status'),
field: 'status',
dictType: DICT_TYPE.COMMON_STATUS,
table: {
slots: {
default: 'status_default'
}
},
search: {
show: true
}
},
{
label: t('common.createTime'),
title: t('common.createTime'),
field: 'createTime',
form: {
show: false
}
},
{
label: t('table.action'),
title: t('table.action'),
field: 'action',
width: '240px',
table: {
width: '240px',
showOverflow: true,
slots: {
default: 'action_default'
}
},
form: {
show: false
},
@ -70,4 +82,4 @@ const crudSchemas = reactive<CrudSchema[]>([
}
}
])
export const { allSchemas } = useCrudSchemas(crudSchemas)
export const { allSchemas } = useVxeCrudSchemas(crudSchemas)