Skip to content

Commit 56e2f21

Browse files
committed
Vue3 重构:REVIEW 菜单
1 parent d72101b commit 56e2f21

File tree

7 files changed

+316
-350
lines changed

7 files changed

+316
-350
lines changed

src/api/system/menu/index.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,13 @@ export interface MenuVO {
1818
createTime: Date
1919
}
2020

21-
export interface MenuPageReqVO {
22-
name?: string
23-
status?: number
24-
}
25-
2621
// 查询菜单(精简)列表
27-
export const listSimpleMenusApi = () => {
22+
export const getSimpleMenusList = () => {
2823
return request.get({ url: '/system/menu/list-all-simple' })
2924
}
3025

3126
// 查询菜单列表
32-
export const getMenuListApi = (params: MenuPageReqVO) => {
27+
export const getMenuList = (params) => {
3328
return request.get({ url: '/system/menu/list', params })
3429
}
3530

@@ -39,16 +34,16 @@ export const getMenuApi = (id: number) => {
3934
}
4035

4136
// 新增菜单
42-
export const createMenuApi = (data: MenuVO) => {
37+
export const createMenu = (data: MenuVO) => {
4338
return request.post({ url: '/system/menu/create', data })
4439
}
4540

4641
// 修改菜单
47-
export const updateMenuApi = (data: MenuVO) => {
42+
export const updateMenu = (data: MenuVO) => {
4843
return request.put({ url: '/system/menu/update', data })
4944
}
5045

5146
// 删除菜单
52-
export const deleteMenuApi = (id: number) => {
47+
export const deleteMenu = (id: number) => {
5348
return request.delete({ url: '/system/menu/delete?id=' + id })
5449
}

src/views/infra/codegen/components/BasicInfoForm.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useForm } from '@/hooks/web/useForm'
66
import { FormSchema } from '@/types/form'
77
import { CodegenTableVO } from '@/api/infra/codegen/types'
88
import { getIntDictOptions } from '@/utils/dict'
9-
import { listSimpleMenusApi } from '@/api/system/menu'
9+
import { getSimpleMenusList } from '@/api/system/menu'
1010
import { handleTree, defaultProps } from '@/utils/tree'
1111
import { PropType } from 'vue'
1212
@@ -21,7 +21,7 @@ const templateTypeOptions = getIntDictOptions(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_T
2121
const sceneOptions = getIntDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE)
2222
const menuOptions = ref<any>([]) // 树形结构
2323
const getTree = async () => {
24-
const res = await listSimpleMenusApi()
24+
const res = await getSimpleMenusList()
2525
menuOptions.value = handleTree(res)
2626
}
2727

src/views/system/menu/MenuForm.vue

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
<template>
2+
<Dialog :title="modelTitle" v-model="modelVisible">
3+
<el-form
4+
ref="formRef"
5+
:model="formData"
6+
:rules="formRules"
7+
label-width="100px"
8+
v-loading="formLoading"
9+
>
10+
<el-form-item label="上级菜单">
11+
<el-tree-select
12+
node-key="id"
13+
v-model="formData.parentId"
14+
:props="defaultProps"
15+
:data="menuTree"
16+
:default-expanded-keys="[0]"
17+
check-strictly
18+
/>
19+
</el-form-item>
20+
<el-form-item label="菜单名称" prop="name">
21+
<el-input v-model="formData.name" placeholder="请输入菜单名称" clearable />
22+
</el-form-item>
23+
<el-form-item label="菜单类型" prop="type">
24+
<el-radio-group v-model="formData.type">
25+
<el-radio-button
26+
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
27+
:key="dict.label"
28+
:label="dict.value"
29+
>
30+
{{ dict.label }}
31+
</el-radio-button>
32+
</el-radio-group>
33+
</el-form-item>
34+
<el-form-item label="菜单图标" v-if="formData.type !== 3">
35+
<IconSelect v-model="formData.icon" clearable />
36+
</el-form-item>
37+
<el-form-item label="路由地址" prop="path" v-if="formData.type !== 3">
38+
<template #label>
39+
<Tooltip
40+
titel="路由地址"
41+
message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
42+
/>
43+
</template>
44+
<el-input v-model="formData.path" placeholder="请输入路由地址" clearable />
45+
</el-form-item>
46+
<el-form-item label="组件地址" prop="component" v-if="formData.type === 2">
47+
<el-input v-model="formData.component" placeholder="例如说:system/user/index" clearable />
48+
</el-form-item>
49+
<el-form-item label="组件名字" prop="componentName" v-if="formData.type === 2">
50+
<el-input v-model="formData.componentName" placeholder="例如说:SystemUser" clearable />
51+
</el-form-item>
52+
<el-form-item label="权限标识" prop="permission" v-if="formData.type !== 1">
53+
<template #label>
54+
<Tooltip
55+
titel="权限标识"
56+
message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
57+
/>
58+
</template>
59+
<el-input v-model="formData.permission" placeholder="请输入权限标识" clearable />
60+
</el-form-item>
61+
<el-form-item label="显示排序" prop="sort">
62+
<el-input-number v-model="formData.sort" controls-position="right" :min="0" clearable />
63+
</el-form-item>
64+
<el-form-item label="菜单状态" prop="status">
65+
<el-radio-group v-model="formData.status">
66+
<el-radio
67+
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
68+
:key="dict.label"
69+
:label="dict.value"
70+
>
71+
{{ dict.label }}
72+
</el-radio>
73+
</el-radio-group>
74+
</el-form-item>
75+
<el-form-item label="显示状态" prop="visible" v-if="formData.type !== 3">
76+
<template #label>
77+
<Tooltip titel="显示状态" message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问" />
78+
</template>
79+
<el-radio-group v-model="formData.visible">
80+
<el-radio border key="true" :label="true">显示</el-radio>
81+
<el-radio border key="false" :label="false">隐藏</el-radio>
82+
</el-radio-group>
83+
</el-form-item>
84+
<el-form-item label="总是显示" prop="alwaysShow" v-if="formData.type !== 3">
85+
<template #label>
86+
<Tooltip
87+
titel="总是显示"
88+
message="选择不是时,当该菜单只有一个子菜单时,不展示自己,直接展示子菜单"
89+
/>
90+
</template>
91+
<el-radio-group v-model="formData.alwaysShow">
92+
<el-radio border key="true" :label="true">总是</el-radio>
93+
<el-radio border key="false" :label="false">不是</el-radio>
94+
</el-radio-group>
95+
</el-form-item>
96+
<el-form-item label="缓存状态" prop="keepAlive" v-if="formData.type === 2">
97+
<template #label>
98+
<Tooltip
99+
titel="缓存状态"
100+
message="选择缓存时,则会被 `keep-alive` 缓存,必须填写「组件名称」字段"
101+
/>
102+
</template>
103+
<el-radio-group v-model="formData.keepAlive">
104+
<el-radio border key="true" :label="true">缓存</el-radio>
105+
<el-radio border key="false" :label="false">不缓存</el-radio>
106+
</el-radio-group>
107+
</el-form-item>
108+
</el-form>
109+
<template #footer>
110+
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
111+
<el-button @click="modelVisible = false">取 消</el-button>
112+
</template>
113+
</Dialog>
114+
</template>
115+
<script setup lang="ts">
116+
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
117+
import * as MenuApi from '@/api/system/menu'
118+
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
119+
import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'
120+
import { handleTree, defaultProps } from '@/utils/tree'
121+
const { wsCache } = useCache()
122+
const { t } = useI18n() // 国际化
123+
const message = useMessage() // 消息弹窗
124+
125+
const modelVisible = ref(false) // 弹窗的是否展示
126+
const modelTitle = ref('') // 弹窗的标题
127+
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
128+
const formType = ref('') // 表单的类型:create - 新增;update - 修改
129+
const formData = ref({
130+
id: 0,
131+
name: '',
132+
permission: '',
133+
type: SystemMenuTypeEnum.DIR,
134+
sort: Number(undefined),
135+
parentId: 0,
136+
path: '',
137+
icon: '',
138+
component: '',
139+
componentName: '',
140+
status: CommonStatusEnum.ENABLE,
141+
visible: true,
142+
keepAlive: true,
143+
alwaysShow: true
144+
})
145+
const formRules = reactive({
146+
name: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
147+
sort: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
148+
path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }],
149+
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
150+
})
151+
const formRef = ref() // 表单 Ref
152+
153+
/** 打开弹窗 */
154+
const open = async (type: string, id?: number, parentId?: number) => {
155+
modelVisible.value = true
156+
modelTitle.value = t('action.' + type)
157+
formType.value = type
158+
resetForm()
159+
if (parentId) {
160+
formData.value.parentId = parentId
161+
}
162+
// 修改时,设置数据
163+
if (id) {
164+
formLoading.value = true
165+
try {
166+
formData.value = await MenuApi.getMenuApi(id)
167+
} finally {
168+
formLoading.value = false
169+
}
170+
}
171+
// 获得菜单列表
172+
await getTree()
173+
}
174+
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
175+
176+
/** 提交表单 */
177+
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
178+
const submitForm = async () => {
179+
// 校验表单
180+
if (!formRef) return
181+
const valid = await formRef.value.validate()
182+
if (!valid) return
183+
// 提交请求
184+
formLoading.value = true
185+
try {
186+
if (
187+
formData.value.type === SystemMenuTypeEnum.DIR ||
188+
formData.value.type === SystemMenuTypeEnum.MENU
189+
) {
190+
if (!isExternal(formData.value.path)) {
191+
if (formData.value.parentId === 0 && formData.value.path.charAt(0) !== '/') {
192+
message.error('路径必须以 / 开头')
193+
return
194+
} else if (formData.value.parentId !== 0 && formData.value.path.charAt(0) === '/') {
195+
message.error('路径不能以 / 开头')
196+
return
197+
}
198+
}
199+
}
200+
const data = formData.value as unknown as MenuApi.MenuVO
201+
if (formType.value === 'create') {
202+
await MenuApi.createMenu(data)
203+
message.success(t('common.createSuccess'))
204+
} else {
205+
await MenuApi.updateMenu(data)
206+
message.success(t('common.updateSuccess'))
207+
}
208+
modelVisible.value = false
209+
// 发送操作成功的事件
210+
emit('success')
211+
} finally {
212+
formLoading.value = false
213+
// 清空,从而触发刷新
214+
wsCache.delete(CACHE_KEY.ROLE_ROUTERS)
215+
}
216+
}
217+
218+
/** 获取下拉框[上级菜单]的数据 */
219+
const menuTree = ref<Tree[]>([]) // 树形结构
220+
const getTree = async () => {
221+
menuTree.value = []
222+
const res = await MenuApi.getSimpleMenusList()
223+
let menu: Tree = { id: 0, name: '主类目', children: [] }
224+
menu.children = handleTree(res)
225+
menuTree.value.push(menu)
226+
}
227+
228+
/** 重置表单 */
229+
const resetForm = () => {
230+
formData.value = {
231+
id: 0,
232+
name: '',
233+
permission: '',
234+
type: SystemMenuTypeEnum.DIR,
235+
sort: Number(undefined),
236+
parentId: 0,
237+
path: '',
238+
icon: '',
239+
component: '',
240+
componentName: '',
241+
status: CommonStatusEnum.ENABLE,
242+
visible: true,
243+
keepAlive: true,
244+
alwaysShow: true
245+
}
246+
formRef.value?.resetFields()
247+
}
248+
249+
/** 判断 path 是不是外部的 HTTP 等链接 */
250+
const isExternal = (path: string) => {
251+
return /^(https?:|mailto:|tel:)/.test(path)
252+
}
253+
</script>

0 commit comments

Comments
 (0)