Learn.Archives.Web/src/views/menu/index.vue

229 lines
5.0 KiB
Vue

<template>
<div>
<div style="padding-bottom: 5px">
<el-button type="success" @click="() => showDialog(null)">
新增根菜单
</el-button>
<el-button type="primary"> 分配权限 </el-button>
</div>
<el-tree
:data="treeData"
:props="treeProps"
node-key="id"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
show-checkbox
class="menu-tree"
>
<template #default="{ node, data }">
<div class="menu-node">
<i v-if="data.icon" :class="data.icon" class="menu-icon"></i>
<span class="menu-title">{{ data.title }}</span>
<span class="menu-path-Rank" v-if="data.rank"
>排序[{{ data.rank }}]</span
>
<span class="menu-path" v-if="data.path">{{ data.path }}</span>
</div>
<div style="display: flex; gap: 6px">
<el-button
type="success"
link
@click="() => showDialog({ parentId: data.id } as MenuItem)"
>
添加
</el-button>
<el-button type="primary" link @click="() => showDialog(data)">
编辑
</el-button>
<el-button type="danger" link @click="() => {}"> 删除 </el-button>
</div>
</template>
</el-tree>
<el-dialog
v-if="dialogData.visible"
v-model="dialogData.visible"
title="菜单编辑"
width="800"
:before-close="handleClose"
>
<MenuEdit
@callbackFun="EditCallback"
:info="dialogData.info"
:treeData="treeData"
></MenuEdit>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { MenuAll, MenuItem } from "@/api/menu";
import { ElMessage, ElMessageBox } from "element-plus";
import MenuEdit from "./edit.vue";
import { ref, computed, onMounted } from "vue";
defineOptions({
name: "Menu"
});
/** 显示弹窗 */
const dialogData = ref({
visible: false,
info: null
});
function showDialog(info: MenuItem) {
dialogData.value.visible = true;
dialogData.value.info = info;
}
async function EditCallback(info: MenuItem) {
await fetchInitData();
dialogData.value.visible = false;
}
const handleClose = (done: () => void) => {
done();
// ElMessageBox.confirm("确定要关闭对话框?")
// .then(() => {
// done();
// })
// .catch(() => {
// // catch error
// });
};
// 将扁平数据转换为树形结构
const convertToTree = (menus: MenuItem[]): MenuItem[] => {
const menuMap = new Map<number, MenuItem>();
const tree: MenuItem[] = [];
// 创建映射并初始化children
menus.forEach(menu => {
menuMap.set(menu.id, { ...menu, children: [] });
});
// 构建树形结构
menuMap.forEach(menu => {
if (menu.parentId === 0) {
tree.push(menu);
} else {
const parent = menuMap.get(menu.parentId);
if (parent) {
parent.children!.push(menu);
}
}
});
// 对同级菜单排序
const sortChildren = (nodes: MenuItem[]) => {
nodes.sort((a, b) => a.rank - b.rank);
nodes.forEach(node => {
if (node.children && node.children.length > 0) {
sortChildren(node.children);
}
});
};
sortChildren(tree);
return tree;
};
// 树形结构数据
const treeData = ref<MenuItem[]>([]);
// 树组件配置
const treeProps = {
label: "title",
children: "children"
};
async function fetchInitData() {
const flatData = await MenuAll();
if (flatData.code === 200) {
treeData.value = convertToTree(flatData.data);
} else {
ElMessage.error(flatData.message);
}
}
onMounted(async () => {
await fetchInitData();
});
</script>
<style scoped>
.menu-tree {
padding: 15px;
background: #fff;
border-radius: 4px;
}
.menu-node {
display: flex;
align-items: center;
width: 100%;
padding: 5px 0;
}
.menu-icon {
margin-right: 8px;
font-size: 16px;
color: #409eff;
}
.menu-title {
font-size: 14px;
margin-right: 10px;
}
.menu-path-Rank {
font-size: 13px;
margin-right: 10px;
color: #909399;
}
.menu-path {
font-size: 12px;
color: #909399;
margin-right: 10px;
flex-grow: 0;
}
.menu-tree {
padding: 20px; /* 增加内边距 */
background: #fff;
border-radius: 4px;
font-size: 16px; /* 增大整体字体 */
}
/* 使用深度选择器修改树节点样式 */
:deep(.el-tree-node) {
margin-bottom: 12px; /* 增加节点间距 */
}
:deep(.el-tree-node__content) {
height: 48px; /* 增加节点高度 */
padding: 10px 0; /* 增加垂直内边距 */
}
.menu-node {
display: flex;
align-items: center;
width: 100%;
padding: 8px 0; /* 增加内边距 */
}
.menu-icon {
margin-right: 12px; /* 增加图标右边距 */
font-size: 20px; /* 增大图标 */
color: #409eff;
}
.menu-title {
font-size: 18px; /* 增大标题字体 */
margin-right: 15px; /* 增加右边距 */
font-weight: 500; /* 加粗字体 */
}
.menu-path {
font-size: 16px; /* 增大路径字体 */
color: #909399;
margin-right: 15px; /* 增加右边距 */
flex-grow: 1;
}
</style>