提交 b70d85dc authored 作者: 吕本才's avatar 吕本才

快捷菜单

上级 35b3b12a
...@@ -88,6 +88,72 @@ export const constantRoutes = [ ...@@ -88,6 +88,72 @@ export const constantRoutes = [
} }
] ]
}, },
// 快捷菜单路由
{
path: '/jcjs',
component: Layout,
hidden: true,
children: [
{
path: 'sjy',
component: () => import('@/views/rule/datasource/index'),
name: 'DataSource',
meta: { title: '数据源管理' }
}
]
},
{
path: '/sjjc',
component: Layout,
hidden: true,
children: [
{
path: 'jobInfo',
component: () => import('@/views/datax/jobInfo/index'),
name: 'JobInfo',
meta: { title: '任务管理' }
}
]
},
{
path: '/flink',
component: Layout,
hidden: true,
children: [
{
path: 'FlinkTaskManage',
component: () => import('@/views/flink/task-manage/index'),
name: 'FlinkTaskManage',
meta: { title: '任务列表' }
}
]
},
{
path: '/market',
component: Layout,
hidden: true,
children: [
{
path: 'dataapi',
component: () => import('@/views/market/dataapi/index'),
name: 'DataApi',
meta: { title: '数据服务' }
}
]
},
{
path: '/quality',
component: Layout,
hidden: true,
children: [
{
path: 'checkrule',
component: () => import('@/views/quality/checkrule/index'),
name: 'CheckRule',
meta: { title: '规则配置' }
}
]
},
{ {
path: '/flink/task-manage', path: '/flink/task-manage',
redirect: 'noRedirect', redirect: 'noRedirect',
......
<template> <template>
<div class="dashboard-container"> <div class="dashboard-container">
<div class="dashboard-text">欢迎使用大数据平台</div> <div class="quick-menu">
<div class="dashboard-description">这是一个用于管理大数据任务的平台</div> <div class="menu-header">
<h3>快捷菜单</h3>
<div>
<el-button type="primary" size="small" @click="openConfigDialog">
<i class="el-icon-setting"></i> 配置
</el-button>
</div>
</div>
<div class="menu-grid">
<router-link
v-for="menu in displayMenus"
:key="menu.id"
:to="menu.path || '#'"
class="menu-item"
v-if="menu && menu.path"
>
<div class="menu-icon">{{ menu.icon }}</div>
<div class="menu-label">{{ menu.label }}</div>
</router-link>
</div>
</div>
<!-- 快捷菜单配置抽屉 -->
<el-drawer
title="配置快捷菜单"
:visible.sync="configDialogVisible"
direction="rtl"
size="600px"
>
<el-form :model="configForm" label-width="80px">
<el-form-item label="选择菜单">
<el-checkbox-group v-model="configForm.selectedMenus">
<div v-for="(menuNode, index1) in menuTree" :key="menuNode.menuId">
<div style="display: flex; align-items: center;" class="menu-level-1">
<span style="cursor: pointer; flex: 1;" @click.stop="toggleMenu(menuNode.menuId)">
{{ index1 + 1 }}. {{ menuNode.menuName }}
</span>
</div>
<div
v-if="menuNode.children && menuNode.children.length > 0"
style="margin-left: 20px;"
v-show="expandedMenus[menuNode.menuId]"
>
<div v-for="(childNode, index2) in menuNode.children" :key="childNode.menuId">
<div v-if="!childNode.children || childNode.children.length === 0" class="menu-level-2">
<el-checkbox :label="childNode.menuId.toString()">
{{ index1 + 1 }}.{{ index2 + 1 }} {{ childNode.menuName }}
</el-checkbox>
</div>
<div v-else style="display: flex; align-items: center;" class="menu-level-2">
<span style="cursor: pointer; flex: 1;" @click.stop="toggleMenu(childNode.menuId)">
{{ index1 + 1 }}.{{ index2 + 1 }} {{ childNode.menuName }}
</span>
</div>
<div
v-if="childNode.children && childNode.children.length > 0"
style="margin-left: 20px;"
v-show="expandedMenus[childNode.menuId]"
>
<div v-for="(grandChildNode, index3) in childNode.children" :key="grandChildNode.menuId">
<el-checkbox :label="grandChildNode.menuId.toString()" class="menu-level-3">
{{ index1 + 1 }}.{{ index2 + 1 }}.{{ index3 + 1 }} {{ grandChildNode.menuName }}
</el-checkbox>
</div>
</div>
</div>
</div>
</div>
</el-checkbox-group>
</el-form-item>
</el-form>
<el-button type="success" size="small" @click="saveMenuConfig" style="margin-left: 10px;">
<i class="el-icon-save"></i> 保存配置
</el-button>
<el-button size="small" @click="cancelMenuConfig" style="margin-left: 10px;">
<i class="el-icon-close"></i> 取消配置
</el-button>
</el-drawer>
</div> </div>
</template> </template>
<script> <script>
import { listMenu } from '@/api/system/menu'
export default { export default {
name: 'Index' name: 'Index',
data() {
return {
configDialogVisible: false,
configForm: {
selectedMenus: []
},
// 所有可用的菜单
allMenus: [],
// 菜单树数据
menuTree: [],
// 默认选中的菜单配置
defaultMenuConfig: [],
// 加载状态
loading: false,
// 菜单展开状态
expandedMenus: {},
// 用于触发菜单更新的时间戳
menuUpdateTime: Date.now()
}
},
computed: {
// 显示的菜单
displayMenus() {
console.log('this.displayMenus -000000000000000000');
// 通过引用menuUpdateTime,确保当它变化时计算属性会重新计算
this.menuUpdateTime;
let savedMenuConfig;
try {
savedMenuConfig = localStorage.getItem('quickMenuConfig')
? JSON.parse(localStorage.getItem('quickMenuConfig'))
: null;
} catch (error) {
console.error('解析菜单配置失败:', error);
savedMenuConfig = null;
}
// 确保返回有效的菜单配置
let finalMenuConfig;
if (!savedMenuConfig || !Array.isArray(savedMenuConfig) || savedMenuConfig.length === 0) {
finalMenuConfig = this.defaultMenuConfig;
} else {
// 过滤掉无效的菜单项(没有路径或路径无效)
let filteredMenus = savedMenuConfig.filter(menu => {
return menu && menu.id && menu.path && typeof menu.path === 'string' && menu.path.trim() !== '';
});
// 去重处理,确保每个菜单ID唯一
const menuMap = new Map();
filteredMenus.forEach(menu => {
if (menu && menu.id) {
menuMap.set(menu.id, menu);
}
});
// 转换为数组,得到去重后的菜单列表
filteredMenus = Array.from(menuMap.values());
// 如果过滤后没有有效菜单,使用默认配置
finalMenuConfig = filteredMenus.length > 0 ? filteredMenus : this.defaultMenuConfig;
}
return finalMenuConfig;
}
},
created() {
console.log('ceeated', this.defaultMenuConfig);
this.loadMenus();
},
methods: {
// 加载菜单数据
loadMenus() {
console.log('loadMenus--1111111111111111111');
this.loading = true;
listMenu().then(response => {
// 处理菜单数据,转换为前端需要的格式
this.processMenuData(response.data);
this.loading = false;
}).catch(error => {
console.error('加载菜单失败:', error);
this.loading = false;
// 如果接口调用失败,使用默认菜单
this.useDefaultMenus();
});
},
// 处理菜单数据
processMenuData(menuList) {
console.log('processMenuData--111', menuList);
// 检查menuList是否存在且是数组
if (!Array.isArray(menuList)) {
this.useDefaultMenus();
return;
}
// 转换为菜单树结构
this.menuTree = this.buildMenuTree(menuList);
// 提取所有菜单项
const allMenuItems = [];
const extractMenuItems = (menuNode, parentLabel = '') => {
if (!menuNode) return;
const label = parentLabel ? `${parentLabel}-${menuNode.menuName}` : menuNode.menuName;
const icon = this.getMenuIcon(menuNode.menuName);
// 只有叶子节点且路径有效才作为可点击的菜单项
if (menuNode.path && menuNode.path !== '#' && menuNode.path.trim() !== '' && typeof menuNode.path === 'string' && menuNode.menuType !== 'F' && (!menuNode.children || menuNode.children.length === 0)) {
// 确保路径以斜杠开头
const normalizedPath ="/"+ menuNode.parentPath +(menuNode.path.startsWith('/') ? menuNode.path : `/${menuNode.path}`);
allMenuItems.push({
id: menuNode.menuId.toString(),
label: label,
path: normalizedPath,
icon: icon,
parentPath: menuNode.parentPath,
parentId: menuNode.parentId ? menuNode.parentId.toString() : null
});
}
if (menuNode.children && menuNode.children.length > 0) {
menuNode.children.forEach(childNode => {
extractMenuItems(childNode, label);
});
}
};
this.menuTree.forEach(menuNode => {
// 递归提取所有菜单项
extractMenuItems(menuNode);
});
this.allMenus = allMenuItems;
// 如果没有从接口获取到菜单,使用默认菜单
if (this.allMenus.length === 0) {
this.useDefaultMenus();
}
},
// 构建菜单树
buildMenuTree(menuList) {
console.log('buildMenuTree--2222222222222222222', menuList);
// 创建菜单映射
const menuMap = {};
const rootMenus = [];
// 检查menuList是否存在且是数组
if (!Array.isArray(menuList)) {
return rootMenus;
}
// 首先将所有菜单放入map中,排除menuType为F的菜单
menuList.forEach(menu => {
if (menu && menu.menuType !== 'F') {
menuMap[menu.menuId] = { ...menu, children: [] };
}
});
// 构建树结构,排除menuType为F的菜单
menuList.forEach(menu => {
if (menu && menu.menuType !== 'F') {
if (menu.parentId === 0) {
// 根菜单
rootMenus.push(menuMap[menu.menuId]);
} else {
// 子菜单
if (menuMap[menu.parentId]) {
menuMap[menu.parentId].
children.push( { ...menuMap[menu.menuId], parentPath: menuMap[menu.parentId].path });
}
}
}
});
return rootMenus;
},
// 获取菜单图标
getMenuIcon(menuName) {
const menuIcons = {
'基础建设': '📁',
'数据源': '📁',
'数据集成': '🔧',
'任务管理': '🔧',
'数据开发': '⚙️',
'任务列表': '⚙️',
'数据API': '🔗',
'数据api': '🔗',
'数据服务': '🔗',
'数据质量': '📊',
'规则配置': '📊'
};
return menuIcons[menuName] || '📄';
},
// 使用默认菜单
useDefaultMenus() {
console.log('useDefaultMenus:333333333333333333');
this.allMenus = this.defaultMenuConfig;
// 构建默认菜单树
this.menuTree = [];
},
// 打开配置对话框
openConfigDialog() {
const savedMenuConfig = localStorage.getItem('quickMenuConfig')
? JSON.parse(localStorage.getItem('quickMenuConfig'))
: this.defaultMenuConfig;
console.log('openConfigDialog--2222222222222222222',savedMenuConfig);
// 提取已选中的菜单ID
this.configForm.selectedMenus = savedMenuConfig.map(menu => menu.id);
console.log( "选中了菜单ID:", this.configForm.selectedMenus);
this.configDialogVisible = true;
},
// 保存菜单配置
saveMenuConfig() {
// 检查configForm.selectedMenus是否存在且是数组
if (!Array.isArray(this.configForm.selectedMenus)) {
this.configForm.selectedMenus = [];
}
console.log('saveMenuConfig--2222222222222222222',);
// 随机图标列表
const randomIcons = ['📁', '🔧', '⚙️', '🔗', '📊', '📄', '📋', '📅', '📈', '📉', '📌', '📍', '🔍', '🔎', '🔏', '🔐', '🔒', '🔓', '🔑', '🔨', '🔩', '🔧', '🔦', '🔅', '🔆', '🔇', '🔈', '🔉', '🔊', '🔔', '🔕', '🔖', '🔗', '🔘', '🔙', '🔚', '🔛', '🔝', '🔞', '🔟', '🔢', '🔣', '🔤', '🔡', '🔠', '🔰', '🔱', '🔲', '🔳', '🔴', '🔵', '🔷', '🔶', '🔸', '🔹', '🔺', '🔻', '🔼', '🔽', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚', '🕛', '🕜', '🕝', '🕞', '🕟', '🕠', '🕡', '🕢', '🕣', '🕤', '🕥', '🕦', '🕧', '🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘', '🌙', '🌚', '🌛', '🌜', '🌞', '🌟', '⭐', '🌠', '🌡️', '🌤️', '🌥️', '🌦️', '🌧️', '🌨️', '🌩️', '🌪️', '🌫️', '🌬️', '🌭', '🍔', '🍟', '🍕', '🍖', '🍗', '🍘', '🍙', '🍚', '🍛', '🍜', '🍝', '🍞', '🍟', '🍡', '🍢', '🍣', '🍤', '🍥', '🍦', '🍧', '🍨', '🍩', '🍪', '🍫', '🍬', '🍭', '🍮', '🍯', '🍰', '🍱', '🍲', '🍳', '🍴', '🍵', '🍶', '🍷', '🍸', '🍹', '🍺', '🍻', '🍼', '🍽️', '🍾', '🍿', '🎀', '🎁', '🎂', '🎃', '🎄', '🎆', '🎇', '🎈', '🎉', '🎊', '🎋', '🎍', '🎎', '🎏', '🎐', '🎑', '🎒', '🎓', '🎠', '🎡', '🎢', '🎣', '🎤', '🎥', '🎦', '🎧', '🎨', '🎩', '🎪', '🎫', '🎬', '🎭', '🎮', '🎯', '🎰', '🎱', '🎲', '🎳', '🎴', '🎵', '🎶', '🎷', '🎸', '🎹', '🎺', '🎻', '🎼', '🎽', '🎾', '🎿', '🏀', '🏁', '🏂', '🏃', '🏄', '🏅', '🏆', '🏇', '🏈', '🏉', '🏊', '🏋️', '🏌️', '🏍️', '🏎️', '🏏', '🏐', '🏑', '🏒', '🏓', '🏔️', '🏕️', '🏖️', '🏗️', '🏘️', '🏙️', '🏚️', '🏛️', '🏜️', '🏝️', '🏞️', '🏟️', '🏠', '🏡', '🏢', '🏣', '🏤', '🏥', '🏦', '🏨', '🏩', '🏪', '🏫', '🏬', '🏭', '🏯', '🏰', '🏳️', '🏴', '🏳️‍🌈', '🏳️‍⚧️', '🚀', '🚁', '🚂', '🚃', '🚄', '🚅', '🚆', '🚇', '🚈', '🚉', '🚊', '🚋', '🚌', '🚍', '🚎', '🚐', '🚑', '🚒', '🚓', '🚔', '🚕', '🚖', '🚗', '🚘', '🚙', '🚚', '🚛', '🚜', '🚲', '🛴', '🛵', '🚏', '🛣️', '🛤️', '🛢️', '🛞', '🛟', '🚨', '🚥', '🚦', '🚧', '🛑', '🚪', '🚫', '🚬', '🚭', '🚮', '🚯', '🚰', '🚱', '🚲', '🚳', '🚴', '🚵', '🚶', '🚷', '🚸', '🚹', '🚺', '🚻', '🚼', '🚾', '🛂', '🛃', '🛄', '🛅', '🛆', '🛇', '🛈', '🛉', '🛊', '🛋️', '🛌', '🛍️', '🛎️', '🛏️', '🛐', '🛑', '🛒', '🛓', '🛔', '🛕', '🛖', '🛗', '🛘', '🛙', '🛚', '🛛️', '🛜', '🛝', '🛞', '🛟', '🛠️', '🛡️', '🛢️', '🛣️', '🛤️', '🛥️', '🛦️', '🛧️', '🛨️', '🛩️', '🛪️', '🛫️', '🛬️', '🛭️', '🛮️', '🛯️', '🛰️', '🛱️', '🛲️', '🛳️', '🛴️', '🛵️', '🛶️', '🛷️', '🛸️', '🛹️', '🛺️', '🛻️', '🛼️', '🛽️', '🛾️', '🛿️', '🦠', '🦸', '🦹', '🦺', '🦻', '🦼', '🦽', '🧐', '🤓', '😎', '🤩', '🤔', '🤐', '🤨', '😐', '😑', '😶', '🙄', '😏', '😒', '🙃', '😬', '😴', '😌', '😛', '😜', '😝', '🤗', '🤭', '🤫', '🤔', '🤐', '🤨', '😐', '😑', '😶', '🙄', '😏', '😒', '🙃', '😬', '😴', '😌', '😛', '😜', '😝', '🤗', '🤭', '🤫', '🤔', '🤐', '🤨', '😐', '😑', '😶', '🙄', '😏', '😒', '🙃', '😬', '😴', '😌', '😛', '😜', '😝', '🤗', '🤭', '🤫'];
// 从菜单树中获取所有菜单项
const getAllMenuItems = (menuNodes) => {
const items = [];
// // 有效的快捷菜单路由列表,从路由配置中提取
menuNodes.forEach(menuNode => {
// 只有叶子节点且路径有效才作为可点击的菜单项
if (menuNode.path && menuNode.path !== '#' && menuNode.path.trim() !== '' && typeof menuNode.path === 'string' && menuNode.menuType !== 'F' && (!menuNode.children || menuNode.children.length === 0)) {
// 确保路径以斜杠开头
// const normalizedPath = node.path.startsWith('/') ? node.path : `/${node.path}`;
const normalizedPath ="/"+ menuNode.parentPath +(menuNode.path.startsWith('/') ? menuNode.path : `/${menuNode.path}`);
// 检查路径是否在有效的快捷菜单路由列表中
items.push({
id: menuNode.menuId.toString(),
label: menuNode.menuName,
path: normalizedPath,
icon: this.getMenuIcon(menuNode.menuName)
});
}
if (menuNode.children && menuNode.children.length > 0) {
items.push(...getAllMenuItems(menuNode.children));
}
});
return items;
};
// 构建完整的菜单列表(包括从菜单树中获取的),并去重
const menuMap = new Map();
// 先添加this.allMenus中的菜单项
this.allMenus.forEach(menu => {
if (menu && menu.id) {
menuMap.set(menu.id, menu);
}
});
// 再添加从菜单树中获取的菜单项,相同id的会覆盖,避免重复
const treeMenus = getAllMenuItems(this.menuTree);
treeMenus.forEach(menu => {
if (menu && menu.id) {
menuMap.set(menu.id, menu);
}
});
// 转换为数组,得到去重后的菜单列表
const completeMenuList = Array.from(menuMap.values());
// 根据选中的菜单ID获取菜单详情,并为每个菜单分配随机图标
// 覆盖已选取的 菜单图标
const selectedMenuConfig = completeMenuList.filter(menu =>
menu && this.configForm.selectedMenus.includes(menu.id) && menu.path && typeof menu.path === 'string' && menu.path.trim() !== '' && menu.path !== '#'
).map(menu => {
// 为每个菜单生成随机图标
const randomIcon = randomIcons[Math.floor(Math.random() * randomIcons.length)];
return {
...menu,
icon: randomIcon
};
});
// 如果没有选择菜单,使用默认配置
const finalMenuConfig = selectedMenuConfig.length > 0
? selectedMenuConfig
: this.defaultMenuConfig;
localStorage.setItem('quickMenuConfig', JSON.stringify(finalMenuConfig));
// 更新时间戳,触发displayMenus计算属性重新计算
this.menuUpdateTime = Date.now();
this.configDialogVisible = false;
this.$message.success('配置保存成功!');
},
// 切换菜单展开/折叠状态
toggleMenu(menuId) {
this.$set(this.expandedMenus, menuId, !this.expandedMenus[menuId]);
},
// 取消配置
cancelMenuConfig() {
// 重置选中的菜单
this.configForm.selectedMenus = [];
// 关闭配置抽屉
this.configDialogVisible = false;
this.$message.info('已取消配置');
},
}
} }
</script> </script>
...@@ -32,5 +434,86 @@ export default { ...@@ -32,5 +434,86 @@ export default {
.dashboard-description { .dashboard-description {
font-size: 16px; font-size: 16px;
color: #606266; color: #606266;
margin-bottom: 30px;
}
.quick-menu {
width: 100%;
max-width: 800px;
margin-top: 20px;
}
.menu-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.menu-header h3 {
font-size: 18px;
font-weight: 600;
color: #303133;
margin: 0;
}
.menu-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.menu-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
background-color: #f5f7fa;
border-radius: 8px;
text-decoration: none;
color: #303133;
transition: all 0.3s ease;
border: 1px solid #ebeef5;
}
.menu-item:hover {
background-color: #ecf5ff;
border-color: #d9ecff;
transform: translateY(-2px);
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.menu-icon {
font-size: 32px;
margin-bottom: 10px;
}
.menu-label {
font-size: 14px;
font-weight: 500;
text-align: center;
}
/* 菜单级别样式 */
.menu-level-1 {
font-size: 20px !important;
font-weight: 700 !important;
color: #FF7A00 !important; /* 橙色 */
cursor: pointer !important;
margin-left: -10px !important;
}
.menu-level-2 {
font-size: 16px !important;
font-weight: 600 !important;
color: #9D50BB !important; /* 浅紫色 */
cursor: pointer !important;
}
.menu-level-3 span.el-checkbox__label {
font-size: 14px !important;
font-weight: 400 !important;
color: #606266 !important;
} }
</style> </style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论