本篇文章开始我们便进入了真正的页面开发阶段,首先要做的是一个菜单管理的页面,所实现的功能其实就是对菜单的增删改查功能。效果图如下

其中条件查询部分使用elementplus
的el-form
组件,先定义一下查询的参数
const queryParams = reactive({
title: "",
status: "",
});
const dickStatus = [
{
label: "全部",
value: "",
},
{
label: "启用",
value: 1,
},
{
label: "禁用",
value: 0,
},
];
然后模板中使用el-form
组件,并绑定相关参数,同时为搜索按钮加上权限system:menu:list
,如果没有权限则隐藏该按钮。
<el-form :model="queryParams" ref="queryRef" :inline="true">
<el-form-item label="菜单名称" prop="menuName">
<el-input
v-model="queryParams.title"
placeholder="请输入菜单名称"
clearable
/>
</el-form-item>
<el-form-item label="状态" prop="status" class="w-[150px]">
<el-select v-model="queryParams.status" placeholder="菜单状态" clearable>
<el-option
v-for="dict in dickStatus"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
v-hasPerm="['system:menu:list']"
icon="Search"
@click="handleQuery"
>搜索</el-button
>
</el-form-item>
</el-form>
同样的,表格使用el-table
组件,它是支持树类型的数据的显示, 当 row 中包含 children 字段时,被视为树形数据。
<el-table
:data="tableData"
style="width: 100%; margin-bottom: 20px"
row-key="id"
:default-expand-all="isExpandAll"
>
<el-table-column prop="title" label="菜单名" />
<el-table-column prop="order_num" label="排序" />
<el-table-column prop="path" label="路由" />
<el-table-column prop="permission" label="权限字段" />
<el-table-column prop="component" label="组件路径" />
<el-table-column label="图标" width="80">
<template #default="scope">
<component
v-if="scope.row.icon"
class="w-[20px] mr-2 ml-1"
:is="scope.row.icon"
/>
</template>
</el-table-column>
<el-table-column prop="create_time" label="创建时间" />
<el-table-column prop="status" label="状态" width="80">
<template #default="scope">
<el-switch
@change="changeStatus(scope.row)"
:model-value="!!scope.row.status"
></el-switch>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
width="200"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-link
type="primary"
icon="Plus"
class="mr-1"
@click="handleAdd(scope.row)"
v-hasPerm="['system:menu:add']"
>新增</el-link
>
<el-link
type="primary"
icon="Edit"
class="mr-1"
@click="handleUpdate(scope.row)"
v-hasPerm="['system:menu:edit']"
>修改</el-link
>
<el-link
type="danger"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPerm="['system:menu:delete']"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
同时为新增
,修改
,删除
按钮加上权限,如果没有权限则隐藏该按钮。然后我们需要在api/menu/index.ts
定义一下这些接口
//新增菜单
export const addMenu = (data: any) => {
return request({
url: "/menu/createMenu",
data,
method: "post",
});
};
//获取菜单列表
export const getMenuList = (query: any) => {
return request({
url: "/menu/list",
method: "get",
params: query,
});
};
//删除菜单
export const deleteMenu = (menuId: number | number[]) => {
return request({
url: `/menu/deleteMenu/${menuId}`,
method: "delete",
});
};
//更新菜单
export const updateMenu = (data: any) => {
return request({
url: `/menu/updateMenu`,
method: "put",
data,
});
};
接下来我们就来实现这些增删改查
功能。
查询菜单
查询功能很简单直接调用查询接口然后给表格数据赋值即可
const tableData = ref<MenuList[]>([]);
const getList = async () => {
const { data } = await getMenuList(queryParams);
tableData.value = data;
};
这里查询条件为空默认是查询所有的菜单,如果有条件则查询符合条件的菜单。其中菜单名的查询是模糊查询。我们用管理员账户登录测试一下。注意测试之前需要将按钮的权限添加到菜单表中,或者先先把指令去掉。如system:menu:list
按钮权限,这里后面会实现

然后点击输入一个菜单名称然后点击搜索按钮,可以看到查询到了符合条件的菜单。

新增和修改菜单
新增和修改菜单需要用弹出一个对话框,这里使用Dialog
组件,同时根据菜单类型来显示不同的字段。并且使用rules
规定了其中一些字段的规则
<el-dialog
v-model="dialogVisible"
:title="isUpdate ? '修改菜单' : '新增菜单'"
width="670px"
>
<el-form :model="form" ref="ruleFormRef" label-width="120px" :rules="rules">
<el-row>
<el-col :span="24">
<el-form-item label="上级菜单">
<el-cascader
v-model="form.parent_id"
:show-all-levels="false"
@change="handleMenuChange"
:options="tableData"
placeholder="请选择菜单"
checkStrictly
:props="defaultProps"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="菜单类型" prop="menu_type">
<el-radio-group v-model="form.menu_type">
<el-radio :value="1">目录</el-radio>
<el-radio :value="2">菜单</el-radio>
<el-radio :value="3">按钮</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单名称" prop="title">
<el-input v-model="form.title" placeholder="请输入菜单名称" />
</el-form-item>
</el-col>
<el-col :span="12" v-show="form.menu_type === 2">
<el-form-item label="组件路径">
<el-input v-model="form.component" placeholder="请输入组件路径" />
</el-form-item>
</el-col>
<el-col :span="24" v-show="form.menu_type !== 3">
<el-form-item class="relative" label="图标">
<el-input
:prefix-icon="form.icon"
@click="showIconView = true"
v-model="form.icon"
readonly
placeholder="请选择图标"
/>
<div
v-if="showIconView"
class="border absolute top-10 bg-[#fff] z-10 border-solid p-4 h-[200px] overflow-y-scroll"
>
<selectIcon @change="getMenuIconName" />
</div>
</el-form-item>
</el-col>
<el-col :span="12" v-show="form.menu_type !== 1">
<el-form-item>
<el-input
v-model="form.permission"
placeholder="请输入权限标识"
maxlength="100"
/>
<template #label>
<span>
<el-tooltip
content="控制器中定义的权限字符,如: @Permissions('system:menu:list')"
>
<component class="w-[18px] inline" is="Warning" />
</el-tooltip>
权限字符
</span>
</template>
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.menu_type !== 3">
<el-form-item label="路由地址" prop="path">
<el-input v-model="form.path" placeholder="请输入路由地址" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="排序">
<el-input v-model="form.order_num" type="number" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="显示状态">
<el-radio-group v-model="form.status">
<el-radio :value="1">启用</el-radio>
<el-radio :value="0">不启用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" v-show="form.menu_type === 2">
<el-form-item label="是否缓存">
<el-radio-group v-model="form.catch">
<el-radio :value="1">缓存</el-radio>
<el-radio :value="0">不缓存</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handelMenuList(ruleFormRef)">
确定
</el-button>
</span>
</template>
</el-dialog>
图标选择的时候需要使用selectIcon
组件,这是一个自定义的组件如下
<template>
<div class="flex flex-wrap">
<div
v-for="item in ElementPlusIconsVue"
@click="emits('change', item.name)"
class="p-3 border border-solid cursor-pointer"
:key="item.name"
>
<component class="w-5" :is="item.name" />
</div>
</div>
</template>
<script lang="ts" setup>
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
type Emits = {
(e: "change", val: string): void;
};
const emits = defineEmits<Emits>();
</script>
其中每次新增和修改菜单的时候需要将form
表单重置一下,然后根据isUpdate
的值来判断是新增还是修改。
const rules = ref({
title: [{ required: true, message: "菜单名称不能为空", trigger: "blur" }],
path: [{ required: true, message: "路由地址不能为空", trigger: "blur" }],
});
//是否显示图标
const showIconView = ref(false);
const isUpdate = ref(false);
interface MenuForm {
title: string;
path: string;
parent_id: any;
component: string;
order_num: number;
icon: string;
id: number | null;
menu_type: number;
permission: string;
status: number;
catch: number;
}
const form = ref<MenuForm>({} as MenuForm);
//新增
const handleAdd = (row?: MenuForm) => {
resetForm();
if (row?.id) {
//如果选择从列表里新增,赋值父级菜单id
form.value.parent_id = row.id;
}
dialogVisible.value = true;
isUpdate.value = false;
};
//编辑
const handleUpdate = (row: MenuForm) => {
resetForm();
isUpdate.value = true;
form.value = deepClone(row);
dialogVisible.value = true;
};
//重置表单
const resetForm = () => {
form.value = {
title: "",
path: "",
parent_id: null,
component: "",
order_num: 0,
icon: "",
id: null,
menu_type: 2,
permission: "",
status: 1,
catch: 0,
};
};
const handelMenuList = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate(async (valid, fields) => {
if (valid) {
dialogVisible.value = false;
if (isUpdate.value) {
await updateMenu(form.value);
ElMessage({
type: "success",
message: "修改成功",
});
handleQuery();
return;
}
await addMenu(form.value);
ElMessage({
type: "success",
message: "新增成功",
});
handleQuery();
} else {
console.log("error submit!", fields);
}
});
//获取选择菜单图标名称
const getMenuIconName = (menuName: string) => {
form.value.icon = menuName;
showIconView.value = false;
};
};
//选择父级菜单
const handleMenuChange = (val: any) => {
form.value.parent_id = val[val.length - 1];
};
//更新状态
const changeStatus = async (row: MenuForm) => {
const uptateRow: MenuForm = {} as MenuForm;
uptateRow.status = row.status === 1 ? 0 : 1;
uptateRow.id = row.id;
await updateMenu(uptateRow);
ElMessage({
type: "success",
message: "状态更新成功",
});
getList();
};
最后看一下效果

删除菜单
最后看一下删除菜单,这里实现的逻辑是:如果删除的菜单有子菜单则会将其下的所有子菜单删除,所以我们传入的是一些 id 的数组。这时候当我们删除的菜单有子菜单的时候就需要将子菜单的 id 和自身的 id 都传入删除接口。因此我们用递归的方式先实现一个获取自身及子菜单 id 的函数
//获取自身及子菜单id
const getTreesId = (row: MenuList): number[] => {
const ids: number[] = [];
ids.push(row.id);
if (row.children) {
row.children.forEach((item: MenuList) => {
if (item.children) {
const childIds = getTreesId(item);
ids.push(...childIds);
} else {
ids.push(item.id);
}
});
}
return ids;
};
然后删除的时候用这个函数处理一下即可
//删除
const handleDelete = async (row: MenuList) => {
await ElMessageBox.confirm("确认删除此条菜单及子菜单吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
});
const ids = getTreesId(row);
await deleteMenu(ids);
ElMessage({
type: "success",
message: "删除成功",
});
handleQuery();
};
到这里我们便完成了菜单管理的所有功能
源码地址( https://github.com/qddidi/fs-admin) 各位看官们可以去下载源码,喜欢的话可以给个 star 哦!
原文始发于微信公众号(web前端进阶):Vue + ElementPlus 实现权限管理系统(七): 实现菜单管理功能
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/305612.html