chore: 清理 Docker 相关文件并优化前端布局
- 删除 Docker 相关文件 (docker-compose, Dockerfile, nginx.conf, init.sql 等) - 优化 platforms.html 卡片布局和响应式样式 - 优化 users.html 格式和移动端卡片设计 - 优化 admin.html 页面结构和表格布局 - 修复各页面 min-height 和溢出问题 - 更新导航组件样式
This commit is contained in:
@@ -5,17 +5,20 @@
|
||||
function injectStyles() {
|
||||
if (document.getElementById('navigation-styles')) return;
|
||||
const styles = `
|
||||
.navigation-wrapper .sidebar { position: fixed; top: 60px; left: 0; bottom: 0; width: 180px; background: #fff; padding: 12px; box-shadow: 2px 0 8px rgba(0,0,0,0.1); overflow-y: auto; z-index: 99999; border-right: 1px solid #ebeef5; }
|
||||
.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 !important; }
|
||||
.navigation-wrapper .mobile-nav { display: none; position: fixed; bottom: 0; left: 0; right: 0; background: #1890ff; box-shadow: 0 -2px 8px rgba(0,0,0,0.2); padding: 8px 0; z-index: 99999; justify-content: space-around; }
|
||||
.navigation-wrapper .mobile-nav-btn { flex: 1; border: none; background: transparent; padding: 10px 4px; text-align: center; font-size: 12px; color: white !important; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 2px; }
|
||||
.navigation-wrapper .mobile-nav-btn:hover { background: rgba(255,255,255,0.2); }
|
||||
.navigation-wrapper .mobile-nav-btn.active { background: rgba(255,255,255,0.3); font-weight: 600 !important; }
|
||||
@media (max-width: 768px) { .navigation-wrapper .sidebar { display: none !important; } .navigation-wrapper .mobile-nav { display: flex !important; } }
|
||||
.nav-wrapper, .navigation-wrapper { position: fixed; top: 60px; left: 0; bottom: 0; z-index: 9999; }
|
||||
.nav-wrapper .sidebar, .navigation-wrapper .sidebar { position: fixed; top: 60px; left: 0; bottom: 0; width: 180px; background: #fff; padding: 12px; box-shadow: 2px 0 8px rgba(0,0,0,0.1); overflow-y: auto; z-index: 99999; border-right: 1px solid #ebeef5; }
|
||||
.nav-wrapper .sidebar-header, .navigation-wrapper .sidebar-header { padding: 12px 8px 16px; border-bottom: 1px solid #ebeef5; margin-bottom: 12px; }
|
||||
.nav-wrapper .sidebar-header h3, .navigation-wrapper .sidebar-header h3 { margin: 0; font-size: 16px; font-weight: 600; color: #303133; }
|
||||
.nav-wrapper .sidebar-btn, .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; }
|
||||
.nav-wrapper .sidebar-btn:hover, .navigation-wrapper .sidebar-btn:hover { background: #f5f7fa; color: #409eff; }
|
||||
.nav-wrapper .sidebar-btn.active, .navigation-wrapper .sidebar-btn.active { background: #ecf5ff; color: #409eff; font-weight: 600 !important; }
|
||||
.nav-wrapper .mobile-nav, .navigation-wrapper .mobile-nav { display: none; position: fixed; bottom: 0; left: 0; right: 0; background: #1890ff; box-shadow: 0 -2px 8px rgba(0,0,0,0.2); padding: 8px 0; z-index: 99999; justify-content: space-around; }
|
||||
.nav-wrapper .mobile-nav-btn, .navigation-wrapper .mobile-nav-btn { flex: 1; border: none; background: transparent; padding: 8px 4px; text-align: center; font-size: 11px; color: white !important; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 2px; }
|
||||
.nav-wrapper .mobile-nav-btn .nav-icon, .navigation-wrapper .mobile-nav-btn .nav-icon { font-size: 18px; line-height: 1; }
|
||||
.nav-wrapper .mobile-nav-btn .nav-text, .navigation-wrapper .mobile-nav-btn .nav-text { font-size: 10px; margin-top: 2px; }
|
||||
.nav-wrapper .mobile-nav-btn:hover, .navigation-wrapper .mobile-nav-btn:hover { background: rgba(255,255,255,0.2); }
|
||||
.nav-wrapper .mobile-nav-btn.active, .navigation-wrapper .mobile-nav-btn.active { background: rgba(255,255,255,0.3); font-weight: 600 !important; }
|
||||
@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 { margin-left: 180px !important; padding-top: 60px !important; }
|
||||
@media (max-width: 768px) { body > #app > .main-content { margin-left: 0 !important; padding-bottom: 60px !important; } }
|
||||
`;
|
||||
@@ -50,11 +53,11 @@
|
||||
const mobileNav = document.createElement('nav');
|
||||
mobileNav.className = 'mobile-nav';
|
||||
mobileNav.innerHTML = `
|
||||
<button class="mobile-nav-btn ${currentPage==='dashboard'?'active':''}" data-page="/">📊</button>
|
||||
<button class="mobile-nav-btn ${currentPage==='topics'?'active':''}" data-page="topics.html">📋</button>
|
||||
<button class="mobile-nav-btn ${currentPage==='metrics'?'active':''}" data-page="metrics.html">📊</button>
|
||||
<button class="mobile-nav-btn ${currentPage==='assets'?'active':''}" data-page="assets.html">🖼️</button>
|
||||
<button class="mobile-nav-btn ${currentPage==='tasks'?'active':''}" data-page="tasks.html">🚀</button>
|
||||
<button class="mobile-nav-btn ${currentPage==='dashboard'?'active':''}" data-page="/"><span class="nav-icon">📊</span><span class="nav-text">首页</span></button>
|
||||
<button class="mobile-nav-btn ${currentPage==='topics'?'active':''}" data-page="topics.html"><span class="nav-icon">📋</span><span class="nav-text">选题</span></button>
|
||||
<button class="mobile-nav-btn ${currentPage==='calendar'?'active':''}" data-page="calendar.html"><span class="nav-icon">📅</span><span class="nav-text">日历</span></button>
|
||||
<button class="mobile-nav-btn ${currentPage==='assets'?'active':''}" data-page="assets.html"><span class="nav-icon">🖼️</span><span class="nav-text">素材</span></button>
|
||||
<button class="mobile-nav-btn ${currentPage==='tasks'?'active':''}" data-page="tasks.html"><span class="nav-icon">🚀</span><span class="nav-text">任务</span></button>
|
||||
`;
|
||||
|
||||
wrapper.appendChild(sidebar);
|
||||
@@ -62,10 +65,24 @@
|
||||
|
||||
// 绑定点击事件
|
||||
wrapper.querySelectorAll('button').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const page = btn.getAttribute('data-page');
|
||||
console.log('[Nav] 导航到:', page);
|
||||
if (onNavigate) onNavigate(page);
|
||||
console.log('[Nav] 点击导航:', page);
|
||||
if (onNavigate) {
|
||||
onNavigate(page);
|
||||
} else {
|
||||
// 直接跳转 - 修复路径处理
|
||||
let target;
|
||||
if (page === '/' || page === '') {
|
||||
target = '/';
|
||||
} else if (page.startsWith('/')) {
|
||||
target = page;
|
||||
} else {
|
||||
target = '/' + page;
|
||||
}
|
||||
window.location.href = target;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,36 +103,210 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 尝试从 Vue 实例获取 isAdmin 和 currentPage
|
||||
let isAdmin = false;
|
||||
// 获取当前页面名称(从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;
|
||||
|
||||
// 尝试从 Vue 实例提取数据
|
||||
if (app && app._instance && app._instance.proxy) {
|
||||
const proxy = app._instance.proxy;
|
||||
if (proxy.isAdmin !== undefined) isAdmin = proxy.isAdmin;
|
||||
if (proxy.currentPage !== undefined) currentPage = proxy.currentPage;
|
||||
if (proxy.redirectToPage) redirectToPage = proxy.redirectToPage;
|
||||
} else if (window.Vue && Vue.app && Vue.app._instance && Vue.app._instance.proxy) {
|
||||
const proxy = Vue.app._instance.proxy;
|
||||
if (proxy.isAdmin !== undefined) isAdmin = proxy.isAdmin;
|
||||
if (proxy.currentPage !== undefined) currentPage = proxy.currentPage;
|
||||
if (proxy.redirectToPage) redirectToPage = proxy.redirectToPage;
|
||||
// 首先检查 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 || ((page) => { window.location.href = page; }));
|
||||
const nav = createNavigation(currentPage, isAdmin, redirectToPage || defaultNavigate);
|
||||
container.insertBefore(nav, container.firstChild);
|
||||
console.log('[Nav] 导航已插入');
|
||||
};
|
||||
|
||||
init();
|
||||
// 延迟执行,确保 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: 'calendar', label: '日历', icon: '📅', page: 'calendar.html' },
|
||||
{ key: 'assets', label: '素材', icon: '🖼️', page: 'assets.html' },
|
||||
{ key: 'tasks', label: '任务', icon: '🚀', page: 'tasks.html' }
|
||||
]
|
||||
};
|
||||
},
|
||||
template: `
|
||||
<div class="nav-wrapper">
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-header"><h3>宇之然平台</h3></div>
|
||||
<nav class="sidebar-nav">
|
||||
<button v-for="item in menuItems" :key="item.key" :class="['sidebar-btn', { active: currentPage === item.key }]" @click="navigate(item.page)">
|
||||
{{ item.icon }} {{ item.label }}
|
||||
</button>
|
||||
<template v-if="isAdmin">
|
||||
<button v-for="item in adminItems" :key="item.key" :class="['sidebar-btn', { active: currentPage === item.key }]" @click="navigate(item.page)">
|
||||
{{ item.icon }} {{ item.label }}
|
||||
</button>
|
||||
</template>
|
||||
</nav>
|
||||
</aside>
|
||||
<nav class="mobile-nav">
|
||||
<button v-for="item in mobileItems" :key="item.key" :class="['mobile-nav-btn', { active: currentPage === item.key }]" @click="navigate(item.page)">
|
||||
<span class="nav-icon">{{ item.icon }}</span>
|
||||
<span class="nav-text">{{ item.label }}</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
`,
|
||||
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);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user