Files
FT501-WMS/ruoyi-ui/src/views/system/menu/index.vue
2025-06-06 09:38:54 +08:00

558 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch">
<!-- 菜单名称 -->
<el-form-item :label="$t('systemManagement_MenuManagement.menuName')" prop="menuName">
<el-input
v-model="queryParams.menuName"
:placeholder="$t('systemManagement_MenuManagement.placeholderMenuName')"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('systemManagement_MenuManagement.status')" prop="status">
<el-select v-model="queryParams.status" :placeholder="$t('systemManagement_MenuManagement.placeholderStatus')" clearable>
<el-option
v-for="dict in dict.type.sys_normal_disable"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<!-- 【查询】按钮 -->
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ $t('systemManagement_MenuManagement.btnSearch') }}</el-button>
<!-- 【重置】按钮 -->
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ $t('systemManagement_MenuManagement.btnReset') }}</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<!-- 【新增】按钮 -->
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['system:menu:add']"
>{{ $t('systemManagement_MenuManagement.btnAdd') }}</el-button>
</el-col>
<el-col :span="1.5">
<!-- 【展开/折叠】按钮 -->
<el-button
type="info"
plain
icon="el-icon-sort"
size="mini"
@click="toggleExpandAll"
>{{ $t('systemManagement_MenuManagement.btnExpandOrCollapse') }}</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-if="refreshTable"
v-loading="loading"
:data="menuList"
row-key="menuId"
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}" >
<!-- 菜单名称 -->
<el-table-column prop="menuName" :label="$t('systemManagement_MenuManagement.menuName')" :show-overflow-tooltip="true" width="160"></el-table-column>
<!-- 图标 -->
<el-table-column prop="icon" :label="$t('systemManagement_MenuManagement.icon')" align="center" width="100">
<template slot-scope="scope">
<svg-icon :icon-class="scope.row.icon" />
</template>
</el-table-column>
<!-- 排序 -->
<el-table-column prop="orderNum" :label="$t('systemManagement_MenuManagement.orderNum')" align="center" width="90"></el-table-column>
<!-- 权限标识 -->
<el-table-column prop="perms" :label="$t('systemManagement_MenuManagement.perms')" :show-overflow-tooltip="true"></el-table-column>
<!-- 组件 -->
<el-table-column prop="component" :label="$t('systemManagement_MenuManagement.componentPath')" :show-overflow-tooltip="true"></el-table-column>
<!-- 状态 -->
<el-table-column prop="status" :label="$t('systemManagement_MenuManagement.status')" width="80">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
</template>
</el-table-column>
<!-- 创建时间 -->
<el-table-column :label="$t('systemManagement_MenuManagement.createTime')" align="center" prop="createTime">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<!-- 人工操作 -->
<el-table-column :label="$t('systemManagement_MenuManagement.operation')" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<!-- 人工操作---【修改】按钮 -->
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:menu:edit']"
>{{ $t('systemManagement_MenuManagement.btnEdit') }}</el-button>
<!-- 人工操作---【新增】按钮 -->
<el-button
size="mini"
type="text"
icon="el-icon-plus"
@click="handleAdd(scope.row)"
v-hasPermi="['system:menu:add']"
>{{ $t('systemManagement_MenuManagement.btnAdd') }}</el-button>
<!-- 人工操作---【删除】按钮 -->
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:menu:remove']"
>{{ $t('systemManagement_MenuManagement.btnDelete') }}</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改菜单对话框 -->
<el-dialog :title="title" :visible.sync="open" width="680px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-row>
<el-col :span="24">
<!-- 上级菜单 -->
<el-form-item :label="$t('systemManagement_MenuManagement.parentMenu')" prop="parentId">
<treeselect
v-model="form.parentId"
:options="menuOptions"
:normalizer="normalizer"
:show-count="true"
:placeholder="$t('systemManagement_MenuManagement.placeholderParentMenu')"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<!-- 菜单类型 -->
<el-form-item :label="$t('systemManagement_MenuManagement.menuType')" prop="menuType">
<el-radio-group v-model="form.menuType">
<!-- 选项:目录 -->
<el-radio label="M">{{ $t('systemManagement_MenuManagement.menuTypeOptions1') }}</el-radio>
<!-- 选项:菜单 -->
<el-radio label="C">{{ $t('systemManagement_MenuManagement.menuTypeOptions2') }}</el-radio>
<!-- 选项:按钮 -->
<el-radio label="F">{{ $t('systemManagement_MenuManagement.menuTypeOptions3') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12" v-if="form.menuType != 'F'">
<!-- 菜单名称 -->
<el-form-item :label="$t('systemManagement_MenuManagement.menuIcon')" prop="icon">
<el-popover
placement="bottom-start"
width="460"
trigger="click"
@show="$refs['iconSelect'].reset()"
>
<IconSelect ref="iconSelect" @selected="selected" :active-icon="form.icon" />
<!-- 选择菜单图标 -->
<el-input slot="reference" v-model="form.icon" :placeholder="$t('systemManagement_MenuManagement.placeholderMenuIcon')" readonly>
<svg-icon
v-if="form.icon"
slot="prefix"
:icon-class="form.icon"
style="width: 25px;"
/>
<i v-else slot="prefix" class="el-icon-search el-input__icon" />
</el-input>
</el-popover>
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 显示排序 -->
<el-form-item :label="$t('systemManagement_MenuManagement.orderNum')" prop="orderNum">
<el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<!-- 菜单名称 -->
<el-form-item :label="$t('systemManagement_MenuManagement.menuName')" prop="menuName">
<el-input v-model="form.menuName" :placeholder="$t('systemManagement_MenuManagement.placeholderMenuName')" />
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.menuType == 'C'">
<!-- 路由名称 -->
<el-form-item prop="routeName">
<el-input v-model="form.routeName" :placeholder="$t('systemManagement_MenuManagement.placeholderRouterName')" />
<span slot="label">
<!-- 文本:默认不填则和路由地址相同:如地址为:`user`,则名称为`User`(注意:为避免名字的冲突,特殊情况下请自定义,保证唯一性) -->
<el-tooltip :content="$t('systemManagement_MenuManagement.textRouterName')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t('systemManagement_MenuManagement.routerName') }}
</span>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12" v-if="form.menuType != 'F'">
<el-form-item prop="isFrame">
<!-- 内外链选择框 -->
<span slot="label">
<!-- 文本:选择是外链则路由地址需要以`http(s)://`开头 -->
<el-tooltip :content="$t('systemManagement_MenuManagement.textIsFrame')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t('systemManagement_MenuManagement.isFrame') }}
</span>
<el-radio-group v-model="form.isFrame">
<!-- 选项:是 -->
<el-radio label="0">{{ $t('systemManagement_MenuManagement.isFrameOptions1') }}</el-radio>
<!-- 选项:否 -->
<el-radio label="1">{{ $t('systemManagement_MenuManagement.isFrameOptions2') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.menuType != 'F'">
<el-form-item prop="path">
<!-- 路由地址 -->
<span slot="label">
<!-- 文本:路由地址:如地址为`user`,则路由地址为`/system/user`(注意:为避免名字的冲突,特殊情况下请自定义,保证唯一性) -->
<el-tooltip :content="$t('systemManagement_MenuManagement.textRouterPath')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t('systemManagement_MenuManagement.routerPath') }}
</span>
<!-- 提示:请输入路由地址 -->
<el-input v-model="form.path" :placeholder="$t('systemManagement_MenuManagement.placeholderRouterPath')" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12" v-if="form.menuType == 'C'">
<el-form-item prop="component">
<!-- 组件路径 -->
<span slot="label">
<!-- 文本:访问的组件路径,如:`system/user/index`,默认在`views`目录下 -->
<el-tooltip :content="$t('systemManagement_MenuManagement.textComponentPath')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t('systemManagement_MenuManagement.componentPath') }}
</span>
<!-- 提示:请输入组件路径 -->
<el-input v-model="form.component" :placeholder="$t('systemManagement_MenuManagement.placeholderComponentPath')" />
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.menuType != 'M'">
<el-form-item prop="perms">
<!-- 权限标识 -->
<el-input v-model="form.perms" :placeholder="$t('systemManagement_MenuManagement.placeholderPerms')" maxlength="100" />
<!-- 权限字符 -->
<span slot="label">
<!-- 文本:控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`) -->
<el-tooltip :content="$t('systemManagement_MenuManagement.textPerms')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t('systemManagement_MenuManagement.perms') }}
</span>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12" v-if="form.menuType == 'C'">
<el-form-item prop="query">
<!-- 提示:请输入路由参数 -->
<el-input v-model="form.query" :placeholder="$t('systemManagement_MenuManagement.placeholderParams')" maxlength="255" />
<!-- 路由参数 -->
<span slot="label">
<!-- 文本:访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}` -->
<el-tooltip :content="$t('systemManagement_MenuManagement.textRouterParams')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t('systemManagement_MenuManagement.routerParams') }}
</span>
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.menuType == 'C'">
<el-form-item prop="isCache">
<!-- 是否缓存 -->
<span slot="label">
<!-- 文本:选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致 -->
<el-tooltip :content="$t('systemManagement_MenuManagement.textIsCache')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t('systemManagement_MenuManagement.isCache') }}
</span>
<el-radio-group v-model="form.isCache">
<!-- 选项:是 -->
<el-radio label="0">{{ $t('systemManagement_MenuManagement.isCacheOptions1') }}</el-radio>
<!-- 选项:否 -->
<el-radio label="1">{{ $t('systemManagement_MenuManagement.isCacheOptions2') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12" v-if="form.menuType != 'F'">
<el-form-item prop="visible">
<!-- 显示状态 -->
<span slot="label">
<!-- 文本:选择隐藏则路由将不会出现在侧边栏,但仍然可以访问 -->
<el-tooltip :content="$t('systemManagement_MenuManagement.textIsVisible')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t('systemManagement_MenuManagement.isVisible') }}
</span>
<el-radio-group v-model="form.visible">
<el-radio
v-for="dict in dict.type.sys_show_hide"
:key="dict.value"
:label="dict.value"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="status">
<!-- 状态 -->
<span slot="label">
<!-- 文本:选择停用则路由将不会出现在侧边栏,也不能被访问 -->
<el-tooltip :content="$t('systemManagement_MenuManagement.textStatus')" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
{{ $t('systemManagement_MenuManagement.status') }}
</span>
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in dict.type.sys_normal_disable"
:key="dict.value"
:label="dict.value"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<!-- 二级窗口---【确定】按钮 -->
<el-button type="primary" @click="submitForm">{{ $t('systemManagement_MenuManagement.btnSubmit') }}</el-button>
<!-- 二级窗口---【取消】按钮 -->
<el-button @click="cancel">{{ $t('systemManagement_MenuManagement.btnCancel') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listMenu, getMenu, delMenu, addMenu, updateMenu } from "@/api/system/menu"
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import IconSelect from "@/components/IconSelect"
export default {
name: "Menu",
dicts: ['sys_show_hide', 'sys_normal_disable'],
components: { Treeselect, IconSelect },
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 菜单表格树数据
menuList: [],
// 菜单树选项
menuOptions: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 是否展开,默认全部折叠
isExpandAll: false,
// 重新渲染表格状态
refreshTable: true,
// 查询参数
queryParams: {
menuName: undefined,
visible: undefined
},
// 表单参数
form: {},
// 表单校验
rules: {
menuName: [
// 校验:菜单名称不能为空
{ required: true, message: this.$t('systemManagement_MenuManagement.checkMenuNameIsNull'), trigger: "blur" }
],
orderNum: [
// 校验:菜单顺序不能为空
{ required: true, message: this.$t('systemManagement_MenuManagement.checkOrderNumIsNull'), trigger: "blur" }
],
path: [
// 校验:路由地址不能为空
{ required: true, message: this.$t('systemManagement_MenuManagement.checkRouterPathIsNull'), trigger: "blur" }
]
}
}
},
created() {
this.getList()
},
methods: {
// 选择图标
selected(name) {
this.form.icon = name
},
/** 查询菜单列表 */
getList() {
this.loading = true
listMenu(this.queryParams).then(response => {
// 国际化菜单名称
for (const item of response.data) {
item.menuName = this.$t(item.menuName)
}
this.menuList = this.handleTree(response.data, "menuId")
this.loading = false
})
},
/** 转换菜单数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children
}
return {
id: node.menuId,
label: node.menuName,
children: node.children
}
},
/** 查询菜单下拉树结构 */
getTreeselect() {
listMenu().then(response => {
this.menuOptions = []
// 主类目
const menu = { menuId: 0, menuName: this.$t('systemManagement_MenuManagement.MainTitle'), children: [] }
const translatedData = this.translateMenuLabels(response.data);
menu.children = this.handleTree(translatedData, "menuId")
this.menuOptions.push(menu)
// console.log(response.data)
// console.log(menu.children)
// console.log(this.menuOptions)
})
},
//@@@ 递归处理树形结构
translateMenuLabels(data) {
return data.map(item => ({
...item,
menuName: this.$t(item.menuName),
children: item.children ? this.translateMenuLabels(item.children) : []
}));
},
// 取消按钮
cancel() {
this.open = false
this.reset()
},
// 表单重置
reset() {
this.form = {
menuId: undefined,
parentId: 0,
menuName: undefined,
icon: undefined,
menuType: "M",
orderNum: undefined,
isFrame: "1",
isCache: "0",
visible: "0",
status: "0"
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
},
/** 新增按钮操作 */
handleAdd(row) {
this.reset()
this.getTreeselect()
if (row != null && row.menuId) {
this.form.parentId = row.menuId
} else {
this.form.parentId = 0
}
this.open = true
// 设置标题: 添加菜单
this.title = this.$t('systemManagement_MenuManagement.addMenuItem')
},
/** 展开/折叠操作 */
toggleExpandAll() {
this.refreshTable = false
this.isExpandAll = !this.isExpandAll
this.$nextTick(() => {
this.refreshTable = true
})
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
this.getTreeselect()
getMenu(row.menuId).then(response => {
this.form = response.data
this.open = true
// 设置标题: 修改菜单
this.title = this.$t('systemManagement_MenuManagement.editMenuItem')
})
},
/** 提交按钮 */
submitForm: function() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.menuId != undefined) {
updateMenu(this.form).then(response => {
// 提示:修改成功
this.$modal.msgSuccess(this.$t('systemManagement_MenuManagement.editSuccessInfo'))
this.open = false
this.getList()
})
} else {
addMenu(this.form).then(response => {
// 提示:新增成功
this.$modal.msgSuccess(this.$t('systemManagement_MenuManagement.addSuccessInfo'))
this.open = false
this.getList()
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
this.$modal.confirm(this.$t('systemManagement_MenuManagement.txtDeleteConfirm1') + row.menuName + this.$t('systemManagement_MenuManagement.txtDeleteConfirm2')).then(function() {
return delMenu(row.menuId)
}).then(() => {
this.getList()
// 提示:删除成功
this.$modal.msgSuccess(this.$t('systemManagement_MenuManagement.deleteSuccessInfo'))
}).catch(() => {})
}
}
}
</script>