You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
WeChat/src/views/modules/sys/menu-add-or-update.vue

253 lines
8.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<!-- 弹窗组件标题根据dataForm.id判断是新增还是修改 -->
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<!-- 表单组件绑定数据模型和验证规则 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<!-- 菜单类型选择 -->
<el-form-item label="类型" prop="type">
<el-radio-group v-model="dataForm.type">
<!-- 循环渲染可选择的菜单类型 -->
<el-radio v-for="(type, index) in dataForm.typeList" :label="index" :key="index">{{ type }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 菜单名称输入框 -->
<el-form-item :label="dataForm.typeList[dataForm.type] + '名称'" prop="name">
<el-input v-model="dataForm.name" :placeholder="dataForm.typeList[dataForm.type] + '名称'"></el-input>
</el-form-item>
<!-- 上级菜单选择 -->
<el-form-item label="上级菜单" prop="parentName">
<!-- 弹出菜单树选择 -->
<el-popover ref="menuListPopover" placement="bottom-start" trigger="click">
<el-tree :data="menuList" :props="menuListTreeProps" node-key="menuId" ref="menuListTree"
@current-change="menuListTreeCurrentChangeHandle" :default-expand-all="true"
:highlight-current="true" :expand-on-click-node="false">
</el-tree>
</el-popover>
<!-- 显示上级菜单选择 -->
<el-input v-model="dataForm.parentName" v-popover:menuListPopover :readonly="true"
placeholder="点击选择上级菜单" class="menu-list__input"></el-input>
</el-form-item>
<!-- 菜单路由(仅在选择菜单类型时显示) -->
<el-form-item v-if="dataForm.type === 1" label="菜单路由" prop="url">
<el-input v-model="dataForm.url" placeholder="菜单路由"></el-input>
</el-form-item>
<!-- 授权标识(仅在类型不为目录时显示) -->
<el-form-item v-if="dataForm.type !== 0" label="授权标识" prop="perms">
<el-input v-model="dataForm.perms" placeholder="多个用逗号分隔, 如: user:list,user:create"></el-input>
</el-form-item>
<!-- 菜单图标(仅在类型不为按钮时显示) -->
<el-form-item v-if="dataForm.type !== 2" label="菜单图标" prop="icon">
<el-row>
<el-col :span="12">
<el-input v-model="dataForm.icon" placeholder="菜单图标名称" class="icon-list__input"></el-input>
</el-col>
<el-col :span="12" class="icon-list__tips">
<!-- 排序号输入框(仅在类型不为按钮时显示) -->
<el-form-item v-if="dataForm.type !== 2" label="排序号" prop="orderNum">
<el-input-number v-model="dataForm.orderNum" controls-position="right" :min="0" label="排序号"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<!-- 提示用户参考图标库 -->
<div>参考ElementUI图标库, 找图标</div>
</el-form>
<!-- 弹窗底部按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
// 引入工具函数,用于处理树形数据
import { treeDataTranslate } from '@/utils'
export default {
data() {
// 验证URL的函数
var validateUrl = (rule, value, callback) => {
// 如果类型是菜单且URL为空返回错误
if (this.dataForm.type === 1 && !/\S/.test(value)) {
callback(new Error('菜单URL不能为空'))
} else {
callback() // 验证通过
}
}
return {
visible: false, // 控制弹窗的显示与隐藏
dataForm: {
id: 0, // 菜单ID新增时为0
type: 1, // 菜单类型0-目录1-菜单2-按钮)
typeList: ['目录', '菜单', '按钮'], // 菜单类型列表
name: '', // 菜单名称
parentId: 0, // 上级菜单ID
parentName: '', // 上级菜单名称
url: '', // 菜单路由
perms: '', // 授权标识
orderNum: 0, // 排序号
icon: '', // 菜单图标
},
// 表单验证规则
dataRule: {
name: [
{ required: true, message: '菜单名称不能为空', trigger: 'blur' } // 菜单名称必填
],
parentName: [
{ required: true, message: '上级菜单不能为空', trigger: 'change' } // 上级菜单必填
],
url: [
{ validator: validateUrl, trigger: 'blur' } // URL验证
]
},
menuList: [], // 菜单列表,供菜单树使用
menuListTreeProps: {
label: 'name', // 树节点显示的字段名称
children: 'children' // 树节点的子节点字段
}
}
},
methods: {
// 初始化方法,用于新增或修改菜单
init(id) {
this.dataForm.id = id || 0 // 设置菜单ID
this.$http({
url: this.$http.adornUrl('/sys/menu/select'),
method: 'get',
params: this.$http.adornParams()
}).then(({ data }) => {
// 获取菜单列表并转换为树形结构
this.menuList = treeDataTranslate(data.menuList, 'menuId')
}).then(() => {
this.visible = true // 显示弹窗
this.$nextTick(() => {
this.$refs['dataForm'].resetFields() // 重置表单字段
})
}).then(() => {
if (!this.dataForm.id) {
// 新增时,设置当前选中的上级菜单
this.menuListTreeSetCurrentNode()
} else {
// 修改时,获取原数据
this.$http({
url: this.$http.adornUrl(`/sys/menu/info/${this.dataForm.id}`),
method: 'get',
params: this.$http.adornParams()
}).then(({ data }) => {
// 设置表单字段为获取到的原数据
this.dataForm.id = data.menu.menuId
this.dataForm.type = data.menu.type
this.dataForm.name = data.menu.name
this.dataForm.parentId = data.menu.parentId
this.dataForm.url = data.menu.url
this.dataForm.perms = data.menu.perms
this.dataForm.orderNum = data.menu.orderNum
this.dataForm.icon = data.menu.icon
this.menuListTreeSetCurrentNode() // 设置当前选中的菜单树节点
})
}
})
},
// 处理菜单树节点选择事件
menuListTreeCurrentChangeHandle(data, node) {
this.dataForm.parentId = data.menuId // 更新上级菜单ID
this.dataForm.parentName = data.name // 更新上级菜单名称
},
// 设置菜单树当前选中节点
menuListTreeSetCurrentNode() {
this.$refs.menuListTree.setCurrentKey(this.dataForm.parentId) // 设置当前选中的树节点
this.dataForm.parentName = (this.$refs.menuListTree.getCurrentNode() || {})['name'] // 更新上级菜单名称
},
// 表单提交方法
dataFormSubmit() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
// 提交表单数据到后台
this.$http({
url: this.$http.adornUrl(`/sys/menu/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
'menuId': this.dataForm.id || undefined,
'type': this.dataForm.type,
'name': this.dataForm.name,
'parentId': this.dataForm.parentId,
'url': this.dataForm.url,
'perms': this.dataForm.perms,
'orderNum': this.dataForm.orderNum,
'icon': this.dataForm.icon
})
}).then(({ data }) => {
if (data && data.code === 200) {
// 操作成功提示
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false // 关闭弹窗
this.$emit('refreshDataList') // 触发数据列表刷新事件
}
})
} else {
// 提示错误信息
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<style lang="scss">
.mod-menu {
.menu-list__input,
.icon-list__input {
> .el-input__inner {
cursor: pointer; // 鼠标悬停时显示为手型
}
}
&__icon-popover {
width: 458px; // 图标选择器的宽度
overflow: hidden; // 超出部分隐藏
}
&__icon-inner {
width: 478px; // 内部图标容器的宽度
max-height: 258px; // 最大高度
overflow-x: hidden; // 水平溢出隐藏
overflow-y: auto; // 垂直溢出滚动
}
&__icon-list {
width: 458px; // 图标列表宽度
padding: 0; // 内边距
margin: -8px 0 0 -8px; // 外边距调整
> .el-button {
padding: 8px; // 按钮内边距
margin: 8px 0 0 8px; // 按钮外边距
> span {
display: inline-block; // 使图标显示为行内块元素
vertical-align: middle; // 垂直居中对齐
width: 18px; // 图标宽度
height: 18px; // 图标高度
font-size: 18px; // 图标字体大小
}
}
}
.icon-list__tips {
font-size: 18px; // 提示字体大小
text-align: center; // 内容置中
color: #e6a23c; // 提示字体颜色
cursor: pointer; // 鼠标悬停时显示为手型
}
}
</style>