Files
ReelShortWeb/src/views/permission/composables/useMenuManagement.js
T

166 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
}
}