个人主页增加菜单编辑功能

发布时间:2026/8/24 21:33:50
个人主页增加菜单编辑功能 个人主页增加下菜单编辑功能功能localStorage 存储哪些菜单显示哪些菜单隐藏。如果这个菜单网页里有但是 localStorage 缓存里如果没有这个菜单是否显示默认显示。编辑菜单中可以编辑所有菜单包括网页里的菜单以及本地存储中新增的菜单。包括增、删、修改。完整代码!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleXuange2026 的个人主页/title style * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif; height: 100vh; display: flex; background: #f5f5f5; overflow: hidden; } /* 左侧菜单 */ .sidebar { width: 220px; background: #2c3e50; color: white; display: flex; flex-direction: column; overflow-y: auto; box-shadow: 2px 0 8px rgba(0,0,0,0.1); transition: width 0.3s ease; position: relative; } .sidebar.hidden { width: 0; overflow: hidden; } .sidebar h2 { padding: 20px 16px 10px; font-size: 18px; font-weight: 500; border-bottom: 1px solid rgba(255,255,255,0.2); margin-bottom: 10px; text-align: center; } .toggle-sidebar-inner{ position:absolute; top:12px; right:8px; width:24px; height:24px; background:rgba(255,255,255,0.12); border:none; color:#fff; border-radius:4px; cursor:pointer; display:flex; align-items:center; justify-content:center; } .toggle-sidebar-inner:hover{ background:rgba(255,255,255,0.22); } .toggle-sidebar-float{ position:fixed; left:0; top:50%; transform:translateY(-50%); width:28px; height:60px; background:#2c3e50; color:white; border:none; border-radius:0 6px 6px 0; cursor:pointer; display:none; z-index:999; } .toggle-sidebar-float.show{ display:flex; align-items:center; justify-content:center; } .menu-list { list-style: none; flex: 1; padding: 0 8px; } .group-header{ padding:10px 12px; margin:6px 0 2px; background:rgba(255,255,255,0.08); border-radius:6px; cursor:pointer; display:flex; align-items:center; font-size:15px; } .group-header .arrow{ margin-right:6px; transition: transform 0.2s; } .group-header.collapsed .arrow{ transform: rotate(-90deg); } .group-children{ list-style:none; overflow:hidden; transition: height 0.24s ease; } .group-children.collapse{ height:0 !important; } .menu-item { padding: 10px 14px 10px 26px; margin: 3px 0; border-radius: 6px; cursor: pointer; transition: background 0.2s; color: #ecf0f1; font-size: 14px; word-break: break-all; } .menu-item:hover { background: rgba(255,255,255,0.15); } .menu-item.active { background: #3498db; color: white; font-weight: 500; } .content { flex: 1; position: relative; background: white; } .iframe-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; display: none; } .iframe-container.active { display: block; } .placeholder { display: flex; align-items: center; justify-content: center; height: 100%; color: #95a5a6; font-size: 20px; } .placeholder.hidden { display: none; } .secret-trigger{ position:fixed; bottom:8px; right:8px; width:16px; height:16px; background:#607d8b; border-radius:50%; z-index:99999; cursor:pointer; } .secret-trigger:hover{ background:#d44e6c; } .love-layer{ position:fixed; inset:0; z-index:9999; display:none; } .love-layer.show{ display:block; } .love-layer iframe{ width:100%; height:100%; border:none; } .love-close{ position:fixed; top:12px; right:12px; z-index:10001; background:rgba(255,255,255,0.35); border:1px solid #d44e6c; color:#d44e6c; padding:4px 10px; border-radius:8px; cursor:pointer; font-size:13px; } .love-close:hover{ background:rgba(255,255,255,0.7); } /* 菜单编辑按钮与弹窗 */ .sidebar-header-row{ display:flex; justify-content:space‑between; align-items:center; padding:12px 8px 0 8px; } .sidebar-edit-btn{ background:rgba(255,255,255,0.15); border:none; color:#fff; border-radius:4px; padding:3px 8px; font-size:12px; cursor:pointer; } .sidebar-edit-btn:hover{ background:rgba(255,255,255,0.28); } .edit-modal{ position:fixed; inset:0; background:rgba(0,0,0,0.55); z-index:100000; display:none; align-items:center; justify-content:center; } .edit-modal.show{ display:flex; } .edit-panel{ width:920px; max-height:85vh; background:#fff; border-radius:10px; overflow:hidden; display:flex; flex-direction:column; } .edit-header{ padding:14px 18px; background:#2c3e50; color:#fff; display:flex; justify-content:space-between; align-items:center; } .edit-body{ flex:1; overflow:auto; padding:16px; } .edit-footer{ padding:12px 18px; border-top:1px solid #ddd; display:flex; gap:10px; justify-content:flex-end; } .group-edit-block{ border:1px solid #ccc; margin-bottom:14px; border-radius:6px; padding:12px; } .child-edit-item{ border:1px solid #eee; padding:10px; margin:8px 0; border-radius:4px; display:flex; gap:8px; align-items:center; flex-wrap:wrap; } input,select{ padding:4px 6px; font-size:13px; } button{ padding:4px 8px; font-size:13px; cursor:pointer; } /style /head body div classsecret-trigger idsecretBtn title秘密入口/div div classsidebar idsidebar div classsidebar-header-row button classtoggle-sidebar-inner idtoggleInner◀/button button classsidebar-edit-btn idopenEditBtn编辑/button /div h2常用工具/h2 ul classmenu-list idmenuList/ul /div button classtoggle-sidebar-float idtoggleFloat▶/button div classcontent idcontentArea div classplaceholder idplaceholder 点击左侧菜单打开页面/div /div div classlove-layer idloveLayer button classlove-close idcloseLove← 返回工具页/button iframe srclove.html/iframe /div !-- 编辑弹窗 -- div classedit-modal ideditModal div classedit-panel div classedit-header span菜单编辑内置本地新增/span /div div classedit-body ideditBody/div div classedit-footer button idresetDefaultBtn恢复默认菜单/button button idcancelEditBtn取消/button button idsaveEditBtn保存并应用/button /div /div /div script (function() { // 原始内置菜单只读不会被修改 const defaultGroups [ { groupName:开发工具, collapsed:false, lastGroupClickTs: null, children:[ { name: JSON格式化, url: https://www.sojson.com/, allowIframe: true, lastClickTs:null, hidden:false }, { name: 时间戳转换, url: https://tool.lu/timestamp/, allowIframe: true, lastClickTs:null, hidden:false }, {name: base64编解码, url: https://base64.us/, allowIframe: true, lastClickTs:null, hidden:false}, {name: 进制转化, url: https://tool.oschina.net/hexconvert/, allowIframe: true, lastClickTs:null, hidden:false}, {name: 草料二维码, url: https://cli.im/, allowIframe: true, lastClickTs:null, hidden:false} ] }, { groupName:AI工具, collapsed:false, lastGroupClickTs: null, children:[ {name: deepseek, url: https://chat.deepseek.com/, allowIframe: false, lastClickTs:null, hidden:false}, {name: 豆包, url: https://www.doubao.com/chat, allowIframe: true, lastClickTs:null, hidden:false}, {name: 文心, url: https://wenxin.baidu.com/?extParams%7B%22enter_type%22%3A%22home_operate%22%7D, allowIframe: false, lastClickTs:null, hidden:false}, {name: 千问, url: https://www.qianwen.com/?chtongyi_redirect, allowIframe: false, lastClickTs:null, hidden:false} ] }, { groupName:刷题平台, collapsed:false, lastGroupClickTs: null, children:[ {name: 力扣, url: https://leetcode.cn/, allowIframe: true, lastClickTs:null, hidden:false}, {name: 牛客, url: https://www.nowcoder.com/, allowIframe: true, lastClickTs:null, hidden:false} ] }, { groupName:搜索博客, collapsed:false, lastGroupClickTs: null, children:[ { name: 百度, url: https://www.baidu.com/, allowIframe: false, lastClickTs:null, hidden:false }, { name: 必应, url: https://www.bing.com/, allowIframe: false, lastClickTs:null, hidden:false }, {name: 旋哥博客, url: https://blog.csdn.net/u012116089?typeblog, allowIframe: false, lastClickTs:null, hidden:false}, {name: c语言中文网, url: https://c.54benniao.com/, allowIframe: true, lastClickTs:null, hidden:false} ] }, { groupName:代码仓库, collapsed:false, lastGroupClickTs: null, children:[ {name: gitee, url: https://gitee.com/, allowIframe: false, lastClickTs:null, hidden:false}, {name: github, url: https://github.com/, allowIframe: false, lastClickTs:null, hidden:false} ] } ]; const STORAGE_USER_GROUPS site_user_groups; const STORAGE_SIDEBAR site_sidebar_state; const STORAGE_GROUP_COLLAPSE site_groups_collapse; const STORAGE_CLICK_RECORD site_click_record; // 运行时使用的菜单数据 let groups; // 初始化优先读取用户自定义配置没有就拷贝默认 function initGroups(){ const userRaw localStorage.getItem(STORAGE_USER_GROUPS); if(userRaw){ groups JSON.parse(userRaw); }else{ groups JSON.parse(JSON.stringify(defaultGroups)); } } const menuList document.getElementById(menuList); const contentArea document.getElementById(contentArea); const placeholder document.getElementById(placeholder); const secretBtn document.getElementById(secretBtn); const loveLayer document.getElementById(loveLayer); const closeLove document.getElementById(closeLove); const sidebar document.getElementById(sidebar); const toggleInner document.getElementById(toggleInner); const toggleFloat document.getElementById(toggleFloat); const openEditBtn document.getElementById(openEditBtn); const editModal document.getElementById(editModal); const editBody document.getElementById(editBody); const cancelEditBtn document.getElementById(cancelEditBtn); const saveEditBtn document.getElementById(saveEditBtn); const resetDefaultBtn document.getElementById(resetDefaultBtn); const iframeCache {}; let currentActiveIndex -1; let flatMenuList []; function loadSidebarState(){ const val localStorage.getItem(STORAGE_SIDEBAR); if(val hidden){ sidebar.classList.add(hidden); toggleFloat.classList.add(show); }else{ sidebar.classList.remove(hidden); toggleFloat.classList.remove(show); } } function saveSidebarState(){ if(sidebar.classList.contains(hidden)){ localStorage.setItem(STORAGE_SIDEBAR,hidden); }else{ localStorage.removeItem(STORAGE_SIDEBAR); } } function loadGroupCollapseState(){ const json localStorage.getItem(STORAGE_GROUP_COLLAPSE); if(!json) return; try{ const saved JSON.parse(json); groups.forEach((g,idx){ if(saved[idx] ! undefined){ g.collapsed saved[idx]; } }); }catch(e){} } function saveGroupCollapseState(){ const arr groups.map(gg.collapsed); localStorage.setItem(STORAGE_GROUP_COLLAPSE, JSON.stringify(arr)); } function loadClickRecord(){ const raw localStorage.getItem(STORAGE_CLICK_RECORD); if(!raw) return; try{ const rec JSON.parse(raw); groups.forEach((g,gIdx){ if(rec.groupTs rec.groupTs[gIdx] ! undefined){ g.lastGroupClickTs rec.groupTs[gIdx]; } g.children.forEach((child,cIdx){ const key ${gIdx}_${cIdx}; if(rec.childTs rec.childTs[key]!undefined){ child.lastClickTs rec.childTs[key]; } }) }) }catch(e){} } function saveClickRecord(){ const groupTs {}; const childTs {}; groups.forEach((g,gIdx){ groupTs[gIdx] g.lastGroupClickTs; g.children.forEach((child,cIdx){ childTs[${gIdx}_${cIdx}] child.lastClickTs; }) }) localStorage.setItem(STORAGE_CLICK_RECORD, JSON.stringify({groupTs,childTs})); } function toggleSidebar(){ sidebar.classList.toggle(hidden); if(sidebar.classList.contains(hidden)){ toggleFloat.classList.add(show); }else{ toggleFloat.classList.remove(show); } saveSidebarState(); } toggleInner.addEventListener(click, toggleSidebar); toggleFloat.addEventListener(click, toggleSidebar); function openLove(){ loveLayer.classList.add(show); } function closeLoveFunc(){ loveLayer.classList.remove(show); } secretBtn.addEventListener(click, openLove); closeLove.addEventListener(click, closeLoveFunc); function getSortedGroups(){ const sortedGroups [...groups].sort((a,b){ const ta a.lastGroupClickTs; const tb b.lastGroupClickTs; if(ta null tb null) return 0; if(ta null) return 1; if(tb null) return -1; return tb - ta; }); sortedGroups.forEach(g{ g.children.sort((a,b){ const ta a.lastClickTs; const tb b.lastClickTs; if(ta null tb null) return 0; if(ta null) return 1; if(tb null) return -1; return tb - ta; }) }) return sortedGroups; } function renderMenu() { menuList.innerHTML ; flatMenuList []; const sortedGroups getSortedGroups(); sortedGroups.forEach((group){ const liGroup document.createElement(li); const header document.createElement(div); header.className group-header; if(group.collapsed) header.classList.add(collapsed); header.innerHTML span classarrow▼/spanspan${group.groupName}/span; const ulChild document.createElement(ul); ulChild.className group-children; if(group.collapsed) ulChild.classList.add(collapse); header.addEventListener(click,(){ group.collapsed !group.collapsed; header.classList.toggle(collapsed, group.collapsed); ulChild.classList.toggle(collapse, group.collapsed); saveGroupCollapseState(); }); // ✅ 渲染时跳过 hiddentrue 的菜单 group.children.forEach(childItem{ if(childItem.hidden true) return; const idx flatMenuList.length; flatMenuList.push(childItem); const li document.createElement(li); li.className menu-item; li.textContent childItem.name; li.dataset.index idx; li.addEventListener(click, () { const now Date.now(); childItem.lastClickTs now; group.lastGroupClickTs now; saveClickRecord(); switchToMenu(idx); }); ulChild.appendChild(li); }); liGroup.appendChild(header); liGroup.appendChild(ulChild); menuList.appendChild(liGroup); }); } function switchToMenu(index) { const item flatMenuList[index]; if(!item.allowIframe){ window.open(item.url, _blank); return; } if (currentActiveIndex index) return; const allItems menuList.querySelectorAll(.menu-item); allItems.forEach(el el.classList.remove(active)); allItems[index].classList.add(active); placeholder.classList.add(hidden); if (currentActiveIndex ! -1) { const prevIframe iframeCache[currentActiveIndex]; if (prevIframe) prevIframe.classList.remove(active); } if (!iframeCache[index]) createIframe(index); iframeCache[index].classList.add(active); currentActiveIndex index; } function createIframe(index) { const iframe document.createElement(iframe); iframe.className iframe-container; iframe.src flatMenuList[index].url; iframe.setAttribute(sandbox, allow-same-origin allow-scripts allow-popups allow-forms); contentArea.appendChild(iframe); iframeCache[index] iframe; } // 菜单编辑弹窗逻辑 let editCopy null; // 打开编辑弹窗深拷贝当前groups用于编辑 function openEditModal(){ editCopy JSON.parse(JSON.stringify(groups)); renderEditPanel(); editModal.classList.add(show); } function closeEditModal(){ editModal.classList.remove(show); editCopy null; } // 渲染编辑面板 function renderEditPanel(){ editBody.innerHTML ; editCopy.forEach((g, gIdx){ const gBlock document.createElement(div); gBlock.className group-edit-block; gBlock.innerHTML div styledisplay:flex;gap:8px;align-items:center;margin-bottom:8px; label分组名称:/label input classgname value${g.groupName} />点击编辑可以编辑可以新增分组也可以恢复到默认菜单。ok.