feat: 完善菜单管理功能,添加备注字段,修复显示问题

This commit is contained in:
user 2025-02-27 11:58:20 +08:00
parent 28b1bdee7d
commit 139cdf7a7a
7 changed files with 171 additions and 48 deletions

View File

@ -26,6 +26,10 @@ MySQL数据库要求如下:
99. 请将项目前端和后端 打包生成一个运行jar包
docker:
compose:
enabled: false
重新启动项目
.\mvnw clean spring-boot:run
npm run webapp:build && npm start
@ -47,4 +51,5 @@ java -jar target/jewpms-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
请初始化git 连接到远程仓库
https://gitea.vxnet.cn/admingit/jewpms.git
提交更新项目代码
提交更新项目代码
请修正此问题

View File

@ -48,7 +48,6 @@ spring:
thymeleaf:
cache: false
docker:
enabled: false
compose:
enabled: false

View File

@ -1,5 +1,5 @@
id,name,path,icon,parent_id,order_num,component,is_frame,visible,status,perms,query,remark,menu_type,createtime,updatetime,lastmodby,version
1,系统管理,/system,system,null,1,null,false,true,0,null,null,系统管理目录,M,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
1,系统管理,/system,users-cog,null,1,null,false,true,0,null,null,系统管理目录,M,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
2,用户管理,/system/user,user,1,1,/system/user/index,false,true,0,system:user:list,null,用户管理菜单,C,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
3,角色管理,/system/role,peoples,1,2,/system/role/index,false,true,0,system:role:list,null,角色管理菜单,C,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
4,菜单管理,/system/menu,tree-table,1,3,/system/menu/index,false,true,0,system:menu:list,null,菜单管理菜单,C,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
@ -14,4 +14,5 @@ id,name,path,icon,parent_id,order_num,component,is_frame,visible,status,perms,qu
13,菜单查询,/system/menu/query,null,4,1,null,false,true,0,system:menu:query,null,菜单查询按钮,F,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
14,菜单新增,/system/menu/add,null,4,2,null,false,true,0,system:menu:add,null,菜单新增按钮,F,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
15,菜单修改,/system/menu/edit,null,4,3,null,false,true,0,system:menu:edit,null,菜单修改按钮,F,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
16,菜单删除,/system/menu/remove,null,4,4,null,false,true,0,system:menu:remove,null,菜单删除按钮,F,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
16,菜单删除,/system/menu/remove,null,4,4,null,false,true,0,system:menu:remove,null,菜单删除按钮,F,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
17,首页,/home,home,null,0,/home/index,false,true,0,null,null,系统首页,C,2024-01-01 00:00:00,2024-01-01 00:00:00,admin,0
1 id name path icon parent_id order_num component is_frame visible status perms query remark menu_type createtime updatetime lastmodby version
2 1 系统管理 /system system users-cog null 1 null false true 0 null null 系统管理目录 M 2024-01-01 00:00:00 2024-01-01 00:00:00 admin 0
3 2 用户管理 /system/user user 1 1 /system/user/index false true 0 system:user:list null 用户管理菜单 C 2024-01-01 00:00:00 2024-01-01 00:00:00 admin 0
4 3 角色管理 /system/role peoples 1 2 /system/role/index false true 0 system:role:list null 角色管理菜单 C 2024-01-01 00:00:00 2024-01-01 00:00:00 admin 0
5 4 菜单管理 /system/menu tree-table 1 3 /system/menu/index false true 0 system:menu:list null 菜单管理菜单 C 2024-01-01 00:00:00 2024-01-01 00:00:00 admin 0
14 13 菜单查询 /system/menu/query null 4 1 null false true 0 system:menu:query null 菜单查询按钮 F 2024-01-01 00:00:00 2024-01-01 00:00:00 admin 0
15 14 菜单新增 /system/menu/add null 4 2 null false true 0 system:menu:add null 菜单新增按钮 F 2024-01-01 00:00:00 2024-01-01 00:00:00 admin 0
16 15 菜单修改 /system/menu/edit null 4 3 null false true 0 system:menu:edit null 菜单修改按钮 F 2024-01-01 00:00:00 2024-01-01 00:00:00 admin 0
17 16 菜单删除 /system/menu/remove null 4 4 null false true 0 system:menu:remove null 菜单删除按钮 F 2024-01-01 00:00:00 2024-01-01 00:00:00 admin 0
18 17 首页 /home home null 0 /home/index false true 0 null null 系统首页 C 2024-01-01 00:00:00 2024-01-01 00:00:00 admin 0

View File

@ -3,6 +3,7 @@ import { useI18n } from 'vue-i18n';
import { useVuelidate } from '@vuelidate/core';
import { required } from '@vuelidate/validators';
import axios from 'axios';
import type { BTableField } from 'bootstrap-vue';
export default defineComponent({
name: 'Menu',
@ -14,6 +15,66 @@ export default defineComponent({
const removeId = ref(null);
const dialogTitle = ref('');
const fields: BTableField[] = [
{ key: 'name', label: t('jewpmsApp.menu.name'), sortable: true },
{ key: 'path', label: t('jewpmsApp.menu.path'), sortable: true },
{ key: 'icon', label: t('jewpmsApp.menu.icon') },
{ key: 'sort', label: t('jewpmsApp.menu.sort'), sortable: true },
{ key: 'visible', label: t('jewpmsApp.menu.visible'), sortable: true },
{ key: 'remark', label: t('jewpmsApp.menu.remark'), sortable: true },
{ key: 'actions', label: t('jewpmsApp.menu.actions') },
];
const treeMenus = computed(() => {
const buildTree = (items: any[], parentId: string | null = null, level = 0) => {
return items
.filter(item => item.parentId === parentId)
.map(item => {
const children = buildTree(items, item.id, level + 1);
return {
...item,
_showChildren: children.length > 0,
_level: level,
_expanded: false,
children: children,
};
})
.sort((a, b) => a.sort - b.sort);
};
return buildTree(menus.value);
});
const toggleChildren = (item: any) => {
item._expanded = !item._expanded;
if (item._expanded) {
item.children.forEach((child: any) => {
const index = menus.value.findIndex(m => m.id === child.id);
if (index > -1) {
menus.value.splice(index + 1, 0, { ...child, _level: item._level + 1 });
}
});
} else {
const removeChildren = (parent: any) => {
if (parent.children) {
parent.children.forEach((child: any) => {
const index = menus.value.findIndex(m => m.id === child.id);
if (index > -1) {
menus.value.splice(index, 1);
if (child._expanded) {
removeChildren(child);
}
}
});
}
};
removeChildren(item);
}
};
const rowClass = (item: any) => {
return item._level > 0 ? `tree-level-${item._level}` : '';
};
const menu = ref({
id: null,
name: '',
@ -21,6 +82,8 @@ export default defineComponent({
icon: '',
parentId: '',
sort: 0,
visible: true,
remark: '',
createtime: null,
updatetime: null,
lastmodby: '',
@ -36,7 +99,7 @@ export default defineComponent({
const loadAll = async () => {
try {
isFetching.value = true;
const res = await axios.get('/api/menus');
const res = await axios.get('api/menus/tree');
menus.value = res.data;
loadParentMenus();
} catch (err) {
@ -68,21 +131,28 @@ export default defineComponent({
icon: '',
parentId: '',
sort: 0,
visible: true,
remark: '',
createtime: null,
updatetime: null,
lastmodby: '',
version: 0,
};
const modal = document.getElementById('editDialog');
// 使用$refs访问b-modal组件
const modal = document.querySelector('#editDialog___BV_modal_content_');
if (modal) {
modal.style.display = 'block';
modal.parentElement.parentElement.style.display = 'block';
modal.parentElement.parentElement.classList.add('show');
document.body.classList.add('modal-open');
}
};
const closeDialog = () => {
const modal = document.getElementById('editDialog');
const modal = document.querySelector('#editDialog___BV_modal_content_');
if (modal) {
modal.style.display = 'none';
modal.parentElement.parentElement.style.display = 'none';
modal.parentElement.parentElement.classList.remove('show');
document.body.classList.remove('modal-open');
}
};
@ -91,15 +161,18 @@ export default defineComponent({
menu.value = { ...menuItem };
const modal = document.getElementById('editDialog');
if (modal) {
modal.style.display = 'block';
modal.show();
}
};
const prepareDelete = menuItem => {
removeId.value = menuItem.id;
const modal = document.getElementById('removeDialog');
menu.value = menuItem;
const modal = document.querySelector('#deleteDialog___BV_modal_content_');
if (modal) {
modal.style.display = 'block';
modal.parentElement.parentElement.style.display = 'block';
modal.parentElement.parentElement.classList.add('show');
document.body.classList.add('modal-open');
}
};
@ -109,9 +182,9 @@ export default defineComponent({
if (!isValid) return;
if (menu.value.id) {
await axios.put(`/api/menus/${menu.value.id}`, menu.value);
await axios.put(`api/menus/${menu.value.id}`, menu.value);
} else {
await axios.post('/api/menus', menu.value);
await axios.post('api/menus', menu.value);
}
closeDialog();
@ -123,7 +196,7 @@ export default defineComponent({
const removeMenu = async () => {
try {
await axios.delete(`/api/menus/${removeId.value}`);
await axios.delete(`api/menus/${removeId.value}`);
closeDialog();
loadAll();
} catch (err) {
@ -149,7 +222,11 @@ export default defineComponent({
prepareDelete,
save,
removeMenu,
deleteMenu: removeMenu, // 添加deleteMenu方法的引用
getParentName,
treeMenus,
fields,
rowClass,
};
},
});

View File

@ -19,39 +19,48 @@
</div>
<div class="table-responsive" v-if="menus && menus.length > 0">
<table class="table table-striped" aria-describedby="menus">
<thead>
<tr>
<th scope="row"><span v-text="$t('jewpmsApp.menu.name')"></span></th>
<th scope="row"><span v-text="$t('jewpmsApp.menu.path')"></span></th>
<th scope="row"><span v-text="$t('jewpmsApp.menu.icon')"></span></th>
<th scope="row"><span v-text="$t('jewpmsApp.menu.parentId')"></span></th>
<th scope="row"><span v-text="$t('jewpmsApp.menu.sort')"></span></th>
<th scope="row"></th>
</tr>
</thead>
<tbody>
<tr v-for="menu in menus" :key="menu.id" data-cy="entityTable">
<td>{{ menu.name }}</td>
<td>{{ menu.path }}</td>
<td><font-awesome-icon :icon="menu.icon" /></td>
<td>{{ getParentName(menu.parentId) }}</td>
<td>{{ menu.sort }}</td>
<td class="text-right">
<div class="btn-group">
<button type="button" class="btn btn-info btn-sm" v-on:click="prepareEdit(menu)">
<font-awesome-icon icon="pencil-alt" />
<span v-text="$t('entity.action.edit')"></span>
</button>
<button type="button" class="btn btn-danger btn-sm" v-on:click="prepareDelete(menu)">
<font-awesome-icon icon="times" />
<span v-text="$t('entity.action.delete')"></span>
</button>
</div>
</td>
</tr>
</tbody>
</table>
<b-table
:items="treeMenus"
:fields="fields"
striped
hover
show-empty
small
primary-key="id"
:tbody-tr-class="rowClass"
:sort-by="'sort'"
:sort-desc="false"
>
<!-- 名称列支持树形展开/折叠 -->
<template #cell(name)="row">
<div :style="{ paddingLeft: `${row.item._level * 20}px` }">
<span v-if="row.item._showChildren" class="mr-2" style="cursor: pointer" @click="toggleChildren(row.item)">
<font-awesome-icon :icon="row.item._expanded ? 'caret-down' : 'caret-right'" />
</span>
<span v-else class="mr-4"></span>
{{ row.item.name }}
</div>
</template>
<!-- 图标列 -->
<template #cell(icon)="row">
<font-awesome-icon :icon="row.item.icon" />
</template>
<!-- 操作列 -->
<template #cell(actions)="row">
<div class="btn-group">
<button type="button" class="btn btn-info btn-sm" @click="prepareEdit(row.item)">
<font-awesome-icon icon="pencil-alt" />
<span v-text="$t('entity.action.edit')"></span>
</button>
<button type="button" class="btn btn-danger btn-sm" @click="prepareDelete(row.item)">
<font-awesome-icon icon="times" />
<span v-text="$t('entity.action.delete')"></span>
</button>
</div>
</template>
</b-table>
</div>
<!-- 新增/编辑对话框 -->
@ -79,6 +88,16 @@
<label v-text="$t('jewpmsApp.menu.sort')"></label>
<input type="number" class="form-control" v-model="menu.sort" />
</div>
<div class="form-group">
<label v-text="$t('jewpmsApp.menu.visible')"></label>
<b-form-checkbox v-model="menu.visible">
{{ $t('jewpmsApp.menu.visibleHint') }}
</b-form-checkbox>
</div>
<div class="form-group">
<label v-text="$t('jewpmsApp.menu.remark')"></label>
<textarea class="form-control" v-model="menu.remark" rows="3"></textarea>
</div>
</b-modal>
<!-- 删除确认对话框 -->

View File

@ -23,6 +23,7 @@ import {
BPagination,
BProgress,
BProgressBar,
BTable,
ToastPlugin,
VBModal,
} from 'bootstrap-vue';
@ -48,6 +49,7 @@ export function initBootstrapVue(vue) {
vue.component('b-form-group', BFormGroup);
vue.component('b-form-checkbox', BFormCheckbox);
vue.component('b-collapse', BCollapse);
vue.component('b-table', BTable);
vue.component('b-nav-item', BNavItem);
vue.component('b-nav-item-dropdown', BNavItemDropdown);
vue.component('b-modal', BModal);

View File

@ -0,0 +1,20 @@
{
"jewpmsApp": {
"menu": {
"home": {
"title": "菜单管理",
"createLabel": "创建新菜单",
"editLabel": "编辑菜单",
"deleteLabel": "删除菜单",
"refreshListLabel": "刷新列表"
},
"name": "名称",
"path": "路径",
"icon": "图标",
"sort": "排序",
"visible": "可见性",
"remark": "备注",
"actions": "操作"
}
}
}