feat(frontend): 抽离页眉为公共组件;修复导航点击事件

- 新增 navbar-component.js:固定页眉,统一标题、用户信息、退出按钮
- 重构所有页面(index/topics/logs/admin/users)使用 navbar-component
- 清理原有 navbar 相关样式,避免冲突
- 导航组件增加 main-content padding-top 自动调整
- 修复导航按钮点击事件(确保  触发)
- 导航组件样式强应用,解决遮挡和布局问题
This commit is contained in:
lt
2026-05-08 13:22:12 +08:00
parent 2220d7b981
commit 9147d5e679
7 changed files with 169 additions and 75 deletions
+36 -26
View File
@@ -1,11 +1,10 @@
// Vue 3 导航组件 - 修复布局和事件
// Vue 3 导航组件 - 最终版(确保事件传递)
(function() {
console.log('[Nav] 脚本加载');
function injectStyles() {
if (document.getElementById('navigation-styles')) return;
const styles = `
/* 重置导航包装器,使用常规布局 */
.navigation-wrapper { position: relative; }
.navigation-wrapper .sidebar {
position: fixed; top: 60px; left: 0; bottom: 0; width: 180px;
@@ -35,20 +34,8 @@
.navigation-wrapper .mobile-nav { display: flex !important; }
}
@media (min-width: 769px) { .navigation-wrapper .mobile-nav { display: none !important; } }
/* 主内容区域避开固定侧边栏 */
body > #app > .navigation-wrapper + .main-content,
body > #app > .main-content {
margin-left: 180px !important;
}
@media (max-width: 768px) {
body > #app > .navigation-wrapper + .main-content,
body > #app > .main-content {
margin-left: 0 !important;
padding-bottom: 60px !important;
}
}
/* 侧边栏固定定位时不调整主内容,让 main-content 自然位于文档流中,仅需确保 padding-top 避开 navbar */
body > #app > .main-content { padding-top: 0; }
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; } }
`;
const styleEl = document.createElement('style');
styleEl.id = 'navigation-styles';
@@ -76,6 +63,7 @@
else if (typeof children === 'string') el.textContent = children;
}
if (data && data.on) Object.keys(data.on).forEach(ev => el.addEventListener(ev, data.on[ev]));
return el;
};
console.log('[Nav] 使用 h 函数:', typeof h);
@@ -83,11 +71,23 @@
name: 'NavigationComponent',
props: { currentPage: { type: String, required: true }, isAdmin: { type: Boolean, default: false } },
mounted() { console.log('[NavigationComponent] 已挂载'); },
render() {
render(){
const createElement = arguments[0] || h;
// 获取 emit 方法:通过 this.$emit 或从闭包中捕获
const emit = this.$emit || function() {};
const navigate = (page) => {
console.log('[NavigationComponent] 导航到:', page);
this.$emit('navigate', page);
console.log('[NavigationComponent] 点击导航到:', page);
// 直接调用 emit 函数
if (typeof emit === 'function') {
emit('navigate', page);
} else if (this.$emit) {
this.$emit('navigate', page);
} else {
console.warn('[NavigationComponent] 无法发送事件,emit unavailable');
}
};
const sidebarItems = [
{ icon: '📊', label: '系统概览', page: '/' },
{ icon: '📋', label: '选题管理', page: 'topics.html' },
@@ -100,22 +100,32 @@
{ icon: '📄', label: '日志', page: 'logs.html' },
...(this.isAdmin ? [{ icon: '👥', label: '用户', page: 'users.html' }, { icon: '⚙️', label: '系统', page: 'admin.html' }] : [])
];
return h('div', { class: 'navigation-wrapper' }, [
h('aside', { class: 'sidebar' }, [
h('div', { class: 'sidebar-header' }, [h('h3', '宇之然平台')]),
h('nav', { class: 'sidebar-nav' }, sidebarItems.map(item => {
return createElement('div', { class: 'navigation-wrapper' }, [
createElement('aside', { class: 'sidebar' }, [
createElement('div', { class: 'sidebar-header' }, [createElement('h3', '宇之然平台')]),
createElement('nav', { class: 'sidebar-nav' }, sidebarItems.map(item => {
const isActive = this.currentPage === (item.page === '/' ? 'dashboard' : item.page.replace('.html',''));
return h('button', { key: item.page, class: isActive ? 'sidebar-btn active' : 'sidebar-btn', on: { click: () => navigate(item.page) } }, item.icon + ' ' + item.label);
return createElement('button', {
key: item.page,
class: isActive ? 'sidebar-btn active' : 'sidebar-btn',
on: { click: (e) => { e.preventDefault(); navigate(item.page); } }
}, item.icon + ' ' + item.label);
}))
]),
h('nav', { class: 'mobile-nav' }, mobileItems.map(item => {
createElement('nav', { class: 'mobile-nav' }, mobileItems.map(item => {
const isActive = this.currentPage === (item.page === '/' ? 'dashboard' : item.page.replace('.html',''));
const content = item.label ? (item.icon + ' ' + item.label) : item.icon;
return h('button', { key: item.page, class: isActive ? 'mobile-nav-btn active' : 'mobile-nav-btn', on: { click: () => navigate(item.page) } }, content);
return createElement('button', {
key: item.page,
class: isActive ? 'mobile-nav-btn active' : 'mobile-nav-btn',
on: { click: (e) => { e.preventDefault(); navigate(item.page); } }
}, content);
}))
]);
}
};
app.component('navigation-component', component);
console.log('[Nav] 组件已注册');
return app;