372 lines
8.7 KiB
Vue
372 lines
8.7 KiB
Vue
<template>
|
|
<div class="menu-edit-container">
|
|
<el-form
|
|
ref="menuFormRef"
|
|
:model="menuForm"
|
|
:rules="formRules"
|
|
label-position="top"
|
|
class="menu-form"
|
|
>
|
|
<el-row :gutter="30">
|
|
<!-- 左侧表单区域 -->
|
|
<el-col :span="12">
|
|
<el-form-item label="路由名称" prop="name">
|
|
<el-input
|
|
v-model="menuForm.name"
|
|
placeholder="请输入路由唯一名称"
|
|
clearable
|
|
/>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="菜单标题" prop="title">
|
|
<el-input
|
|
v-model="menuForm.title"
|
|
placeholder="请输入菜单标题"
|
|
clearable
|
|
/>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="路由路径" prop="path">
|
|
<el-input
|
|
v-model="menuForm.path"
|
|
placeholder="请输入路由路径,如 /dashboard"
|
|
clearable
|
|
/>
|
|
<div class="form-tip">如果是按钮权限,可留空</div>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="图标" prop="icon">
|
|
<el-input
|
|
v-model="menuForm.icon"
|
|
placeholder="请输入图标类名,如 el-icon-menu"
|
|
clearable
|
|
>
|
|
</el-input>
|
|
<div v-if="menuForm.icon" class="icon-preview">
|
|
<i
|
|
:class="menuForm.icon"
|
|
style="font-size: 24px; margin-top: 8px"
|
|
></i>
|
|
</div>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="授权码" prop="auths">
|
|
<el-input
|
|
v-model="menuForm.auths"
|
|
placeholder="请输入授权码,多个用逗号分隔"
|
|
clearable
|
|
/>
|
|
<div class="form-tip">按钮权限需要的授权码</div>
|
|
</el-form-item>
|
|
</el-col>
|
|
|
|
<!-- 右侧表单区域 -->
|
|
<el-col :span="12">
|
|
<el-form-item label="父级菜单" prop="parentId">
|
|
<el-tree-select
|
|
v-model="menuForm.parentId"
|
|
:data="menuTreeData"
|
|
:props="treeProps"
|
|
check-strictly
|
|
placeholder="请选择父级菜单"
|
|
clearable
|
|
:expand-on-click-node="false"
|
|
:render-after-expand="false"
|
|
:default-expand-all="true"
|
|
:highlight-current="true"
|
|
/>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="排序排名" prop="rank">
|
|
<el-input-number
|
|
v-model="menuForm.rank"
|
|
:min="0"
|
|
:max="100"
|
|
controls-position="right"
|
|
/>
|
|
<div class="form-tip">数值越小排名越靠前</div>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="菜单类型">
|
|
<el-switch
|
|
v-model="menuForm.isButton"
|
|
active-text="按钮权限"
|
|
inactive-text="常规菜单"
|
|
style="
|
|
--el-switch-on-color: #13ce66;
|
|
--el-switch-off-color: #409eff;
|
|
"
|
|
/>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="显示状态">
|
|
<el-switch
|
|
v-model="menuForm.showLink"
|
|
active-text="显示"
|
|
inactive-text="隐藏"
|
|
/>
|
|
</el-form-item>
|
|
|
|
<div v-if="menuForm.isButton" class="button-permission-info">
|
|
<el-alert title="按钮权限说明" type="info" :closable="false">
|
|
<p>1. 按钮权限不会显示在导航菜单中</p>
|
|
<p>2. 需要填写授权码用于权限控制</p>
|
|
<p>3. 路由路径可留空</p>
|
|
</el-alert>
|
|
</div>
|
|
</el-col>
|
|
</el-row>
|
|
</el-form>
|
|
|
|
<el-divider />
|
|
<div class="header">
|
|
<div class="header-actions">
|
|
<el-button @click="resetForm">重置</el-button>
|
|
<el-button type="primary" @click="submitForm" :loading="submitting">
|
|
{{ isEditMode ? "更新菜单" : "创建菜单" }}
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { defineComponent, ref, reactive, onMounted, onBeforeMount } from "vue";
|
|
import { MenuItem, Edit } from "@/api/menu";
|
|
import {
|
|
ElMessage,
|
|
ElMessageBox,
|
|
FormInstance,
|
|
FormValidateCallback
|
|
} from "element-plus";
|
|
import propTypes from "@/utils/propTypes";
|
|
defineOptions({
|
|
name: "MenuEdit"
|
|
});
|
|
const props = defineProps({
|
|
treeData: {
|
|
type: Object as PropType<MenuItem[]>
|
|
},
|
|
info: {
|
|
type: Object as PropType<MenuItem>,
|
|
default: null
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(["callbackFun"]);
|
|
const menuFormRef = ref<FormInstance>();
|
|
const menuForm = reactive<MenuItem>({
|
|
name: "",
|
|
path: "",
|
|
isButton: false,
|
|
title: "",
|
|
icon: "",
|
|
auths: "",
|
|
rank: 50,
|
|
showLink: true,
|
|
parentId: undefined
|
|
});
|
|
|
|
const menuTreeData = ref<MenuItem[]>(props.treeData);
|
|
|
|
const treeProps = {
|
|
value: "id",
|
|
label: "title",
|
|
children: "children"
|
|
};
|
|
|
|
const isEditMode = ref(false);
|
|
const submitting = ref(false);
|
|
const iconDialogVisible = ref(false);
|
|
|
|
// 模拟一些常用图标
|
|
const icons = ref([
|
|
"el-icon-menu",
|
|
"el-icon-setting",
|
|
"el-icon-user",
|
|
"el-icon-s-home",
|
|
"el-icon-s-data",
|
|
"el-icon-s-check",
|
|
"el-icon-s-opportunity",
|
|
"el-icon-s-order",
|
|
"el-icon-s-platform",
|
|
"el-icon-s-promotion",
|
|
"el-icon-s-shop",
|
|
"el-icon-s-marketing",
|
|
"el-icon-s-flag",
|
|
"el-icon-s-comment",
|
|
"el-icon-s-finance"
|
|
]);
|
|
|
|
const formRules = {
|
|
name: [
|
|
{ required: true, message: "请输入菜单名称", trigger: "blur" },
|
|
{ min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" }
|
|
],
|
|
title: [
|
|
{ required: true, message: "请输入菜单标题", trigger: "blur" },
|
|
{ min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" }
|
|
],
|
|
path: [
|
|
{
|
|
validator: (rule: any, value: string, callback: any) => {
|
|
if (!menuForm.isButton && !value) {
|
|
callback(new Error("常规菜单必须填写路由路径"));
|
|
} else {
|
|
callback();
|
|
}
|
|
},
|
|
trigger: "blur"
|
|
}
|
|
],
|
|
auths: [
|
|
{
|
|
validator: (rule: any, value: string, callback: any) => {
|
|
if (menuForm.isButton && !value) {
|
|
callback(new Error("按钮权限必须填写授权码"));
|
|
} else {
|
|
callback();
|
|
}
|
|
},
|
|
trigger: "blur"
|
|
}
|
|
],
|
|
rank: [{ required: true, message: "请输入排序值", trigger: "blur" }]
|
|
};
|
|
|
|
// 初始化数据
|
|
onMounted(() => {
|
|
Object.assign(menuForm, props.info);
|
|
if (props.info != null && props.info.id > 0) {
|
|
isEditMode.value = true;
|
|
} else menuForm.id = 0;
|
|
});
|
|
const submitForm = () => {
|
|
menuFormRef.value?.validate(valid => {
|
|
if (valid) {
|
|
submitting.value = true;
|
|
Edit(menuForm).then(res => {
|
|
submitting.value = false;
|
|
ElMessage.success(
|
|
isEditMode.value ? "菜单更新成功!" : "菜单创建成功!"
|
|
);
|
|
goBack();
|
|
});
|
|
} else {
|
|
ElMessage.warning("请正确填写表单内容");
|
|
}
|
|
});
|
|
};
|
|
|
|
// 重置表单
|
|
const resetForm = () => {
|
|
ElMessageBox.confirm("确定要重置表单内容吗?", "提示", {
|
|
confirmButtonText: "确定",
|
|
cancelButtonText: "取消",
|
|
type: "warning"
|
|
}).then(() => {
|
|
menuFormRef.value?.resetFields();
|
|
if (!isEditMode.value) {
|
|
Object.assign(menuForm, {
|
|
isButton: false,
|
|
showLink: true,
|
|
rank: 50,
|
|
parentId: menuForm.parentId
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
// 返回菜单列表
|
|
const goBack = () => emit("callbackFun"); // 传参给父组件;
|
|
</script>
|
|
|
|
<style scoped>
|
|
.menu-edit-container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.header h1 {
|
|
margin: 0;
|
|
font-size: 24px;
|
|
color: #303133;
|
|
}
|
|
|
|
.header-actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.menu-form {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.form-tip {
|
|
font-size: 12px;
|
|
color: #909399;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.button-permission-info {
|
|
margin-top: 20px;
|
|
padding: 15px;
|
|
background-color: #f4f4f5;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.icon-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(6, 1fr);
|
|
gap: 15px;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.icon-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 15px 10px;
|
|
border: 1px solid #ebeef5;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.icon-item:hover {
|
|
background-color: #ecf5ff;
|
|
border-color: #409eff;
|
|
}
|
|
|
|
.icon-item.selected {
|
|
background-color: #ecf5ff;
|
|
border-color: #409eff;
|
|
color: #409eff;
|
|
}
|
|
|
|
.icon-item i {
|
|
font-size: 24px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.icon-name {
|
|
font-size: 12px;
|
|
text-align: center;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.el-alert p {
|
|
margin: 5px 0;
|
|
font-size: 13px;
|
|
line-height: 1.5;
|
|
}
|
|
</style>
|