// 公共导航组件定义(使用 render 函数,兼容 prod 版 Vue) const NavigationComponent = { name: 'NavigationComponent', props: { currentPage: { type: String, required: true, validator: (value) => ['dashboard', 'topics', 'logs', 'users', 'admin'].includes(value) }, isAdmin: { type: Boolean, default: false } }, render() { const h = this.$createElement; const navigate = (page) => this.$emit('navigate', page === 'dashboard' ? '/' : page + '.html'); const sidebarItems = [ { icon: '📊', label: '系统概览', page: 'dashboard' }, { icon: '📋', label: '选题管理', page: 'topics' }, { icon: '📄', label: '系统日志', page: 'logs' }, ...(this.isAdmin ? [ { icon: '👥', label: '用户管理', page: 'users' }, { icon: '⚙️', label: '系统管理', page: 'admin' } ] : []) ]; const mobileItems = [ { icon: '📊', label: '', page: 'dashboard' }, { icon: '📋', label: '选题', page: 'topics' }, { icon: '📄', label: '日志', page: 'logs' }, ...(this.isAdmin ? [ { icon: '👥', label: '用户', page: 'users' }, { icon: '⚙️', label: '系统', page: 'admin' } ] : []) ]; const createSidebarBtn = (item) => { const isActive = this.currentPage === item.page; return h('button', { class: isActive ? 'sidebar-btn active' : 'sidebar-btn', on: { click: () => navigate(item.page) } }, [item.icon, ' ', item.label].join('')); }; const createMobileBtn = (item) => { const isActive = this.currentPage === item.page; const content = item.label ? [item.icon, item.label].join('') : item.icon; return h('button', { class: isActive ? 'mobile-nav-btn active' : 'mobile-nav-btn', on: { click: () => navigate(item.page) } }, content); }; return h('div', { class: 'navigation-wrapper' }, [ h('aside', { class: 'sidebar' }, [ h('div', { class: 'sidebar-header' }, [h('h3', '宇之然平台')]), h('nav', { class: 'sidebar-nav' }, sidebarItems.map(createSidebarBtn)) ]), h('nav', { class: 'mobile-nav' }, mobileItems.map(createMobileBtn)) ]); } }; function injectNavigationStyles() { if (document.getElementById('navigation-styles')) return; const styles = ` .navigation-wrapper { position: relative; } .navigation-wrapper .sidebar { width: 180px; background: white; padding: 12px; box-shadow: 2px 0 8px rgba(0,0,0,0.05); position: fixed; left: 0; top: 60px; bottom: 0; overflow-y: auto; z-index: 9999; } .navigation-wrapper .sidebar-header { padding: 12px 8px 16px; border-bottom: 1px solid #ebeef5; margin-bottom: 12px; } .navigation-wrapper .sidebar-header h3 { margin: 0; font-size: 16px; font-weight: 600; color: #303133; } .navigation-wrapper .sidebar-btn { width: 100%; text-align: left; padding: 10px 12px; border: none; background: transparent; border-radius: 8px; margin-bottom: 6px; cursor: pointer; transition: all 0.3s; color: #606266; font-size: 14px; display: flex; align-items: center; gap: 6px; } .navigation-wrapper .sidebar-btn:hover { background: #f5f7fa; color: #409eff; } .navigation-wrapper .sidebar-btn.active { background: #ecf5ff; color: #409eff; font-weight: 600; } .navigation-wrapper .mobile-nav { display: none; position: fixed; bottom: 0; left: 0; right: 0; background: white; box-shadow: 0 -2px 8px rgba(0,0,0,0.1); padding: 8px 0; z-index: 1000; justify-content: space-around; } .navigation-wrapper .mobile-nav-btn { flex: 1; border: none; background: transparent; padding: 10px 4px; text-align: center; font-size: 11px; color: #606266; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 2px; } .navigation-wrapper .mobile-nav-btn:hover { color: #409eff; } .navigation-wrapper .mobile-nav-btn.active { color: #409eff; font-weight: 600; } @media (max-width: 768px) { .navigation-wrapper .sidebar { display: none !important; } .navigation-wrapper .mobile-nav { display: flex !important; } } undefined body > #app > .navigation-wrapper + .main-content { margin-left: 180px !important; } @media (max-width: 768px) { body > #app > .navigation-wrapper + .main-content { margin-left: 0 !important; padding-bottom: 60px !important; } } `; const styleEl = document.createElement('style'); styleEl.id = 'navigation-styles'; styleEl.textContent = styles; document.head.appendChild(styleEl); } function installNavigation(app) { injectNavigationStyles(); app.component('navigation-component', NavigationComponent); app.config.globalProperties.$redirectToPage = (path) => { window.location.href = path; }; return app; } if (typeof window !== 'undefined') { window.NavigationComponent = NavigationComponent; window.installNavigation = installNavigation; } if (typeof module !== 'undefined' && module.exports) { module.exports = { NavigationComponent, installNavigation }; } // 自动调整 main-content 边距 (function adjustLayout() { // 等待DOM加载 const check = () => { const main = document.querySelector('.main-content'); if (main) { // 检测是否有导航组件 const hasSidebar = document.querySelector('.navigation-wrapper .sidebar') !== null; if (hasSidebar) { main.style.marginLeft = '180px'; } else { main.style.marginLeft = '0'; } } // 移动端调整 const mobileNav = document.querySelector('.navigation-wrapper .mobile-nav'); if (mobileNav) { main.style.paddingBottom = '60px'; } }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', check); } else { check(); } // Vue 挂载后再次调整(通过全局API) if (window.Vue) { window.Vue.mixin({ mounted() { setTimeout(check, 0); } }); } })(); // 运行时检查(仅开发环境) if (typeof window !== 'undefined') { window.addEventListener('load', () => { setTimeout(() => { const nav = document.querySelector('.navigation-wrapper'); if (!nav) { console.warn('[NavigationComponent] 未找到 .navigation-wrapper 元素,可能未正确渲染'); } else { console.log('[NavigationComponent] 已渲染'); } }, 100); }); }