/* 框架页面js文件 */
// 渲染头像和昵称
document.querySelector('.avatar').innerHTML = `
<img src="./img/10043.png" alt="用户头像"
onclick="preview('./img/10043.png')"
onerror="this.src='./img/10043.png'" />
<div class="nickname">${proof?.userinfo?.nickname || proof?.nickname || '用户'}</div>
`;
// 映射菜单图表
const firstLevelIconMap = {
1: './img/IconBarChart.png',
2: './img/IconUserGroup.png',
3: './img/IconIdcard.png',
4: './img/IconCalendarClock.png',
5: './img/IconUser.png',
6: './img/IconApps.png',
7: './img/IconArchive.png',
10: './img/IconFile.png',
11: './img/IconImage.png',
default: './img/default-icon.png' //默认
};
let tree = []; //用于存储构建好的树形结构
let data; //用于存储经过处理后的树形结构数据,后续用于渲染页面
// 获取菜单数据
let menuData = proof.menu;
// 构建树形结构
let menuTree = buildTree(menuData);
data = menuTree;
/**
* 构建树形结构
* @param {Array} menu - 传参菜单数据数组
* @returns {Array} - 构建后的树形结构数组
*/
function buildTree(menu) {
tree = [];
let mapped = {};
// 第一遍循环,初始化mapped对象并且初始化children属性
menu.forEach(menu => {
mapped[menu.id] = menu;
menu.children = menu.children || []; // 确保children属性始终是数组
});
// 第二遍循环,根据pid构建树形结构
menu.forEach(menu => {
if (menu.pid !== 0) {
if (mapped[menu.pid]) {
mapped[menu.pid].children.push(menu);
}
} else {
tree.push(menu);
}
});
return tree;
}
// 检查数据是否有效
if (data && Array.isArray(data)) {
// 渲染树形菜单
render(data);
}
// 将数据中的菜单赋值给 `menu` 变量,方便后续使用
let menu = data;
// 声明数组 `arr`,用于后续渲染面包屑导航
let arr;
/**
* 渲染树形菜单的递归函数
* @param {Array} menu - 菜单数据数组
* @param {boolean} isFirstLevel - 标记是否为一级菜单
* @returns {string} 拼接好的 HTML 字符串
*/
function render(menu, isFirstLevel = true) {
let str = '';
// 菜单数据验证(原有逻辑保留)
if (!menu || !Array.isArray(menu)) {
console.error('Invalid menu data:', menu);
return str;
}
for (let i in menu) {
const menuItem = menu[i];
if (!menuItem || typeof menuItem !== 'object') continue;
// 基础数据获取
const locale = menuItem.meta && menuItem.meta.locale ? menuItem.meta.locale : '未命名菜单';
const menuId = menuItem.id || ''; // 一级菜单ID(用于匹配图标)
const path = menuItem.path || '#';
// 仅一级菜单:匹配图标(从firstLevelIconMap取,无匹配用默认)
let iconHtml = ''; // 存储图标HTML(子菜单为空)
if (isFirstLevel) { // 仅当"是一级菜单"时,拼接图标HTML
const iconSrc = firstLevelIconMap[menuId] || firstLevelIconMap.default;
iconHtml = `<img src="${iconSrc}" alt="${locale}" class="menu-icon" />`;
}
// 3. 有子菜单的菜单项
if (menuItem.children && Array.isArray(menuItem.children) && menuItem.children.length > 0) {
str += `<p class="option_parent" data-menu-id="${menuId}">`;
str += iconHtml;
str += `<span class="nickname-text">${locale}</span>`;
str += `<img src="./img/arrows.png" alt="" data-arrow="true" class="arrow-icon" /></p>`;
str += `<div class="child" style="display:none;">`;
// 递归渲染子菜单
str += render(menuItem.children, false);
str += `</div>`;
}
// 4. 无子菜单的菜单项
else {
str += `<p class="option" data-id="${menuId}" onclick="navigateToMenu(1,'${path}','${locale}','${menuId}')">`;
str += iconHtml; // 仅一级菜单插入图标
str += `<span class="nickname-text">${locale}</span></p>`;
}
}
return str;
}
// 调用 `render` 函数,开始渲染树形菜单
let str = render(menu);
// 将渲染好的菜单项插入到页面的 .option_box 元素中
document.querySelector(".option_box").innerHTML = str;
// 获取所有树形菜单中的选项元素
let tree_option = document.querySelectorAll(".option");
// 监听 class 为 'option_box' 的鼠标松开事件(mouseup)
document.getElementsByClassName('option_box')[0].addEventListener('mouseup', function (e) {
e = e || window.event;
let target = e.target || e.srcElement;
// 关键修复:如果点击的是父菜单文字,向上找到对应的父菜单容器
if (target.classList.contains('nickname-text')) {
target = target.closest('.option_parent');
}
// 如果点击的是图标或箭头,向上查找父菜单元素(option_parent 或 option)
else if (target.classList.contains('menu-icon') || target.classList.contains('arrow-icon')) {
target = target.parentElement;
}
// 点击子菜单选项(option):高亮当前子菜单
if (target?.className === 'option') {
highlightTargetOnly(target); // 仅子菜单保留高亮
}
// 点击父菜单选项(option_parent):仅展开,不高亮
else if (target?.className === 'option_parent') {
// 此处删除 highlightTargetOnly(target);
}
// 处理子菜单的显示/隐藏逻辑(仅针对有子菜单的父菜单)
if (target && target.nextElementSibling !== null && target.nextElementSibling.className === 'child') {
const childMenu = target.nextElementSibling;
childMenu.style.display = childMenu.style.display === 'none' ? 'block' : 'none';
}
// 处理箭头图标旋转状态(仅针对有箭头的父菜单)
if (target) {
const arrowIcon = target.querySelector('.arrow-icon');
if (arrowIcon) {
const isExpanded = arrowIcon.getAttribute('data-arrow') === 'false';
if (isExpanded) {
arrowIcon.classList.remove('arrowhead');
arrowIcon.setAttribute('data-arrow', 'true');
} else {
arrowIcon.classList.add('arrowhead');
arrowIcon.setAttribute('data-arrow', 'false');
}
}
}
});
// 检查 sessionStorage 中是否存在 'main' 键值对
if (sessionStorage.getItem('main')) {
// 获取到面包屑数据,使用 JSON.parse 解析字符串为数组,在为arr赋值
arr = JSON.parse(sessionStorage.getItem('main'));
} else {
// arr没有值就将"数据统计"的数据放入
arr = [{
status: 1,
path: menu[0].path,
locale: menu[0].meta.locale, // 从meta中获取locale
id: menu[0].id
}];
}
// 页面加载完成后恢复高亮状态
document.addEventListener('DOMContentLoaded', function () {
addCrumb(arr); // 调用面包屑的渲染函数
restoreMenuHighlight(); // 恢复菜单高亮状态
});
// 面包屑点击添加函数,通过传参将状态,路径,名称,id传过去
function navigateToMenu(status, path, locale, id) {
let obj = {
status: status,
path: path,
locale: locale,
id: id
};
let isExists = false;
// 检查是否已存在该菜单项
for (let i = 0; i < arr.length; i++) {
if (arr[i].id === id) {
isExists = true;
// 重置所有状态为默认,再将匹配项设为高亮
for (let j = 0; j < arr.length; j++) {
arr[j].status = 2;
}
arr[i].status = 1;
break;
}
}
// 不存在则添加新项
if (!isExists) {
for (let i = 0; i < arr.length; i++) {
arr[i].status = 2;
}
arr.push(obj);
arr[arr.length - 1].status = 1;
}
sessionStorage.setItem('main', JSON.stringify(arr));
addCrumb(arr);
}
// 面包屑渲染函数
function addCrumb(arr) {
// 用于存储除首页外的面包屑 HTML 代码
let bread = '';
// 用于存储首页的面包屑 HTML 代码
let breads = '';
// 存储当前激活的菜单ID(用于后续高亮)
let activeId = null;
// 遍历传入的数组,处理每个面包屑项
for (let i in arr) {
// 记录当前激活的菜单ID
if (arr[i].status === 1) {
activeId = arr[i].id;
}
// 渲染首页面包屑
if (arr[i].locale === '数据统计') {
breads = `
<div class="home_bread ${arr[i].status === 1 ? 'highlighted' : ''}">
<img src="${arr[i].status === 1 ? './img/lightHome.png' : './img/home.png'}" alt="首页图标" class="home_icon" />
<div class="page" onclick="target(1, '${arr[i].path}', '${arr[i].locale}','${arr[i].id}')">${arr[i].locale}</div>
</div>
`;
} else {
// 渲染其他面包屑
bread += `
<div class="crumb ${arr[i].status === 1 ? 'highlighted' : ''}">
<p class="crumbName" onclick="target(${arr[i].status},'${arr[i].path}','${arr[i].locale}','${arr[i].id}')">${arr[i].locale}</p>
<div class="omit" onclick="delete_btn('${arr[i].locale}', ${i})">
<img src="./img/delete.png" alt="删除图标" />
</div>
</div>
`;
}
// 更新 iframe 内容
if (arr[i].status === 1) {
// 修复路径处理逻辑
let pathUrl = arr[i].path.startsWith('./') ? arr[i].path :
arr[i].path.startsWith('/') ? '.' + arr[i].path + '.html' :
'./' + arr[i].path + '.html';
checkUrl(pathUrl, function (isValid) {
if (isValid) {
// 如果路径有效,加载 iframe
document.getElementById("page").style.display = "block";
document.getElementById("page").setAttribute("src", pathUrl);
document.getElementById("devAlert").style.display = "none";
} else {
// 如果路径无效,隐藏 iframe 并显示提示
document.getElementById("page").style.display = "none";
document.getElementById("devAlert").style.display = "flex";
}
});
}
}
// 将面包屑 HTML 插入到容器中
document.querySelector('.crumb_content').innerHTML = bread;
document.querySelector('.home').innerHTML = breads;
// 恢复菜单高亮状态
restoreMenuHighlight();
}
// 恢复菜单高亮状态函数
function restoreMenuHighlight() {
if (!arr || arr.length === 0) return;
// 查找当前激活的菜单项
let activeItem = arr.find(item => item.status === 1);
if (!activeItem) return;
// 移除所有菜单的高亮
document.querySelectorAll(".option, .option_parent").forEach(el => {
el.classList.remove("backColor");
});
// 高亮匹配的子菜单
document.querySelectorAll(`.option[data-id="${activeItem.id}"]`).forEach(el => {
el.classList.add("backColor");
});
// 高亮匹配的父菜单
document.querySelectorAll(`.option_parent[data-menu-id="${activeItem.id}"]`).forEach(el => {
el.classList.add("backColor");
});
// 动态控制父菜单箭头旋转
let activeElement = document.querySelector(".backColor");
if (activeElement) {
// 查找最近的父菜单容器
let parentContainer = activeElement.closest('.child');
if (parentContainer && parentContainer.style.display === "none") {
// 展开隐藏的子菜单
parentContainer.style.display = "block";
// 切换箭头方向
let arrowIcon = parentContainer.previousElementSibling.querySelector('.arrow-icon');
if (arrowIcon) {
arrowIcon.classList.add("arrowhead");
arrowIcon.setAttribute("data-arrow", "false");
}
}
}
}
function highlightTargetOnly(target) {
// 移除所有菜单(父 + 子)的高亮
document.querySelectorAll(".option, .option_parent").forEach(el => {
el.classList.remove("backColor");
});
// 为当前点击的菜单添加高亮
target.classList.add("backColor");
}
// 检查 URL 是否有效的函数
function checkUrl(url, callback) {
const xhr = new XMLHttpRequest();
xhr.open("HEAD", url, true);
xhr.onload = function () {
// 如果状态码为 404,则说明路径不存在
if (xhr.status === 404) {
callback(false);
} else {
callback(true);
}
};
xhr.onerror = function () {
callback(false); // 如果请求发生错误,也视为路径不存在
};
xhr.send();
}
// 面包屑的删除函数
function delete_btn(locale, index) {
// 判断当前删除的如果是最后一个,并且最后一个时是当前高亮状态
if (index == arr.length - 1 && arr[arr.length - 1].status == 1) {
// 将当前删除后的最后一个设置为高亮
arr[arr.length - 2].status = 1;
sessionStorage.setItem('main', JSON.stringify(arr));
}
// 再判断当前删除的如果是高亮状态的,并且不是面包屑最后一个
if (arr[index].status == 1 && arr[arr.length - 1].status != 1) {
// 将当前删除面包屑后面的一个
arr[index + 1].status = 1;
sessionStorage.setItem('main', JSON.stringify(arr));
}
// 删除当前面包屑
arr.splice(index, 1);
sessionStorage.setItem('main', JSON.stringify(arr));
// 重新调用一下渲染函数
addCrumb(arr);
}
// 面包屑的点击函数
function target(status, path, title, id) {
// 将当前的面包屑循环一遍
for (let i in arr) {
// 将全部的状态改为默认状态
arr[i].status = 2;
// 再将当前的数据重新存储一下
sessionStorage.setItem('main', JSON.stringify(arr));
// 利用判断得到当前的面包屑
if (arr[i].id == id) {
// 将当前面包屑的状态改为高亮状态
arr[i].status = 1;
// 重新存储一下数据
sessionStorage.setItem('main', JSON.stringify(arr));
}
}
// 调用渲染函数,刷新页面状态
addCrumb(arr);
}
// 点击全屏开关函数
function full() {
// 判断当前页面是否处于全屏状态
if (!document.fullscreenElement) {
// 如果不是全屏状态,则请求将整个文档元素设置为全屏显示
document.documentElement.requestFullscreen();
// 切换全屏按钮的图标为退出全屏图标
document.querySelector("#screen img").setAttribute("src", `./img/screen.png`);
} else {
// 如果已经是全屏状态,则尝试退出全屏
if (document.exitFullscreen) {
// 调用退出全屏的方法
document.exitFullscreen();
// 切换全屏按钮的图标为进入全屏图标
document.querySelector("#screen img").setAttribute("src", `./img/allScreen.png`);
}
}
}
document.addEventListener("fullscreenchange", updateIcon);
// 该函数用于根据当前的全屏状态更新全屏按钮的图标
function updateIcon() {
// 判断当前是否处于全屏状态,考虑了不同浏览器的兼容性
if (
document.fullscreenElement || // 标准的全屏元素属性
document.webkitFullscreenElement || // WebKit 浏览器(如 Safari)的全屏元素属性
document.mozFullScreenElement || // Firefox 浏览器的全屏元素属性
document.msFullscreenElement // IE 浏览器的全屏元素属性
) {
// 如果处于全屏状态,将全屏按钮的图标设置为退出全屏图标
document.querySelector("#screen img").setAttribute("src", `./img/screen.png`);
} else {
// 如果不是全屏状态,将全屏按钮的图标设置为进入全屏图标
document.querySelector("#screen img").setAttribute("src", `./img/allScreen.png`);
}
}
// 刷新
function refresh() {
document.querySelector("#refresh img").style.transform = 'rotate(360deg)';
document.querySelector("#refresh img").style.transition = 'all 0.5s';
// 延时500毫秒后刷新页面
setTimeout(function () {
window.location.reload();
}, 500);
}
//点击跳转到锁屏页面
function lock() {
window.location.replace('./lock.html');
sessionStorage.setItem('lock', 1);
}
// 头像点击退出登录
function exit() {
document.querySelector('.over').style.display = 'block';
}
function cancel() {
document.querySelector('.over').style.display = 'none';
}
// 点击确定清空数据,并跳转到登录页
function sure() {
sessionStorage.clear();
window.location.replace("./login.html");
}
// 全局变量,用于跟踪菜单折叠状态
let isMenuCollapsed = false;
let deg = true; // 声明状态用于判断菜单是否展开
// 折叠
function toggle() {
const treeBar = document.querySelector('.tree_bar');
const content = document.querySelector('.content');
const toolImg = document.querySelector('.tool_img img');
if (deg) {
// 折叠菜单
treeBar.classList.add('collapsed');
content.classList.add('expanded');
toolImg.style.transform = 'rotate(180deg)';
isMenuCollapsed = true;
// 折叠时强制收起所有子菜单
document.querySelectorAll('.child').forEach(child => {
child.style.display = 'none';
});
// 折叠时恢复所有箭头方向
document.querySelectorAll('.arrow-icon').forEach(icon => {
icon.classList.remove('arrowhead');
icon.setAttribute('data-arrow', 'true');
});
} else {
// 展开菜单
treeBar.classList.remove('collapsed');
content.classList.remove('expanded');
toolImg.style.transform = 'rotate(0deg)';
isMenuCollapsed = false;
// 展开时恢复菜单高亮状态
restoreMenuHighlight();
}
deg = !deg;
}
// 将渲染好的菜单项插入到页面的 .option_box 元素中
document.querySelector(".option_box").innerHTML = str;
// 获取所有一级父菜单
const parentMenus = document.querySelectorAll(".option_parent");
let hoverTimer; // 用于延迟收起子菜单的计时器
parentMenus.forEach(parent => {
// 悬浮进入:展开子菜单
parent.addEventListener("mouseenter", function () {
// 仅当菜单处于折叠状态,且存在子菜单时,才展开
if (isMenuCollapsed) {
const childMenu = this.nextElementSibling; // 子菜单容器
const arrowIcon = this.querySelector(".arrow-icon");
if (childMenu && childMenu.className === "child") {
childMenu.style.display = "block";
}
if (arrowIcon) {
arrowIcon.classList.add("arrowhead");
arrowIcon.setAttribute("data-arrow", "false");
}
}
});
// 悬浮离开:延迟收起子菜单
parent.addEventListener("mouseleave", function () {
// 仅当菜单处于折叠状态,且存在子菜单时,才延迟收起
if (isMenuCollapsed) {
const childMenu = this.nextElementSibling;
const arrowIcon = this.querySelector(".arrow-icon");
// 延迟300ms收起(避免鼠标快速划过误触发)
hoverTimer = setTimeout(() => {
if (childMenu && childMenu.className === "child") {
childMenu.style.display = "none"; // 收起子菜单
}
if (arrowIcon) {
arrowIcon.classList.remove("arrowhead"); // 恢复箭头方向
arrowIcon.setAttribute("data-arrow", "true");
}
}, 100);
}
});
// 子菜单区域悬浮:延续父菜单悬浮状态
const childMenu = parent.nextElementSibling;
if (childMenu && childMenu.className === "child") {
// 子菜单悬浮进入:清除延迟收起计时器
childMenu.addEventListener("mouseenter", function () {
if (hoverTimer) clearTimeout(hoverTimer);
});
// 子菜单悬浮离开:延迟收起
childMenu.addEventListener("mouseleave", function () {
if (isMenuCollapsed) {
hoverTimer = setTimeout(() => {
childMenu.style.display = "none";
const arrowIcon = parent.querySelector(".arrow-icon");
if (arrowIcon) {
arrowIcon.classList.remove("arrowhead");
arrowIcon.setAttribute("data-arrow", "true");
}
}, 300);
}
});
}
});怎么让子菜单在折叠情况下悬浮在父菜单右边?
最新发布