166 lines
4.4 KiB
JavaScript
166 lines
4.4 KiB
JavaScript
// Author: fruit.com.cn
|
||
|
||
import { computed, onMounted, reactive, ref } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { createMenuApi, deleteMenuApi, getMenuTreeApi, updateMenuApi } from '@/api/admin/role'
|
||
import { MenuIconOptions, normalizeAppIcon } from '@/constants/icons'
|
||
|
||
export const menuIconNames = MenuIconOptions
|
||
|
||
/** 菜单管理:直接读写后端 sys_menu,变更后重新登录生效。 */
|
||
export function useMenuManagement() {
|
||
const menuTree = ref([])
|
||
const loading = ref(false)
|
||
|
||
const form = reactive({
|
||
id: null,
|
||
parentId: 0,
|
||
title: '',
|
||
routePath: '',
|
||
routeName: '',
|
||
component: '',
|
||
icon: '',
|
||
permissionCode: '',
|
||
type: 'MENU',
|
||
sort: 1,
|
||
visible: 1,
|
||
status: 1
|
||
})
|
||
|
||
function mapNodes(nodes) {
|
||
return (nodes || []).map((node) => ({
|
||
id: node.id,
|
||
label: node.meta?.title || node.path || String(node.id),
|
||
raw: node,
|
||
children: node.children?.length ? mapNodes(node.children) : undefined
|
||
}))
|
||
}
|
||
|
||
const treeData = computed(() => mapNodes(menuTree.value))
|
||
|
||
const parentOptions = computed(() => {
|
||
// 顶级菜单哨兵跟接口的 parentId 一样用字符串 "0":菜单 id 是字符串,
|
||
// 数字 0 会在下拉里对不上、回显成裸 "0"
|
||
const options = [{ id: '0', label: '顶级菜单' }]
|
||
const walk = (nodes, depth) => {
|
||
;(nodes || []).forEach((node) => {
|
||
if (node.type !== 'BUTTON') {
|
||
options.push({
|
||
id: node.id,
|
||
label: `${' '.repeat(depth)}${node.meta?.title || node.path || node.id}`
|
||
})
|
||
}
|
||
if (node.children?.length) walk(node.children, depth + 1)
|
||
})
|
||
}
|
||
walk(menuTree.value, 0)
|
||
return options
|
||
})
|
||
|
||
async function load() {
|
||
loading.value = true
|
||
try {
|
||
const res = await getMenuTreeApi()
|
||
menuTree.value = res.data || []
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function resetForm(parentId = '0') {
|
||
Object.assign(form, {
|
||
id: null,
|
||
parentId,
|
||
title: '',
|
||
routePath: '',
|
||
routeName: '',
|
||
component: '',
|
||
icon: '',
|
||
permissionCode: '',
|
||
type: 'MENU',
|
||
sort: 1,
|
||
visible: 1,
|
||
status: 1
|
||
})
|
||
}
|
||
|
||
function selectNode(node) {
|
||
const raw = node.raw || {}
|
||
Object.assign(form, {
|
||
id: raw.id,
|
||
parentId: raw.parentId ?? '0',
|
||
title: raw.meta?.title || '',
|
||
routePath: raw.path || '',
|
||
routeName: raw.name || '',
|
||
component: raw.component === '#' ? '' : raw.component || '',
|
||
icon: raw.type === 'CATALOG' ? normalizeAppIcon(raw.icon) : '',
|
||
permissionCode: raw.permissionCode || '',
|
||
type: raw.type || 'MENU',
|
||
sort: raw.sort ?? 1,
|
||
visible: raw.visible ?? 1,
|
||
status: raw.status ?? 1
|
||
})
|
||
}
|
||
|
||
async function saveMenu() {
|
||
if (!form.title.trim()) return ElMessage.warning('请填写菜单名称')
|
||
if (form.type !== 'BUTTON' && !form.routePath.trim()) {
|
||
return ElMessage.warning('请填写对应路由')
|
||
}
|
||
const payload = {
|
||
parentId: form.parentId || '0',
|
||
title: form.title.trim(),
|
||
routePath: form.routePath.trim(),
|
||
routeName: form.routeName.trim(),
|
||
component: form.type === 'CATALOG' ? '#' : form.component.trim(),
|
||
icon: form.type === 'CATALOG' ? normalizeAppIcon(form.icon) : '',
|
||
permissionCode: form.permissionCode.trim(),
|
||
type: form.type,
|
||
sort: Number(form.sort) || 1,
|
||
visible: form.visible,
|
||
status: form.status
|
||
}
|
||
const isCreate = !form.id
|
||
if (isCreate) {
|
||
await createMenuApi(payload)
|
||
} else {
|
||
await updateMenuApi(form.id, payload)
|
||
}
|
||
await load()
|
||
resetForm()
|
||
ElMessage.success(isCreate ? '菜单已添加,重新登录后生效' : '菜单已保存,重新登录后生效')
|
||
}
|
||
|
||
async function deleteMenu() {
|
||
if (!form.id) return ElMessage.warning('请先在左侧选择一个菜单')
|
||
try {
|
||
await ElMessageBox.confirm(`确认删除「${form.title}」菜单吗?`, '删除确认', {
|
||
confirmButtonText: '确认删除',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
confirmButtonClass: 'el-button--danger'
|
||
})
|
||
} catch {
|
||
return
|
||
}
|
||
await deleteMenuApi(form.id)
|
||
ElMessage.success('菜单已删除,重新登录后生效')
|
||
resetForm()
|
||
await load()
|
||
}
|
||
|
||
onMounted(load)
|
||
|
||
return {
|
||
menuTree,
|
||
treeData,
|
||
loading,
|
||
form,
|
||
parentOptions,
|
||
resetForm,
|
||
selectNode,
|
||
saveMenu,
|
||
deleteMenu
|
||
}
|
||
}
|