feat: 完善菜单管理功能,添加备注字段,修复显示问题
This commit is contained in:
parent
28b1bdee7d
commit
139cdf7a7a
@ -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
|
||||
提交更新项目代码
|
||||
提交更新项目代码
|
||||
请修正此问题
|
||||
@ -48,7 +48,6 @@ spring:
|
||||
thymeleaf:
|
||||
cache: false
|
||||
docker:
|
||||
enabled: false
|
||||
compose:
|
||||
enabled: false
|
||||
|
||||
|
||||
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@ -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>
|
||||
|
||||
<!-- 删除确认对话框 -->
|
||||
|
||||
@ -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);
|
||||
|
||||
20
src/main/webapp/i18n/zh-cn/menu.json
Normal file
20
src/main/webapp/i18n/zh-cn/menu.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"jewpmsApp": {
|
||||
"menu": {
|
||||
"home": {
|
||||
"title": "菜单管理",
|
||||
"createLabel": "创建新菜单",
|
||||
"editLabel": "编辑菜单",
|
||||
"deleteLabel": "删除菜单",
|
||||
"refreshListLabel": "刷新列表"
|
||||
},
|
||||
"name": "名称",
|
||||
"path": "路径",
|
||||
"icon": "图标",
|
||||
"sort": "排序",
|
||||
"visible": "可见性",
|
||||
"remark": "备注",
|
||||
"actions": "操作"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user