Learn.Archives.Web/src/views/menu/edit.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>