后端变更: - 移除 tasks_manager.py 和 core/auth.py,简化架构 - 新增 core/scheduler.py 验证调度器,替代原有任务管理 - 大幅优化 api_server.py:统一错误处理、增强参数验证、支持调度器控制 - validator.py 增强 SOCKS4/SOCKS5 代理验证支持 - config.py 清理废弃配置(WebSocket、API Key、认证开关) - SQLite 数据库操作性能优化 前端变更: - 移除任务管理页面 (CrawlerTasks) 和 WebSocket 相关代码 - 路由简化为 4 个核心页面:总览、代理列表、插件管理、设置 - 提取前端工具函数(clipboard、confirm、format)和 API 类型定义 - 优化 CSS 架构:完善 variables、utilities、element-plus 样式 - Dashboard、Plugins、ProxyList、Settings 页面 UI/UX 优化 - App.vue 响应式侧边栏和页面过渡动画优化 其他: - 移除 PowerShell 启动脚本,简化 Windows 批处理脚本 - 新增 README_SOCKS.md SOCKS 代理支持文档 - .env.example 和 .gitignore 更新
257 lines
4.5 KiB
Vue
257 lines
4.5 KiB
Vue
<template>
|
|
<div class="app-container">
|
|
<aside class="sidebar">
|
|
<div class="logo-section">
|
|
<el-icon class="logo" :size="40"><Grid /></el-icon>
|
|
<h1 class="logo-text">代理池</h1>
|
|
</div>
|
|
|
|
<nav class="menu-nav">
|
|
<router-link
|
|
v-for="item in menuItems"
|
|
:key="item.index"
|
|
:to="item.index"
|
|
:class="['menu-item', { active: isActive(item.index) }]"
|
|
>
|
|
<el-icon class="menu-icon" :size="18">
|
|
<component :is="item.icon" />
|
|
</el-icon>
|
|
<span class="menu-label">{{ item.label }}</span>
|
|
</router-link>
|
|
</nav>
|
|
</aside>
|
|
|
|
<main class="main-content">
|
|
<router-view v-slot="{ Component }">
|
|
<transition name="fade" mode="out-in">
|
|
<component :is="Component" />
|
|
</transition>
|
|
</router-view>
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import {
|
|
House,
|
|
Document,
|
|
Connection,
|
|
Setting,
|
|
Grid
|
|
} from '@element-plus/icons-vue'
|
|
|
|
const route = useRoute()
|
|
|
|
const menuItems = [
|
|
{ index: '/dashboard', icon: House, label: '总览' },
|
|
{ index: '/proxies', icon: Document, label: '代理列表' },
|
|
{ index: '/plugins', icon: Connection, label: '插件管理' },
|
|
{ index: '/settings', icon: Setting, label: '设置' }
|
|
]
|
|
|
|
const isActive = (path) => route.path === path || route.path.startsWith(path + '/')
|
|
</script>
|
|
|
|
<style scoped>
|
|
.app-container {
|
|
display: flex;
|
|
width: 100%;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
background: var(--bg);
|
|
}
|
|
|
|
/* 侧边栏 - 冷灰紫风格 */
|
|
.sidebar {
|
|
width: 220px;
|
|
height: 100%;
|
|
flex-shrink: 0;
|
|
background: var(--surface);
|
|
border-right: 1px solid var(--border);
|
|
z-index: 100;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.logo-section {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 24px 20px;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.logo {
|
|
color: var(--primary);
|
|
margin-right: 12px;
|
|
filter: drop-shadow(0 0 8px rgba(146, 124, 255, 0.4));
|
|
}
|
|
|
|
.logo-text {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
letter-spacing: 1px;
|
|
margin: 0;
|
|
}
|
|
|
|
.menu-nav {
|
|
flex: 1;
|
|
padding: 16px 12px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.menu-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 12px 16px;
|
|
margin: 4px 0;
|
|
border-radius: var(--radius-md);
|
|
color: var(--text-secondary);
|
|
font-weight: 500;
|
|
text-decoration: none;
|
|
transition: var(--transition-base);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 悬停状态 */
|
|
.menu-item:hover {
|
|
background: var(--surface-2);
|
|
color: var(--primary);
|
|
}
|
|
|
|
/* 激活状态 - 紫底 + 左条 */
|
|
.menu-item.active {
|
|
background: var(--primary-soft);
|
|
color: var(--primary);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.menu-item.active::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 3px;
|
|
height: 20px;
|
|
background: var(--primary);
|
|
border-radius: 0 2px 2px 0;
|
|
}
|
|
|
|
.menu-icon {
|
|
margin-right: 12px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.menu-label {
|
|
white-space: nowrap;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* 主内容区 */
|
|
.main-content {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
background: var(--bg);
|
|
min-width: 0;
|
|
}
|
|
|
|
/* 页面过渡动画 */
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
}
|
|
|
|
.fade-enter-from {
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
}
|
|
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
transform: translateY(-10px);
|
|
}
|
|
|
|
/* 响应式 - 平板 */
|
|
@media (max-width: 768px) {
|
|
.sidebar {
|
|
width: 64px;
|
|
}
|
|
|
|
.logo-text,
|
|
.menu-label {
|
|
display: none;
|
|
}
|
|
|
|
.logo-section {
|
|
justify-content: center;
|
|
padding: 20px 0;
|
|
}
|
|
|
|
.logo {
|
|
margin-right: 0;
|
|
}
|
|
|
|
.menu-item {
|
|
justify-content: center;
|
|
padding: 12px;
|
|
margin: 4px 8px;
|
|
}
|
|
|
|
.menu-icon {
|
|
margin-right: 0;
|
|
}
|
|
}
|
|
|
|
/* 响应式 - 手机 */
|
|
@media (max-width: 480px) {
|
|
.sidebar {
|
|
width: 100%;
|
|
height: auto;
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
z-index: 1000;
|
|
border-right: none;
|
|
border-top: 1px solid var(--border);
|
|
flex-direction: row;
|
|
padding: 0;
|
|
}
|
|
|
|
.logo-section {
|
|
display: none;
|
|
}
|
|
|
|
.menu-nav {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
padding: 8px 0;
|
|
width: 100%;
|
|
flex-direction: row;
|
|
}
|
|
|
|
.menu-item {
|
|
flex-direction: column;
|
|
margin: 0;
|
|
padding: 8px 12px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.menu-item::before {
|
|
display: none;
|
|
}
|
|
|
|
.menu-icon {
|
|
margin-right: 0;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.main-content {
|
|
padding-bottom: 70px;
|
|
}
|
|
}
|
|
</style>
|