fix(navigation): 使用render函数替代template,兼容prod版Vue
- 重写组件为render函数实现,避免编译器依赖 - 优化按钮生成逻辑,使用JS数组map - 修复样式注入时机和CSS冲突问题 - 确保app实例化后调用installNavigation进行注册
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
// 公共导航组件定义
|
// 公共导航组件定义(使用 render 函数,兼容 prod 版 Vue)
|
||||||
const NavigationComponent = {
|
const NavigationComponent = {
|
||||||
|
name: 'NavigationComponent',
|
||||||
props: {
|
props: {
|
||||||
currentPage: {
|
currentPage: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -11,206 +12,110 @@ const NavigationComponent = {
|
|||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `
|
render() {
|
||||||
<div class="navigation-wrapper">
|
const h = this.$createElement;
|
||||||
<!-- PC 左边栏 -->
|
const navigate = (page) => this.$emit('navigate', page === 'dashboard' ? '/' : page + '.html');
|
||||||
<aside class="sidebar">
|
|
||||||
<div class="sidebar-header">
|
|
||||||
<h3>宇之然平台</h3>
|
|
||||||
</div>
|
|
||||||
<nav class="sidebar-nav">
|
|
||||||
<button
|
|
||||||
class="sidebar-btn"
|
|
||||||
:class="{ active: currentPage === 'dashboard' }"
|
|
||||||
@click="$emit('navigate', '/')"
|
|
||||||
>📊 系统概览</button>
|
|
||||||
<button
|
|
||||||
class="sidebar-btn"
|
|
||||||
:class="{ active: currentPage === 'topics' }"
|
|
||||||
@click="$emit('navigate', 'topics.html')"
|
|
||||||
>📋 选题管理</button>
|
|
||||||
<button
|
|
||||||
class="sidebar-btn"
|
|
||||||
:class="{ active: currentPage === 'logs' }"
|
|
||||||
@click="$emit('navigate', 'logs.html')"
|
|
||||||
>📄 系统日志</button>
|
|
||||||
<button
|
|
||||||
v-if="isAdmin"
|
|
||||||
class="sidebar-btn"
|
|
||||||
:class="{ active: currentPage === 'users' }"
|
|
||||||
@click="$emit('navigate', 'users.html')"
|
|
||||||
>👥 用户管理</button>
|
|
||||||
<button
|
|
||||||
v-if="isAdmin"
|
|
||||||
class="sidebar-btn"
|
|
||||||
:class="{ active: currentPage === 'admin' }"
|
|
||||||
@click="$emit('navigate', 'admin.html')"
|
|
||||||
>⚙️ 系统管理</button>
|
|
||||||
</nav>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<!-- H5 底部导航 -->
|
const sidebarItems = [
|
||||||
<nav class="mobile-nav">
|
{ icon: '📊', label: '系统概览', page: 'dashboard' },
|
||||||
<button
|
{ icon: '📋', label: '选题管理', page: 'topics' },
|
||||||
class="mobile-nav-btn"
|
{ icon: '📄', label: '系统日志', page: 'logs' },
|
||||||
:class="{ active: currentPage === 'dashboard' }"
|
...(this.isAdmin ? [
|
||||||
@click="$emit('navigate', '/')"
|
{ icon: '👥', label: '用户管理', page: 'users' },
|
||||||
>📊 概览</button>
|
{ icon: '⚙️', label: '系统管理', page: 'admin' }
|
||||||
<button
|
] : [])
|
||||||
class="mobile-nav-btn"
|
];
|
||||||
:class="{ active: currentPage === 'topics' }"
|
|
||||||
@click="$emit('navigate', 'topics.html')"
|
const mobileItems = [
|
||||||
>📋 选题</button>
|
{ icon: '📊', label: '', page: 'dashboard' },
|
||||||
<button
|
{ icon: '📋', label: '选题', page: 'topics' },
|
||||||
class="mobile-nav-btn"
|
{ icon: '📄', label: '日志', page: 'logs' },
|
||||||
:class="{ active: currentPage === 'logs' }"
|
...(this.isAdmin ? [
|
||||||
@click="$emit('navigate', 'logs.html')"
|
{ icon: '👥', label: '用户', page: 'users' },
|
||||||
>📄 日志</button>
|
{ icon: '⚙️', label: '系统', page: 'admin' }
|
||||||
<button
|
] : [])
|
||||||
v-if="isAdmin"
|
];
|
||||||
class="mobile-nav-btn"
|
|
||||||
:class="{ active: currentPage === 'users' }"
|
const createSidebarBtn = (item) => {
|
||||||
@click="$emit('navigate', 'users.html')"
|
const isActive = this.currentPage === item.page;
|
||||||
>👥 用户</button>
|
return h('button', {
|
||||||
<button
|
class: isActive ? 'sidebar-btn active' : 'sidebar-btn',
|
||||||
v-if="isAdmin"
|
on: { click: () => navigate(item.page) }
|
||||||
class="mobile-nav-btn"
|
}, [item.icon, ' ', item.label].join(''));
|
||||||
:class="{ active: currentPage === 'admin' }"
|
};
|
||||||
@click="$emit('navigate', 'admin.html')"
|
|
||||||
>⚙️ 系统</button>
|
const createMobileBtn = (item) => {
|
||||||
</nav>
|
const isActive = this.currentPage === item.page;
|
||||||
</div>
|
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() {
|
function injectNavigationStyles() {
|
||||||
if (document.getElementById('navigation-styles')) return;
|
if (document.getElementById('navigation-styles')) return;
|
||||||
|
|
||||||
const styles = `
|
const styles = `
|
||||||
/* 导航组件样式 */
|
.navigation-wrapper { position: relative; }
|
||||||
.navigation-wrapper .sidebar {
|
.navigation-wrapper .sidebar {
|
||||||
width: 180px;
|
width: 180px; background: white; padding: 12px; box-shadow: 2px 0 8px rgba(0,0,0,0.05);
|
||||||
background: white;
|
position: fixed; left: 0; top: 60px; bottom: 0; overflow-y: auto; z-index: 100;
|
||||||
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: 100;
|
|
||||||
}
|
|
||||||
.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-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 {
|
.navigation-wrapper .sidebar-btn {
|
||||||
width: 100%;
|
width: 100%; text-align: left; padding: 10px 12px; border: none; background: transparent; border-radius: 8px;
|
||||||
text-align: left;
|
margin-bottom: 6px; cursor: pointer; transition: all 0.3s; color: #606266; font-size: 14px; display: flex; align-items: center; gap: 6px;
|
||||||
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 .sidebar-btn:hover { background: #f5f7fa; color: #409eff; }
|
||||||
|
.navigation-wrapper .sidebar-btn.active { background: #ecf5ff; color: #409eff; font-weight: 600; }
|
||||||
.navigation-wrapper .mobile-nav {
|
.navigation-wrapper .mobile-nav {
|
||||||
display: none;
|
display: none; position: fixed; bottom: 0; left: 0; right: 0; background: white;
|
||||||
position: fixed;
|
box-shadow: 0 -2px 8px rgba(0,0,0,0.1); padding: 8px 0; z-index: 1000; justify-content: space-around;
|
||||||
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 {
|
.navigation-wrapper .mobile-nav-btn {
|
||||||
flex: 1;
|
flex: 1; border: none; background: transparent; padding: 10px 4px; text-align: center;
|
||||||
border: none;
|
font-size: 11px; color: #606266; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 2px;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
.navigation-wrapper .mobile-nav-btn:hover { color: #409eff; }
|
||||||
|
.navigation-wrapper .mobile-nav-btn.active { color: #409eff; font-weight: 600; }
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.navigation-wrapper .sidebar {
|
.navigation-wrapper .sidebar { display: none !important; }
|
||||||
display: none !important;
|
.navigation-wrapper .mobile-nav { display: flex !important; }
|
||||||
}
|
|
||||||
.navigation-wrapper .mobile-nav {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@media (min-width: 769px) {
|
@media (min-width: 769px) { .navigation-wrapper .mobile-nav { display: none !important; } }
|
||||||
.navigation-wrapper .mobile-nav {
|
body > #app > .navigation-wrapper + .main-content { margin-left: 180px; }
|
||||||
display: none !important;
|
@media (max-width: 768px) {
|
||||||
}
|
body > #app > .navigation-wrapper + .main-content { margin-left: 0 !important; padding-bottom: 60px !important; }
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const styleEl = document.createElement('style');
|
const styleEl = document.createElement('style');
|
||||||
styleEl.id = 'navigation-styles';
|
styleEl.id = 'navigation-styles';
|
||||||
styleEl.textContent = styles;
|
styleEl.textContent = styles;
|
||||||
document.head.appendChild(styleEl);
|
document.head.appendChild(styleEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安装组件到Vue应用
|
function installNavigation(app) {
|
||||||
function installNavigation(app, options = {}) {
|
|
||||||
injectNavigationStyles();
|
injectNavigationStyles();
|
||||||
|
|
||||||
app.component('navigation-component', NavigationComponent);
|
app.component('navigation-component', NavigationComponent);
|
||||||
|
app.config.globalProperties.$redirectToPage = (path) => { window.location.href = path; };
|
||||||
// 添加全局redirectToPage方法(兼容旧代码)
|
|
||||||
app.config.globalProperties.$redirectToPage = (path) => {
|
|
||||||
window.location.href = path;
|
|
||||||
};
|
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出到全局(兼容直接使用)
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.NavigationComponent = NavigationComponent;
|
window.NavigationComponent = NavigationComponent;
|
||||||
window.installNavigation = installNavigation;
|
window.installNavigation = installNavigation;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof module !== 'undefined' && module.exports) {
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
module.exports = { NavigationComponent, installNavigation };
|
module.exports = { NavigationComponent, installNavigation };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user