// 纯DOM导航 - 通过 installNavigation 直接插入 (function() { console.log('[Nav] 脚本加载'); function injectStyles() { if (document.getElementById('navigation-styles')) return; const styles = ` .nav-wrapper, .navigation-wrapper { position: fixed; top: 60px; left: 0; bottom: 0; width: 180px; z-index: 9999; pointer-events: none; } .nav-wrapper .sidebar, .navigation-wrapper .sidebar { position: fixed; top: 60px; left: 0; bottom: 0; width: 180px; background: #fff; padding: 12px 8px; box-shadow: 2px 0 12px rgba(0,0,0,0.06); overflow-y: auto; z-index: 10000; border-right: 1px solid #ebeef5; pointer-events: auto; } .nav-wrapper .sidebar-header, .navigation-wrapper .sidebar-header { padding: 12px 12px 16px; border-bottom: 1px solid #f0f2f5; margin-bottom: 12px; } .nav-wrapper .sidebar-header h3, .navigation-wrapper .sidebar-header h3 { margin: 0; font-size: 15px; font-weight: 700; background: linear-gradient(135deg, #2563eb, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; letter-spacing: -0.3px; } .nav-wrapper .sidebar-btn, .navigation-wrapper .sidebar-btn { width: 100%; text-align: left; padding: 10px 12px; border: none; background: transparent; border-radius: 10px; margin-bottom: 4px; cursor: pointer; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); color: #606266; font-size: 13px; display: flex; align-items: center; gap: 8px; position: relative; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .nav-wrapper .sidebar-btn:hover, .navigation-wrapper .sidebar-btn:hover { background: #f0f4ff; color: #2563eb; transform: translateX(2px); } .nav-wrapper .sidebar-btn.active, .navigation-wrapper .sidebar-btn.active { background: linear-gradient(135deg, #eef2ff, #e0e7ff); color: #2563eb; font-weight: 600 !important; box-shadow: inset 3px 0 0 #2563eb; } .nav-wrapper .mobile-nav, .navigation-wrapper .mobile-nav { display: none; position: fixed; bottom: 0; left: 0; right: 0; background: linear-gradient(135deg, #2563eb, #1d4ed8); box-shadow: 0 -4px 20px rgba(37, 99, 235, 0.25); padding: 4px 0; z-index: 99999; justify-content: space-around; pointer-events: auto; } .nav-wrapper .mobile-nav-btn, .navigation-wrapper .mobile-nav-btn { flex: 1; border: none; background: transparent; padding: 4px 2px; text-align: center; font-size: 10px; color: rgba(255,255,255,0.8) !important; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 0; transition: all 0.2s; border-radius: 8px; margin: 0 1px; min-width: 0; } .nav-wrapper .mobile-nav-btn .nav-icon, .navigation-wrapper .mobile-nav-btn .nav-icon { font-size: 16px; line-height: 1.2; } .nav-wrapper .mobile-nav-btn .nav-text, .navigation-wrapper .mobile-nav-btn .nav-text { font-size: 9px; margin-top: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } .nav-wrapper .mobile-nav-btn:hover, .navigation-wrapper .mobile-nav-btn:hover { background: rgba(255,255,255,0.15); color: white !important; } .nav-wrapper .mobile-nav-btn.active, .navigation-wrapper .mobile-nav-btn.active { background: rgba(255,255,255,0.2); color: white !important; font-weight: 600 !important; } .mobile-more-backdrop { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 100000; animation: fadeIn 0.2s ease-out; } .mobile-more-sheet { position: fixed; bottom: 0; left: 0; right: 0; background: white; border-radius: 16px 16px 0 0; box-shadow: 0 -8px 32px rgba(0,0,0,0.15); z-index: 100001; padding: 20px 16px calc(env(safe-area-inset-bottom) + 60px); animation: slideUp 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); max-height: 70vh; overflow-y: auto; pointer-events: auto; } .mobile-more-sheet .sheet-handle { width: 36px; height: 4px; background: #e0e0e0; border-radius: 2px; margin: 0 auto 16px; } .mobile-more-sheet .sheet-title { font-size: 16px; font-weight: 700; color: #303133; margin-bottom: 16px; text-align: center; } .mobile-more-sheet .sheet-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; } .mobile-more-sheet .sheet-item { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 12px 4px; border: none; background: transparent; border-radius: 12px; cursor: pointer; transition: all 0.2s; color: #606266; font-size: 12px; white-space: nowrap; } .mobile-more-sheet .sheet-item:hover { background: #f0f4ff; color: #2563eb; } .mobile-more-sheet .sheet-item.active { background: #eef2ff; color: #2563eb; font-weight: 600; } .mobile-more-sheet .sheet-item .item-icon { font-size: 24px; line-height: 1; } @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @media (max-width: 768px) { .nav-wrapper .sidebar, .navigation-wrapper .sidebar { display: none !important; } .nav-wrapper .mobile-nav, .navigation-wrapper .mobile-nav { display: flex !important; } body > #app > .main-content { padding-bottom: 56px !important; } } body > #app > .main-content { margin-left: 180px !important; padding-top: 60px !important; } @media (max-width: 768px) { body > #app > .main-content { margin-left: 0 !important; padding-bottom: 60px !important; } } `; document.head.appendChild(Object.assign(document.createElement('style'), { id: 'navigation-styles', textContent: styles })); console.log('[Nav] 样式已注入'); } function createNavigation(currentPage, isAdmin, onNavigate) { const wrapper = document.createElement('div'); wrapper.className = 'navigation-wrapper'; // 侧边栏 const sidebar = document.createElement('aside'); sidebar.className = 'sidebar'; sidebar.innerHTML = ` `; // 移动端底部导航 const mobileNav = document.createElement('nav'); mobileNav.className = 'mobile-nav'; mobileNav.innerHTML = ` `; // "更多"弹出菜单 const sheetItems = [ { key: 'metrics', label: '数据分析', icon: '📊', page: 'metrics.html' }, { key: 'platforms', label: '平台配置', icon: '🌐', page: 'platforms.html' }, { key: 'logs', label: '系统日志', icon: '📄', page: 'logs.html' }, { key: 'users', label: '用户管理', icon: '👥', page: 'users.html', admin: true }, { key: 'admin', label: '系统管理', icon: '⚙️', page: 'admin.html', admin: true }, ]; const sheetItemsHtml = sheetItems.map(item => `` ).join(''); const backdrop = document.createElement('div'); backdrop.className = 'mobile-more-backdrop'; backdrop.id = 'mobile-more-backdrop'; backdrop.style.display = 'none'; const sheet = document.createElement('div'); sheet.className = 'mobile-more-sheet'; sheet.id = 'mobile-more-sheet'; sheet.style.display = 'none'; sheet.innerHTML = `
所有菜单
${sheetItemsHtml}
`; const showMore = () => { backdrop.style.display = ''; sheet.style.display = ''; }; const hideMore = () => { backdrop.style.display = 'none'; sheet.style.display = 'none'; }; // 绑定导航跳转 const handleNavClick = (btn) => { const page = btn.getAttribute('data-page'); if (!page) return; console.log('[Nav] 点击导航:', page); hideMore(); if (onNavigate) { onNavigate(page); return; } const target = page === '/' ? '/' : (page.startsWith('/') ? page : '/' + page); window.location.href = target; }; wrapper.appendChild(sidebar); wrapper.appendChild(mobileNav); wrapper.appendChild(backdrop); wrapper.appendChild(sheet); // 事件绑定 wrapper.querySelectorAll('.sidebar-btn').forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); handleNavClick(btn); }); }); wrapper.querySelectorAll('.mobile-nav-btn').forEach(btn => { if (btn.id === 'mobile-more-btn') { btn.addEventListener('click', (e) => { e.preventDefault(); showMore(); }); } else { btn.addEventListener('click', (e) => { e.preventDefault(); handleNavClick(btn); }); } }); wrapper.querySelectorAll('.sheet-item').forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); handleNavClick(btn); }); }); backdrop.addEventListener('click', hideMore); return wrapper; } // 新的 installNavigation 接口:接收 app 对象(为了兼容),但实际不注册组件 window.installNavigation = function(app, options = {}) { console.log('[Nav] installNavigation called (DOM mode)'); injectStyles(); // 延迟执行,确保DOM就绪 const init = () => { const container = document.getElementById('app'); if (!container) { console.warn('[Nav] #app 未找到,等待'); setTimeout(init, 100); return; } // 获取当前页面名称(从URL或body类名推断) let currentPage = 'dashboard'; const path = window.location.pathname; if (path.includes('topics')) currentPage = 'topics'; else if (path.includes('calendar')) currentPage = 'calendar'; else if (path.includes('metrics')) currentPage = 'metrics'; else if (path.includes('assets')) currentPage = 'assets'; else if (path.includes('tasks')) currentPage = 'tasks'; else if (path.includes('platforms')) currentPage = 'platforms'; else if (path.includes('logs')) currentPage = 'logs'; else if (path.includes('users')) currentPage = 'users'; else if (path.includes('admin')) currentPage = 'admin'; // 尝试从 Vue 实例获取 isAdmin let isAdmin = false; let redirectToPage = null; // 首先检查 localStorage,如果用户已登录且是管理员 const userRole = localStorage.getItem('userRole'); if (userRole === 'admin') { isAdmin = true; console.log('[Nav] 从 localStorage 获取到 admin 角色'); } // 尝试从 Vue 实例提取数据 const getVueData = () => { if (app && app._instance && app._instance.proxy) { return app._instance.proxy; } else if (window.Vue && window.Vue.app && window.Vue.app._instance && window.Vue.app._instance.proxy) { return window.Vue.app._instance.proxy; } return null; }; const updateNavFromVue = () => { const proxy = getVueData(); if (!proxy) return false; const newIsAdmin = proxy.isAdmin === true; if (proxy.isAdmin !== undefined) isAdmin = proxy.isAdmin; if (proxy.redirectToPage) redirectToPage = proxy.redirectToPage; // 如果侧边栏已存在,更新管理菜单显示状态 const sidebar = document.querySelector('.sidebar-nav'); if (sidebar) { const adminBtns = sidebar.querySelectorAll('.sidebar-btn[data-page="users.html"], .sidebar-btn[data-page="admin.html"]'); adminBtns.forEach(btn => { btn.style.display = isAdmin ? '' : 'none'; }); } return true; }; // 延迟获取 isAdmin,确保 Vue mounted 已执行 setTimeout(() => { updateNavFromVue(); console.log('[Nav] 延迟获取后 isAdmin:', isAdmin); // 如果侧边栏已存在,再次更新管理菜单显示状态 const sidebarEl = container.querySelector('.sidebar-nav'); if (sidebarEl) { const adminBtns = sidebarEl.querySelectorAll('.sidebar-btn[data-page="users.html"], .sidebar-btn[data-page="admin.html"]'); adminBtns.forEach(btn => { btn.style.display = isAdmin ? '' : 'none'; }); } }, 500); // 持续监听 Vue 数据变化 const stopWatch = setInterval(() => { const updated = updateNavFromVue(); if (updated && isAdmin) { clearInterval(stopWatch); console.log('[Nav] 已获取到 isAdmin:', isAdmin); } }, 200); // 5秒后停止监听 setTimeout(() => clearInterval(stopWatch), 5000); console.log('[Nav] currentPage:', currentPage, '初始 isAdmin:', isAdmin); // 默认跳转函数 const defaultNavigate = (page) => { const target = page === '/' ? '/index.html' : (page.startsWith('/') ? page : '/' + page); window.location.href = target; }; // 移除旧的导航容器(如果有) const oldNav = container.querySelector('.navigation-wrapper'); if (oldNav) oldNav.remove(); const nav = createNavigation(currentPage, isAdmin, redirectToPage || defaultNavigate); container.insertBefore(nav, container.firstChild); console.log('[Nav] 导航已插入'); }; // 延迟执行,确保 Vue 实例挂载完成 setTimeout(init, 100); return app; }; console.log('[Nav] 脚本已加载(纯DOM版)'); // 注册 Vue 组件 (备用方案) const createNavComponent = () => ({ props: { currentPage: { type: String, default: 'dashboard' }, isAdmin: { type: Boolean, default: false }, onNavigate: { type: Function, default: null } }, data() { return { menuItems: [ { key: 'dashboard', label: '系统概览', icon: '📊', page: '/' }, { key: 'topics', label: '选题管理', icon: '📋', page: 'topics.html' }, { key: 'metrics', label: '数据分析', icon: '📊', page: 'metrics.html' }, { key: 'calendar', label: '内容日历', icon: '📅', page: 'calendar.html' }, { key: 'assets', label: '素材库', icon: '🖼️', page: 'assets.html' }, { key: 'tasks', label: '创作任务', icon: '🚀', page: 'tasks.html' }, { key: 'platforms', label: '平台配置', icon: '🌐', page: 'platforms.html' }, { key: 'logs', label: '系统日志', icon: '📄', page: 'logs.html' } ], adminItems: [ { key: 'users', label: '用户管理', icon: '👥', page: 'users.html' }, { key: 'admin', label: '系统管理', icon: '⚙️', page: 'admin.html' } ], mobileItems: [ { key: 'dashboard', label: '首页', icon: '📊', page: '/' }, { key: 'topics', label: '选题', icon: '📋', page: 'topics.html' }, { key: 'tasks', label: '任务', icon: '🚀', page: 'tasks.html' }, { key: 'calendar', label: '日历', icon: '📅', page: 'calendar.html' }, { key: 'assets', label: '素材', icon: '🖼️', page: 'assets.html' } ], sheetItems: [ { key: 'metrics', label: '数据分析', icon: '📊', page: 'metrics.html' }, { key: 'platforms', label: '平台配置', icon: '🌐', page: 'platforms.html' }, { key: 'logs', label: '系统日志', icon: '📄', page: 'logs.html' }, { key: 'users', label: '用户管理', icon: '👥', page: 'users.html', admin: true }, { key: 'admin', label: '系统管理', icon: '⚙️', page: 'admin.html', admin: true } ], showMoreSheet: false }; }, template: ` `, methods: { navigate(page) { console.log('[Nav Vue] 点击导航:', page); if (this.onNavigate) { this.onNavigate(page); } else { // 修复路径处理 let target; if (page === '/' || page === '') { target = '/'; } else if (page.startsWith('/')) { target = page; } else { target = '/' + page; } window.location.href = target; } } }, mounted() { injectStyles(); } }); // 注册全局组件 window.NavigationComponent = createNavComponent(); // 自动注入 DOM 导航 (优先使用) if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { if (window.Vue && window.Vue.app) { window.Vue.app._instance.proxy.$nextTick(() => { const app = window.Vue.app; if (app && app._instance && app._instance.proxy) { const proxy = app._instance.proxy; const nav = createNavigation(proxy.currentPage || 'dashboard', proxy.isAdmin || false, proxy.redirectToPage || null); const container = document.getElementById('app'); if (container && !container.querySelector('.navigation-wrapper')) { container.insertBefore(nav, container.firstChild); } } }); } }, 500); }); } })();