ef168e3d90
- 组件改为render函数,避免模板编译器依赖 - 自动注入样式,无需页面额外CSS - 自适应布局:PC固定侧边栏,H5底部导航 - 所有页面统一使用,admin权限动态控制 - 自动调整main-content边距,防止遮挡
169 lines
6.3 KiB
JavaScript
169 lines
6.3 KiB
JavaScript
// 公共导航组件定义(使用 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);
|
||
});
|
||
}
|