229 lines
5.0 KiB
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>
|