菜单管理

This commit is contained in:
zhaoyz 2024-04-24 19:35:24 +08:00
parent 198b34f732
commit 15c9b35f1b
8 changed files with 442 additions and 307 deletions

55
src/api/menu.ts Normal file
View File

@ -0,0 +1,55 @@
import request from "@/utils/request";
const getMenuTreeUrl = "/admin/menu/tree";
const menuUrl = "/admin/menu";
export function getMenuTree(menuName?: string, parent?: string, type?: string) {
return new Promise((resolve, reject) => {
const params: any = [];
if (parent) {
params.push("parent=" + parent);
}
if (menuName) {
params.push("menuName=" + menuName);
}
if (type) {
params.push("type=" + type);
}
request.get(getMenuTreeUrl + (params.length > 0 ? "?" + params.join("&") : ""))
.then((res) => {
resolve(res.data);
}).catch(err => {
reject(err);
})
})
}
export function saveMenu(menu: any) {
return new Promise((resolve, reject) => {
request.post(menuUrl, menu).then((res: any) => {
resolve(res.data);
}).catch(err => {
reject(err);
})
})
}
export function updateMenu(menu: any) {
return new Promise((resolve, reject) => {
request.put(menuUrl, menu).then((res: any) => {
resolve(res);
}).catch(err => {
reject(err);
})
})
}
export function deleteById(id: string) {
return new Promise((resolve) => {
request.delete(menuUrl + "/" + id).then((res: any) => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}

View File

@ -20,7 +20,6 @@ function getLogList() {
logManageApi.getPage(0, 100).then((res: any) => {
if (res.code == 0) {
activities.value = res.data.records;
console.log(activities.value)
}
})
}

View File

@ -77,7 +77,6 @@ const userInfo = useLoginStore().getlogin()
const hospitals = ref([] as any)
getHospitalsData().then((res: any) => {
hospitals.value = res
// console.log(res)
})
const menus = [] as any

View File

@ -1,40 +1,44 @@
<template>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100">
<el-form-item label="类型" prop="type">
<el-radio-group v-model="formData.type" :disabled="type === 'edit'">
<el-radio label="菜单" border>菜单</el-radio>
<el-radio label="按钮" border>按钮</el-radio>
<el-radio-group v-model="formData.menuType" :disabled="type === 'edit'">
<el-radio label="0" border>菜单</el-radio>
<el-radio label="1" border v-if="formData.parentId != -1">按钮</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="上级菜单" prop="fatherMenu">
<el-select v-model="formData.fatherMenu" placeholder="请选择上级菜单" clearable>
<el-option v-for="item in fatherMenuOption" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-form-item label="上级菜单" prop="parentName">
<!-- <el-select v-model="formData.parentMenu" placeholder="请选择上级菜单" clearable>
<el-option v-for="item in parentMenu" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>-->
<el-input v-model="formData.parentName" readonly placeholder="请输入菜单名称"></el-input>
</el-form-item>
<el-form-item label="菜单名称" prop="menuName">
<el-input v-model="formData.menuName" placeholder="请输入菜单名称"></el-input>
<el-form-item label="菜单名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入菜单名称"></el-input>
</el-form-item>
<el-form-item label="图标" prop="icon">
<i :class="formData.icon" style="font-size: 26px;margin: 0 10px;"></i>
<el-button @click="isIconDialog = true">选择图标</el-button>
</el-form-item>
<el-form-item label="路由" prop="route">
<el-input v-model="formData.route" placeholder="请输入路由"></el-input>
<el-input v-model="formData.path" placeholder="请输入路由"></el-input>
</el-form-item>
<el-form-item label="排序" prop="order">
<el-input class="number-input" v-model="formData.order" type="number" input-style="text-align: center;" @change="numberInputChange">
<el-input class="number-input" v-model="formData.sortOrder"
type="number" input-style="text-align: center;"
@change="numberInputChange">
<template #prepend>
<el-button text icon="Minus" :disabled="formData.order <= 1" @click="formData.order--" />
<el-button text icon="Minus" :disabled="formData.sortOrder <= 1"
@click="formData.sortOrder--"/>
</template>
<template #append>
<el-button text icon="Plus" @click="formData.order++" />
<el-button text icon="Plus" @click="formData.sortOrder++"/>
</template>
</el-input>
</el-form-item>
<el-form-item label="是否显示" prop="isShow">
<el-radio-group v-model="formData.isShow">
<el-radio :label="true" border></el-radio>
<el-radio :label="false" border></el-radio>
<el-form-item label="是否显示" prop="visible">
<el-radio-group v-model="formData.visible">
<el-radio label="1" border></el-radio>
<el-radio label="0" border></el-radio>
</el-radio-group>
</el-form-item>
@ -46,7 +50,8 @@
<el-dialog v-model="isIconDialog" title="选择图标" top="20vh" width="25%">
<div class="iconfont-box">
<div class="iconfont-item" v-for="item in iconfont.glyphs" :key="item.font_class" @click="iconClick(item)">
<div class="iconfont-item" v-for="item in iconfont.glyphs"
:key="item.font_class" @click="iconClick(item)">
<i :class="'icon-' + item.font_class"></i>
</div>
</div>
@ -54,29 +59,26 @@
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {onMounted, reactive, ref} from 'vue'
import {ElMessage} from 'element-plus'
import iconfont from "@/assets/font/iconfont.json";
import * as menuApi from "@/api/menu";
const emit = defineEmits(['close'])
const emit = defineEmits(['close', "saveOrUpdateEvent"])
const props = defineProps({
type: String
})
const fatherMenuOption = [
{ label: '权限管理', value: '权限管理' },
{ label: '患者管理', value: '患者管理' },
]
defineExpose({
resetData,
})
const rules = reactive({
menuName: [
{ required: true, message: '请输入菜单名称', trigger: ['blur', 'change'] },
name: [
{required: true, message: '请输入菜单名称', trigger: ['blur', 'change']},
],
route: [
{ required: true, message: '请输入路由地址', trigger: ['blur', 'change'] },
path: [
{required: true, message: '请输入路由地址', trigger: ['blur', 'change']},
]
})
const formRef = ref()
const isIconDialog = ref(false)
const formData = ref({} as any)
@ -84,33 +86,45 @@ const formData = ref({} as any)
onMounted(() => {
resetData()
// console.log(iconfont.glyphs)
})
defineExpose({
formData,
resetData,
})
function close() {
emit('close')
}
function resetData() {
function resetData(menu?: any, form?: any) {
formRef.value.resetFields()
if (form) {
formData.value = {
id: '',
type: '菜单',
fatherMenu: '',
menuName: '',
menuId: form.id,
menuType: form.menuType,
parentName: form.parentName,
parentId: form.parentId,
name: form.name,
icon: form.meta.icon,
path: form.path,
sortOrder: form.sortOrder,
visible: form.visible
}
} else {
formData.value = {
menuId: '',
menuType: '0',
parentName: menu ? menu.name : "无",
parentId: menu ? menu.id : "-1",
name: '',
icon: '',
route: '',
order: 1,
isShow: true
path: '',
sortOrder: 1,
visible: "1"
}
}
}
const numberInputChange = (e: any) => {
if(!Number(e)) {
formData.value.order = 1
if (!Number(e)) {
formData.value.sortOrder = 1
}
}
const iconClick = (e: any) => {
@ -120,10 +134,26 @@ const iconClick = (e: any) => {
const saveData = async () => {
await formRef.value.validate((valid: any, fields: any) => {
if (valid) {
ElMessage.success('保存成功!')
close()
const form = Object.assign({}, formData.value);
if (form.menuId) {
menuApi.updateMenu(form).then((res: any) => {
if (res.code == 0) {
ElMessage.success("修改成功");
emit("saveOrUpdateEvent");
} else {
// console.log('error submit!', fields)
ElMessage.error("修改失败");
}
})
} else {
menuApi.saveMenu(form).then((res: any) => {
if (res.code == 0) {
ElMessage.success('保存成功!');
emit("saveOrUpdateEvent");
} else {
ElMessage.error("保存失败");
}
})
}
}
})
}
@ -142,20 +172,24 @@ const saveData = async () => {
}
}
}
.iconfont-box {
display: flex;
flex-wrap: wrap;
max-height: 300px;
overflow-y: auto;
.iconfont-item {
width: 50px;
height: 50px;
line-height: 50px;
text-align: center;
font-size: 30px;
&:hover {
background: rgba($main-color, .1);
}
&:focus, &:active {
background: rgba($main-color, .2);
color: $main-color;

View File

@ -10,10 +10,15 @@
<el-input v-model="formData.roleRemark" type="textarea" :rows="3" placeholder="请输入角色描述"></el-input>
</el-form-item>
<el-form-item label="数据权限" prop="dataPermissions">
<el-select v-model="formData.dataPermissions" placeholder="请选择角色">
<!-- <el-select v-model="formData.dataPermissions" placeholder="请选择角色">
<el-option v-for="item in dataPermissionsOption" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-select>-->
<div class="tree-container">
<el-tree ref="treeRef" :key="treeKey" :props="treeProps" :data="treeData" :default-expanded-keys="expandedKey"
node-key="id"
show-checkbox @check-change="checkChange"/>
</div>
</el-form-item>
<div style="text-align: right;padding-top: 0;">
@ -24,30 +29,28 @@
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {onMounted, reactive, ref} from 'vue'
import {ElMessage} from 'element-plus'
import {getMenuData} from '@/static-data/menu'
const emit = defineEmits(['close'])
const props = defineProps({
type: String
})
const dataPermissionsOption = [
{ label: '全部', value: '全部' },
{label: '全部', value: '全部'},
]
const rules = reactive({
roleName: [
{ required: true, message: '请输入角色名称', trigger: ['blur', 'change'] },
{required: true, message: '请输入角色名称', trigger: ['blur', 'change']},
],
roleMark: [
{ required: true, message: '请输入角色标识', trigger: ['blur', 'change'] },
{required: true, message: '请输入角色标识', trigger: ['blur', 'change']},
],
dataPermissions: [
{ required: true, message: '请选择数据权限', trigger: ['blur', 'change'] },
{required: true, message: '请选择数据权限', trigger: ['blur', 'change']},
]
})
const formRef = ref()
const formData = ref({
id: '',
@ -56,19 +59,27 @@ const formData = ref({
roleRemark: '',
dataPermissions: '',
} as any)
const treeProps = {
label: 'menuName',
children: 'children'
}
const treeData = ref()
onMounted(() => {
resetData()
treeData.value = getMenuData()
})
defineExpose({
formData,
resetData,
})
function close() {
emit('close')
}
function resetData() {
formRef.value.resetFields()
formData.value = {
@ -79,6 +90,7 @@ function resetData() {
dataPermissions: '',
}
}
const saveData = async () => {
await formRef.value.validate((valid: any, fields: any) => {
if (valid) {
@ -104,4 +116,11 @@ const saveData = async () => {
}
}
}
.tree-container {
width: 100%;
min-height: 200px;
max-height: 500px;
overflow-y: auto;
}
</style>

View File

@ -3,14 +3,14 @@
<div class="search-part" v-show="isSearch">
<div class="search-cell">
<span class="label">菜单名称</span>
<el-input v-model="queryParams.menuName" placeholder="请输入菜单名称"></el-input>
<el-input v-model="queryParams" placeholder="请输入菜单名称"></el-input>
</div>
<el-button type="primary" icon="Search" @click="queryData(queryParams)">查询</el-button>
<el-button icon="Refresh" @click="queryParams = {}">重置</el-button>
<el-button type="primary" icon="Search" @click="searchList">查询</el-button>
<el-button icon="Refresh" @click="resetSearch">重置</el-button>
</div>
<div class="button-part" style="justify-content: space-between;">
<div>
<el-button type="primary" icon="FirstAidKit" @click="addData">新增</el-button>
<el-button type="primary" icon="FirstAidKit" @click="addData()">新增</el-button>
<el-button @click="expandTable">展开/折叠</el-button>
</div>
<TableAbility :isDownload="false" @searchBtn="isSearch = !isSearch" @refreshBtn="initData()"></TableAbility>
@ -18,62 +18,79 @@
<div class="table-part">
<el-table ref="tableRef" :data="tableData" row-key="id" height="100%" border show-overflow-tooltip
@row-click="tableRowClick">
<el-table-column property="menuName" label="菜单名称" width="180" align="center" />
<el-table-column property="order" label="排序" width="80" align="center" />
<el-table-column property="name" label="菜单名称" width="180" align="center"/>
<el-table-column property="sortOrder" label="排序" width="80" align="center"/>
<el-table-column label="图标" width="80" align="center">
<template #default="scope"><i class="main-color f20" :class="scope.row.icon"></i></template>
<template #default="scope"><i class="main-color f20" :class="scope.row.meta.icon"></i></template>
</el-table-column>
<el-table-column property="route" label="路由" width="260" align="center" />
<el-table-column property="type" label="类型" width="180" align="center" />
<el-table-column property="path" label="路由" width="260" align="center"/>
<el-table-column property="menuType" label="类型" width="180" align="center"/>
<el-table-column label="操作" align="center">
<template #default="scope">
<span @click.stop>
<div @click.stop>
<el-button link icon="EditPen" @click="addData(scope.row)">新建下级</el-button>
<el-button link icon="EditPen" @click="editData(scope.row)">修改</el-button>
<el-button link icon="Delete" @click="removeData(scope.row)">删除</el-button>
</span>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-part">
<CommonPagination :total="100" @paginationChange="paginationChange" />
</div>
</div>
<el-dialog v-model="isFormDialog" :title="formDialogTitle" width="30%">
<MenuForm ref="doctorFormRef" :type="formDialogTitle === '添加' ? 'add' : 'edit'" @close="isFormDialog = false" />
<MenuForm ref="menuFormRef" :type="formDialogTitle === '添加' ? 'add' : 'edit'"
@save-or-update-event="formSaveUpdateEvent" @close="isFormDialog = false"/>
</el-dialog>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { tableRemoveRow, exportData } from '@/utils/table-util'
import { getMenuData } from '@/static-data/menu'
import CommonPagination from '@/components/common-pagination.vue'
import {onMounted, ref} from 'vue'
import MenuForm from './form/menu-form.vue'
import * as menuApi from "@/api/menu";
import {ElMessage, ElMessageBox} from "element-plus";
let isExpand = false //
const tableRef = ref()
const doctorFormRef = ref()
const menuFormRef = ref()
const isSearch = ref(true)
const loading = ref(true)
const isFormDialog = ref(false)
const formDialogTitle = ref('')
const queryParams = ref({
menuName: ''
} as any)
const queryParams = ref('')
const tableData = ref([] as any)
initData()
onMounted(() => {
initData();
})
function initData() {
loading.value = true
tableData.value = []
setTimeout(() => {
tableData.value = getMenuData()
loading.value = false
}, 200);
loading.value = true;
tableData.value = [];
getMenuTree();
}
function searchList() {
loading.value = true;
tableData.value = [];
getMenuTree(queryParams.value)
}
function resetSearch() {
queryParams.value = '';
initData();
}
function getMenuTree(name?: string) {
menuApi.getMenuTree(name).then((res: any) => {
tableData.value = res.data;
updateMenuTree(res.data, tableData.value);
})
}
function updateMenuTree(source: any[], target: any[]) {
target.forEach((row: any) => {
});
}
const queryData = (e: any) => {
@ -93,11 +110,11 @@ const queryData = (e: any) => {
loading.value = false
}, 200);
}
const addData = () => {
const addData = (menu?: any) => {
isFormDialog.value = true
formDialogTitle.value = '添加'
setTimeout(() => {
doctorFormRef.value.resetData()
menuFormRef.value.resetData(menu)
}, 0)
}
const expandTable = () => {
@ -107,27 +124,41 @@ const expandTable = () => {
})
}
const removeData = (e?: any) => {
const selectRow = e || tableRef.value.getSelectionRows()
tableRemoveRow({ data: selectRow }, (res: boolean) => {
if (res) {
// console.log('', selectRow)
ElMessageBox.confirm(
'是否删除?',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
draggable: true
}
).then(() => {
menuApi.deleteById(e.id).then((res: any) => {
if (res.code == 0) {
ElMessage.success("删除成功");
initData();
} else {
ElMessage.error(res.msg ? res.msg : "删除失败");
}
})
})
}
const editData = (e: any) => {
isFormDialog.value = true
formDialogTitle.value = '修改'
setTimeout(() => {
doctorFormRef.value.resetData()
doctorFormRef.value.formData = JSON.parse(JSON.stringify(e))
menuFormRef.value.resetData(null, JSON.parse(JSON.stringify(e)))
}, 0)
}
const tableRowClick = (row: any) => {
tableRef.value.toggleRowExpansion(row)
}
const paginationChange = (page: number, size: number) => {
function formSaveUpdateEvent() {
isFormDialog.value = false;
initData();
}
</script>
<style lang='scss' scoped></style>

View File

@ -3,7 +3,7 @@
<div class="search-part" v-show="isSearch">
<div class="search-cell">
<span class="label">角色名称</span>
<el-input v-model="queryParams.roleName" placeholder="请输入角色名称"></el-input>
<el-input v-model="queryParams" placeholder="请输入角色名称"></el-input>
</div>
<el-button type="primary" icon="Search" @click="queryData(queryParams)">查询</el-button>
<el-button icon="Refresh" @click="queryParams = {}">重置</el-button>
@ -51,10 +51,9 @@
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { dateFormater } from '@/utils/date-util'
import { tableRemoveRow, exportData } from '@/utils/table-util'
import {ref} from 'vue'
import {dateFormater} from '@/utils/date-util'
import {exportData, tableRemoveRow} from '@/utils/table-util'
import CommonPagination from '@/components/common-pagination.vue'
import RoleForm from './form/role-form.vue'
import ImportDialog from '@/components/import-dialog.vue'
@ -68,9 +67,7 @@ const isSearch = ref(true)
const loading = ref(true)
const isFormDialog = ref(false)
const formDialogTitle = ref('')
const queryParams = ref({
roleName: ''
} as any)
const queryParams = ref("")
const tableData = ref([] as any)
queryData({ roleName: '测试' })
@ -127,6 +124,7 @@ const editData = (e: any) => {
const tableRowClick = (row: any) => {
tableRef.value.toggleRowSelection(row)
}
const paginationChange = (page: number, size: number) => {
}

View File

@ -24,14 +24,14 @@ export default defineConfig({
proxy: {
'/api': {
//target: 'http://192.168.137.235:9999', // 目标服务器地址
target: 'http://192.168.1.110:9999', // 目标服务器地址
target: 'http://localhost:9999', // 目标服务器地址
ws: true, // 是否启用 WebSocket
changeOrigin: true, // 是否修改请求头中的 Origin 字段
rewrite: (path) => path.replace(/^\/api/, ''),
},
'/socket.io': {
//target: 'ws://192.168.137.235:9999',
target: 'ws://192.168.1.110:9999',
target: 'ws://localhost:9999',
ws: true,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/socket.io/, ''),