zant-admin 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.en.md +414 -25
- package/README.md +426 -277
- package/bin/cli.js +99 -99
- package/bin/generator.js +502 -502
- package/bin/prompts.js +158 -158
- package/bin/utils.js +133 -133
- package/package.json +3 -3
- package/public/logo.png +0 -0
- package/src/App.vue +1 -1
- package/src/api/methods/sysAccount.js +0 -1
- package/src/api/methods/sysDictItem.js +3 -3
- package/src/api/methods/system.js +10 -11
- package/src/api/request.js +39 -39
- package/src/assets/css/style.css +0 -11
- package/src/assets/css/zcui.css +1002 -319
- package/src/assets/imgs/logo.png +0 -0
- package/src/assets/imgs/md/console.png +0 -0
- package/src/assets/imgs/md/login.png +0 -0
- package/src/assets/imgs/md/menu.png +0 -0
- package/src/assets/imgs/md/serviceMonitoring.png +0 -0
- package/src/assets/imgs/md/statistics.png +0 -0
- package/src/components/FormTable.vue +50 -37
- package/src/components/IconPicker.vue +351 -344
- package/src/components/MainPage.vue +220 -339
- package/src/components/edit/QuartzEdit.vue +1 -1
- package/src/components/edit/SysAccountEdit.vue +15 -8
- package/src/components/edit/SysDictEdit.vue +6 -4
- package/src/components/edit/SysDictItemEdit.vue +8 -6
- package/src/components/edit/SysRoleEdit.vue +5 -3
- package/src/components/edit/sysMenuEdit.vue +10 -25
- package/src/config/index.js +74 -74
- package/src/directives/permission.js +49 -45
- package/src/main.js +2 -3
- package/src/router/index.js +48 -30
- package/src/stores/config.js +7 -1
- package/src/stores/menu.js +32 -8
- package/src/stores/user.js +17 -17
- package/src/utils/regionUtils.js +20 -16
- package/src/utils/useFormCRUD.js +59 -60
- package/src/views/baiscstatis/center.vue +53 -42
- package/src/views/baiscstatis/iframePage.vue +9 -11
- package/src/views/console.vue +92 -42
- package/src/views/demo/button.vue +269 -0
- package/src/views/demo/importexport.vue +8 -12
- package/src/views/demo/region.vue +103 -21
- package/src/views/demo/statistics.vue +38 -19
- package/src/views/home.vue +2 -3
- package/src/views/login.vue +254 -149
- package/src/views/operations/log/logQuartz.vue +0 -1
- package/src/views/operations/quartz.vue +22 -24
- package/src/views/system/sysAccount.vue +16 -11
- package/src/views/system/sysDict.vue +9 -6
- package/src/views/system/sysMenu.vue +17 -15
- package/src/views/system/sysRole.vue +44 -21
- package/SCAFFOLD_README.md +0 -215
- package/src/assets/imgs/md/1.png +0 -0
- package/src/assets/imgs/md/10.png +0 -0
- package/src/assets/imgs/md/11.png +0 -0
- package/src/assets/imgs/md/2.png +0 -0
- package/src/assets/imgs/md/3.png +0 -0
- package/src/assets/imgs/md/4.png +0 -0
- package/src/assets/imgs/md/5.png +0 -0
- package/src/assets/imgs/md/6.png +0 -0
- package/src/assets/imgs/md/7.png +0 -0
- package/src/assets/imgs/md/8.png +0 -0
- package/src/assets/imgs/md/9.png +0 -0
package/src/router/index.js
CHANGED
|
@@ -31,14 +31,13 @@ const routes = [
|
|
|
31
31
|
meta: { title: '主页', cache: true },
|
|
32
32
|
component: () => import('../views/console.vue'),
|
|
33
33
|
},
|
|
34
|
-
|
|
34
|
+
{
|
|
35
35
|
path: 'center',
|
|
36
36
|
name: 'center',
|
|
37
37
|
meta: { title: '个人中心', cache: true },
|
|
38
38
|
component: () => import('../views/baiscstatis/center.vue'),
|
|
39
39
|
},
|
|
40
40
|
|
|
41
|
-
|
|
42
41
|
{
|
|
43
42
|
path: 'iframePage',
|
|
44
43
|
meta: { cache: false },
|
|
@@ -46,6 +45,7 @@ const routes = [
|
|
|
46
45
|
},
|
|
47
46
|
{
|
|
48
47
|
path: 'notFound',
|
|
48
|
+
meta: { title: '404 Not Found', cache: true },
|
|
49
49
|
component: () => import('../views/baiscstatis/notFound.vue'),
|
|
50
50
|
},
|
|
51
51
|
],
|
|
@@ -64,7 +64,7 @@ let hasAddRoutes = false
|
|
|
64
64
|
let routeLoadFailed = false
|
|
65
65
|
|
|
66
66
|
// 组件加载器,带有错误处理
|
|
67
|
-
const loadComponent =
|
|
67
|
+
const loadComponent = path => {
|
|
68
68
|
const possiblePaths = [
|
|
69
69
|
`/src/views${path}.vue`,
|
|
70
70
|
`./views${path}.vue`,
|
|
@@ -90,26 +90,25 @@ async function loadDynamicRoutes() {
|
|
|
90
90
|
const dynamicBtns = resbtn.data || []
|
|
91
91
|
menu.setMenus(dynamicRoutes)
|
|
92
92
|
menu.setPermissions(dynamicBtns)
|
|
93
|
-
dynamicRoutes.forEach(
|
|
93
|
+
dynamicRoutes.forEach(route => {
|
|
94
94
|
if (route.type === 1) {
|
|
95
95
|
// 目录类型
|
|
96
96
|
const parentRoute = {
|
|
97
97
|
path: route.path,
|
|
98
98
|
component: home,
|
|
99
|
-
|
|
99
|
+
meta: { title: route.title },
|
|
100
100
|
// children: [],
|
|
101
101
|
}
|
|
102
102
|
const addRoutes = (parentRoute, children) => {
|
|
103
103
|
if (children && children.length > 0) {
|
|
104
104
|
parentRoute.children = children.map(child => {
|
|
105
105
|
const childRoute = {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
meta: { cache: child.cache,fullPath: child.path, },
|
|
106
|
+
path: child.path,
|
|
107
|
+
name: child.path.replace(/\//g, '-').substring(1),
|
|
108
|
+
meta: { title: child.title, cache: child.cache, fullPath: child.path },
|
|
110
109
|
}
|
|
111
|
-
if(child.type!=1){
|
|
112
|
-
|
|
110
|
+
if (child.type != 1) {
|
|
111
|
+
childRoute.component = loadComponent(child.path)
|
|
113
112
|
}
|
|
114
113
|
// 递归处理多级子路由
|
|
115
114
|
if (child.children) {
|
|
@@ -126,7 +125,7 @@ async function loadDynamicRoutes() {
|
|
|
126
125
|
const pageRoute = {
|
|
127
126
|
path: route.path,
|
|
128
127
|
name: route.path.replace(/\//g, '-').substring(1),
|
|
129
|
-
|
|
128
|
+
component: loadComponent(route.path),
|
|
130
129
|
meta: {
|
|
131
130
|
title: route.title,
|
|
132
131
|
cache: route.cache,
|
|
@@ -140,7 +139,7 @@ async function loadDynamicRoutes() {
|
|
|
140
139
|
} catch (error) {
|
|
141
140
|
console.error('❌ 动态路由加载失败:', error)
|
|
142
141
|
routeLoadFailed = true
|
|
143
|
-
|
|
142
|
+
// 如果是401错误(令牌过期),清除用户信息并跳转到登录页
|
|
144
143
|
if (error.code === 401 || (error.errors && error.errors.includes('登录'))) {
|
|
145
144
|
const user = useUserStore()
|
|
146
145
|
user.clearUserInfo()
|
|
@@ -152,13 +151,11 @@ async function loadDynamicRoutes() {
|
|
|
152
151
|
|
|
153
152
|
// 全局前置守卫
|
|
154
153
|
router.beforeEach(async (to, from, next) => {
|
|
155
|
-
// document.title = to.meta.title || 'ZCloudVue'
|
|
156
|
-
// document.title = 'ZCloudVue'
|
|
157
154
|
const config = configStore()
|
|
158
|
-
document.title = config.projectName
|
|
155
|
+
document.title = to.meta.title ? to.meta.title + ' - ' + config.projectName : config.projectName
|
|
159
156
|
const user = useUserStore()
|
|
160
157
|
|
|
161
|
-
|
|
158
|
+
// 如果目标路由在白名单中,直接放行
|
|
162
159
|
if (publicRoutes.includes(to.path)) {
|
|
163
160
|
next()
|
|
164
161
|
return
|
|
@@ -173,26 +170,41 @@ router.beforeEach(async (to, from, next) => {
|
|
|
173
170
|
if (user.token && !hasAddRoutes && !routeLoadFailed) {
|
|
174
171
|
try {
|
|
175
172
|
await loadDynamicRoutes()
|
|
176
|
-
|
|
173
|
+
|
|
177
174
|
// 检查是否有持久化的路由状态需要恢复
|
|
178
175
|
const menu = menuStore()
|
|
179
|
-
if (
|
|
176
|
+
if (
|
|
177
|
+
menu.selectmenuKey &&
|
|
178
|
+
menu.selectmenuKey.length > 0 &&
|
|
179
|
+
to.path === '/console'
|
|
180
|
+
) {
|
|
180
181
|
// 如果当前是跳转到首页,但有持久化的选中菜单,尝试恢复路由
|
|
181
|
-
const selectedMenu = menu.tagmenus.find(
|
|
182
|
-
|
|
182
|
+
const selectedMenu = menu.tagmenus.find(
|
|
183
|
+
tag => tag.key === menu.selectmenuKey[0],
|
|
184
|
+
)
|
|
185
|
+
if (
|
|
186
|
+
selectedMenu &&
|
|
187
|
+
selectedMenu.path &&
|
|
188
|
+
selectedMenu.path !== '/console'
|
|
189
|
+
) {
|
|
183
190
|
// 检查路由是否存在
|
|
184
|
-
const routeExists = router
|
|
191
|
+
const routeExists = router
|
|
192
|
+
.getRoutes()
|
|
193
|
+
.some(r => r.path === selectedMenu.path)
|
|
185
194
|
if (routeExists) {
|
|
186
195
|
next({ path: selectedMenu.path, replace: true })
|
|
187
196
|
return
|
|
188
197
|
}
|
|
189
198
|
}
|
|
190
199
|
}
|
|
191
|
-
|
|
200
|
+
|
|
192
201
|
next({ ...to, replace: true })
|
|
193
202
|
} catch (error) {
|
|
194
203
|
// 如果是401错误,已经在loadDynamicRoutes中处理了跳转
|
|
195
|
-
if (
|
|
204
|
+
if (
|
|
205
|
+
error.code === 401 ||
|
|
206
|
+
(error.errors && error.errors.includes('登录'))
|
|
207
|
+
) {
|
|
196
208
|
return
|
|
197
209
|
}
|
|
198
210
|
// 其他错误跳转到404页面
|
|
@@ -216,7 +228,7 @@ router.beforeEach(async (to, from, next) => {
|
|
|
216
228
|
}
|
|
217
229
|
|
|
218
230
|
// 路由存在性检查
|
|
219
|
-
const routeExists = router.getRoutes().some(
|
|
231
|
+
const routeExists = router.getRoutes().some(r => r.path === to.path)
|
|
220
232
|
if (!routeExists && !publicRoutes.includes(to.path)) {
|
|
221
233
|
// 如果路由不存在且不在白名单中,检查是否是动态路由还未加载
|
|
222
234
|
if (user.token && !hasAddRoutes && !routeLoadFailed) {
|
|
@@ -227,7 +239,10 @@ router.beforeEach(async (to, from, next) => {
|
|
|
227
239
|
return
|
|
228
240
|
} catch (error) {
|
|
229
241
|
// 如果是401错误,已经在loadDynamicRoutes中处理了跳转
|
|
230
|
-
if (
|
|
242
|
+
if (
|
|
243
|
+
error.code === 401 ||
|
|
244
|
+
(error.errors && error.errors.includes('登录'))
|
|
245
|
+
) {
|
|
231
246
|
return
|
|
232
247
|
}
|
|
233
248
|
// 其他错误跳转到404页面
|
|
@@ -243,7 +258,7 @@ router.beforeEach(async (to, from, next) => {
|
|
|
243
258
|
next()
|
|
244
259
|
})
|
|
245
260
|
|
|
246
|
-
router.onError(
|
|
261
|
+
router.onError(error => {
|
|
247
262
|
console.error('🚨 路由错误:', error)
|
|
248
263
|
})
|
|
249
264
|
|
|
@@ -253,12 +268,15 @@ export async function refreshRoutes() {
|
|
|
253
268
|
// 清空旧的菜单,避免重复
|
|
254
269
|
const menu = menuStore()
|
|
255
270
|
menu.setMenus([])
|
|
256
|
-
|
|
257
|
-
|
|
271
|
+
menu.setPermissions([])
|
|
272
|
+
// 移除之前添加的动态路由
|
|
258
273
|
const routes = router.getRoutes()
|
|
259
274
|
routes.forEach(route => {
|
|
260
275
|
// 只移除动态添加的路由,保留静态路由
|
|
261
|
-
if (
|
|
276
|
+
if (
|
|
277
|
+
route.path &&
|
|
278
|
+
!['/login', '/', '/console', '/notFound', '/center'].includes(route.path)
|
|
279
|
+
) {
|
|
262
280
|
router.removeRoute(route.name)
|
|
263
281
|
}
|
|
264
282
|
})
|
package/src/stores/config.js
CHANGED
|
@@ -30,7 +30,13 @@ export const configStore = defineStore({
|
|
|
30
30
|
{
|
|
31
31
|
key: 'CONFIG',
|
|
32
32
|
storage: localStorage,
|
|
33
|
-
paths: [
|
|
33
|
+
paths: [
|
|
34
|
+
'themeClass',
|
|
35
|
+
'navigationMode',
|
|
36
|
+
'tableSize',
|
|
37
|
+
'tableBordered',
|
|
38
|
+
'locale',
|
|
39
|
+
],
|
|
34
40
|
},
|
|
35
41
|
],
|
|
36
42
|
},
|
package/src/stores/menu.js
CHANGED
|
@@ -16,7 +16,7 @@ export const menuStore = defineStore({
|
|
|
16
16
|
},
|
|
17
17
|
],
|
|
18
18
|
menus: [],
|
|
19
|
-
permissions: []
|
|
19
|
+
permissions: [],
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
actions: {
|
|
@@ -26,8 +26,12 @@ export const menuStore = defineStore({
|
|
|
26
26
|
setOpenmenuKey(keys) {
|
|
27
27
|
this.openmenuKey = keys
|
|
28
28
|
},
|
|
29
|
-
setMenus(menus) {
|
|
30
|
-
|
|
29
|
+
setMenus(menus) {
|
|
30
|
+
this.menus = menus
|
|
31
|
+
},
|
|
32
|
+
setPermissions(permissions) {
|
|
33
|
+
this.permissions = permissions
|
|
34
|
+
},
|
|
31
35
|
// 检查是否有指定权限
|
|
32
36
|
hasPermission(permission) {
|
|
33
37
|
if (!permission) return true
|
|
@@ -35,13 +39,27 @@ export const menuStore = defineStore({
|
|
|
35
39
|
},
|
|
36
40
|
// 检查是否有多个权限中的任意一个
|
|
37
41
|
hasAnyPermission(permissions) {
|
|
38
|
-
if (
|
|
39
|
-
|
|
42
|
+
if (
|
|
43
|
+
!permissions ||
|
|
44
|
+
!Array.isArray(permissions) ||
|
|
45
|
+
permissions.length === 0
|
|
46
|
+
)
|
|
47
|
+
return true
|
|
48
|
+
return permissions.some(permission =>
|
|
49
|
+
this.permissions.includes(permission),
|
|
50
|
+
)
|
|
40
51
|
},
|
|
41
52
|
// 检查是否拥有所有权限
|
|
42
53
|
hasAllPermissions(permissions) {
|
|
43
|
-
if (
|
|
44
|
-
|
|
54
|
+
if (
|
|
55
|
+
!permissions ||
|
|
56
|
+
!Array.isArray(permissions) ||
|
|
57
|
+
permissions.length === 0
|
|
58
|
+
)
|
|
59
|
+
return true
|
|
60
|
+
return permissions.every(permission =>
|
|
61
|
+
this.permissions.includes(permission),
|
|
62
|
+
)
|
|
45
63
|
},
|
|
46
64
|
},
|
|
47
65
|
persist: {
|
|
@@ -50,7 +68,13 @@ export const menuStore = defineStore({
|
|
|
50
68
|
{
|
|
51
69
|
key: 'MENU_RESULT',
|
|
52
70
|
storage: localStorage,
|
|
53
|
-
paths: [
|
|
71
|
+
paths: [
|
|
72
|
+
'menus',
|
|
73
|
+
'openmenuKey',
|
|
74
|
+
'selectmenuKey',
|
|
75
|
+
'tagmenus',
|
|
76
|
+
'permissions',
|
|
77
|
+
],
|
|
54
78
|
},
|
|
55
79
|
],
|
|
56
80
|
},
|
package/src/stores/user.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
2
|
export const useUserStore = defineStore({
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
strategies: [{
|
|
3
|
+
id: 'TOKEN_USER_INFO',
|
|
4
|
+
state: () => {
|
|
5
|
+
return {
|
|
6
|
+
token: '',
|
|
7
|
+
userInfo: {},
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
actions: {},
|
|
11
|
+
persist: {
|
|
12
|
+
enabled: true,
|
|
13
|
+
strategies: [
|
|
14
|
+
{
|
|
16
15
|
key: 'TOKEN_USER_INFO',
|
|
17
16
|
storage: localStorage,
|
|
18
|
-
paths: ['token', 'userInfo'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
}
|
|
17
|
+
paths: ['token', 'userInfo'],
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
})
|
package/src/utils/regionUtils.js
CHANGED
|
@@ -18,7 +18,7 @@ const fetchAllAddressData = async () => {
|
|
|
18
18
|
const [provinceRes, cityRes, areaRes] = await Promise.all([
|
|
19
19
|
region.getAllProvince(),
|
|
20
20
|
region.getAllCity(),
|
|
21
|
-
region.getAllAreas()
|
|
21
|
+
region.getAllAreas(),
|
|
22
22
|
])
|
|
23
23
|
|
|
24
24
|
// 存储原始数据
|
|
@@ -44,7 +44,7 @@ const fetchAllAddressData = async () => {
|
|
|
44
44
|
value: item.adcode,
|
|
45
45
|
})),
|
|
46
46
|
cities: allCity.value,
|
|
47
|
-
areas: allAreas.value
|
|
47
|
+
areas: allAreas.value,
|
|
48
48
|
}
|
|
49
49
|
} catch (error) {
|
|
50
50
|
console.error('获取省市区数据失败:', error)
|
|
@@ -57,12 +57,14 @@ const fetchAllAddressData = async () => {
|
|
|
57
57
|
* @param {string} provinceCode - 省份代码(adcode)
|
|
58
58
|
* @returns {Array} 城市数据数组
|
|
59
59
|
*/
|
|
60
|
-
const getCitiesByProvinceCode =
|
|
60
|
+
const getCitiesByProvinceCode = provinceCode => {
|
|
61
61
|
if (!provinceCode || !cityData.value.length) return []
|
|
62
|
-
return cityData.value
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
return cityData.value
|
|
63
|
+
.filter(city => city.parentAdcode === provinceCode)
|
|
64
|
+
.map(city => ({
|
|
65
|
+
label: city.name,
|
|
66
|
+
value: city.adcode,
|
|
67
|
+
}))
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
/**
|
|
@@ -70,12 +72,14 @@ const getCitiesByProvinceCode = (provinceCode) => {
|
|
|
70
72
|
* @param {string} cityCode - 城市代码(adcode)
|
|
71
73
|
* @returns {Array} 区域数据数组
|
|
72
74
|
*/
|
|
73
|
-
const getAreasByCityCode =
|
|
75
|
+
const getAreasByCityCode = cityCode => {
|
|
74
76
|
if (!cityCode || !areaData.value.length) return []
|
|
75
|
-
return areaData.value
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
return areaData.value
|
|
78
|
+
.filter(area => area.parentAdcode === cityCode)
|
|
79
|
+
.map(area => ({
|
|
80
|
+
label: area.name,
|
|
81
|
+
value: area.adcode,
|
|
82
|
+
}))
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
/**
|
|
@@ -110,7 +114,7 @@ const getAllAreas = () => {
|
|
|
110
114
|
* @param {string} adcode - 省份adcode
|
|
111
115
|
* @returns {Object} 省份信息对象
|
|
112
116
|
*/
|
|
113
|
-
const getProvinceByAdcode =
|
|
117
|
+
const getProvinceByAdcode = adcode => {
|
|
114
118
|
if (!adcode || !provinceData.value.length) return null
|
|
115
119
|
return provinceData.value.find(province => province.adcode === adcode)
|
|
116
120
|
}
|
|
@@ -120,7 +124,7 @@ const getProvinceByAdcode = (adcode) => {
|
|
|
120
124
|
* @param {string} adcode - 城市adcode
|
|
121
125
|
* @returns {Object} 城市信息对象
|
|
122
126
|
*/
|
|
123
|
-
const getCityByAdcode =
|
|
127
|
+
const getCityByAdcode = adcode => {
|
|
124
128
|
if (!adcode || !cityData.value.length) return null
|
|
125
129
|
return cityData.value.find(city => city.adcode === adcode)
|
|
126
130
|
}
|
|
@@ -130,7 +134,7 @@ const getCityByAdcode = (adcode) => {
|
|
|
130
134
|
* @param {string} adcode - 区域adcode
|
|
131
135
|
* @returns {Object} 区域信息对象
|
|
132
136
|
*/
|
|
133
|
-
const getAreaByAdcode =
|
|
137
|
+
const getAreaByAdcode = adcode => {
|
|
134
138
|
if (!adcode || !areaData.value.length) return null
|
|
135
139
|
return areaData.value.find(area => area.adcode === adcode)
|
|
136
140
|
}
|
|
@@ -165,5 +169,5 @@ export default {
|
|
|
165
169
|
getProvinceByAdcode,
|
|
166
170
|
getCityByAdcode,
|
|
167
171
|
getAreaByAdcode,
|
|
168
|
-
getFullAddressName
|
|
172
|
+
getFullAddressName,
|
|
169
173
|
}
|
package/src/utils/useFormCRUD.js
CHANGED
|
@@ -1,60 +1,59 @@
|
|
|
1
|
-
import { ref } from 'vue'
|
|
2
|
-
import { message } from 'ant-design-vue'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 通用表单 CRUD Hook
|
|
6
|
-
* @param {Object} api - API对象,例如 sysRole
|
|
7
|
-
* @param {Object} [options] - 配置
|
|
8
|
-
* @param {string} [options.addMethod='add'] - 新增方法名
|
|
9
|
-
* @param {string} [options.updateMethod='update'] - 修改方法名
|
|
10
|
-
* @param {boolean} [options.autoMessage=true] - 是否自动提示
|
|
11
|
-
*/
|
|
12
|
-
export default function useFormCRUD(
|
|
13
|
-
|
|
14
|
-
updateMethod = 'update',
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*
|
|
21
|
-
* @param {Object}
|
|
22
|
-
* @param {
|
|
23
|
-
* @param {Function} [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
onError
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
import { message } from 'ant-design-vue'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 通用表单 CRUD Hook
|
|
6
|
+
* @param {Object} api - API对象,例如 sysRole
|
|
7
|
+
* @param {Object} [options] - 配置
|
|
8
|
+
* @param {string} [options.addMethod='add'] - 新增方法名
|
|
9
|
+
* @param {string} [options.updateMethod='update'] - 修改方法名
|
|
10
|
+
* @param {boolean} [options.autoMessage=true] - 是否自动提示
|
|
11
|
+
*/
|
|
12
|
+
export default function useFormCRUD(
|
|
13
|
+
api,
|
|
14
|
+
{ addMethod = 'add', updateMethod = 'update', autoMessage = true } = {},
|
|
15
|
+
) {
|
|
16
|
+
const loading = ref(false)
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 保存表单
|
|
20
|
+
* @param {Object} formRef - 表单 ref
|
|
21
|
+
* @param {Object} formData - 表单数据
|
|
22
|
+
* @param {Function} [onSuccess] - 成功回调
|
|
23
|
+
* @param {Function} [onError] - 错误回调
|
|
24
|
+
*/
|
|
25
|
+
const save = async (formRef, formData, { onSuccess, onError } = {}) => {
|
|
26
|
+
try {
|
|
27
|
+
// 校验表单
|
|
28
|
+
await formRef?.validate()
|
|
29
|
+
loading.value = true
|
|
30
|
+
|
|
31
|
+
const method = formData.id ? updateMethod : addMethod
|
|
32
|
+
const result = await api[method](formData)
|
|
33
|
+
// 先显示提示,再执行回调
|
|
34
|
+
if (autoMessage) {
|
|
35
|
+
message.success(formData.id ? '修改成功' : '新增成功', 1, () => {
|
|
36
|
+
onSuccess?.(result)
|
|
37
|
+
})
|
|
38
|
+
} else {
|
|
39
|
+
onSuccess?.(result)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return result
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (err?.errorFields) {
|
|
45
|
+
message.warning('请完善表单信息')
|
|
46
|
+
} else {
|
|
47
|
+
onError?.(err)
|
|
48
|
+
if (autoMessage && onError) {
|
|
49
|
+
message.error('操作失败,请重试')
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
throw err
|
|
53
|
+
} finally {
|
|
54
|
+
loading.value = false
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { loading, save }
|
|
59
|
+
}
|