重构代理池系统:简化架构并增强核心功能
后端变更: - 移除 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 更新
This commit is contained in:
@@ -1,175 +1,256 @@
|
||||
<script setup>
|
||||
import { RouterView, useRoute } from 'vue-router'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const route = useRoute()
|
||||
const activeMenu = computed(() => route.path)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
class="side-menu"
|
||||
router
|
||||
>
|
||||
<aside class="sidebar">
|
||||
<div class="logo-section">
|
||||
<div class="logo">🌸</div>
|
||||
<div class="logo-text">代理池</div>
|
||||
<el-icon class="logo" :size="40"><Grid /></el-icon>
|
||||
<h1 class="logo-text">代理池</h1>
|
||||
</div>
|
||||
|
||||
<el-menu-item index="/dashboard">
|
||||
<template #title>
|
||||
<span class="menu-icon">🏠</span>
|
||||
<span>总览</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/proxies">
|
||||
<template #title>
|
||||
<span class="menu-icon">📋</span>
|
||||
<span>代理列表</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/crawler">
|
||||
<template #title>
|
||||
<span class="menu-icon">🎀</span>
|
||||
<span>任务管理</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/plugins">
|
||||
<template #title>
|
||||
<span class="menu-icon">🔌</span>
|
||||
<span>插件管理</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/settings">
|
||||
<template #title>
|
||||
<span class="menu-icon">⚙️</span>
|
||||
<span>设置</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<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>
|
||||
|
||||
<div class="main-content">
|
||||
<RouterView />
|
||||
</div>
|
||||
<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);
|
||||
}
|
||||
|
||||
.side-menu {
|
||||
width: 240px;
|
||||
/* 侧边栏 - 冷灰紫风格 */
|
||||
.sidebar {
|
||||
width: 220px;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
background: var(--surface);
|
||||
border-right: 1px solid var(--border);
|
||||
box-shadow: 4px 0 20px rgba(255, 107, 157, 0.1);
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
backdrop-filter: blur(10px);
|
||||
z-index: 10;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 35px 0;
|
||||
padding: 24px 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.logo-section::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 80%;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, var(--primary), transparent);
|
||||
animation: shimmer 3s infinite;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 52px;
|
||||
margin-bottom: 10px;
|
||||
animation: float 3s ease-in-out infinite;
|
||||
color: var(--primary);
|
||||
margin-right: 12px;
|
||||
filter: drop-shadow(0 0 8px rgba(146, 124, 255, 0.4));
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
text-shadow: 0 0 20px rgba(255, 107, 157, 0.3);
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
:deep(.el-menu) {
|
||||
border-right: none;
|
||||
background-color: transparent;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item) {
|
||||
border-radius: 12px;
|
||||
margin: 8px 12px;
|
||||
transition: var(--transition-hover);
|
||||
color: var(--text-secondary);
|
||||
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;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item::before) {
|
||||
/* 悬停状态 */
|
||||
.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: 0;
|
||||
height: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 3px;
|
||||
height: 20px;
|
||||
background: var(--primary);
|
||||
transform: scaleY(0);
|
||||
transition: transform 0.3s ease;
|
||||
border-radius: 0 2px 2px 0;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item:hover) {
|
||||
background: rgba(0, 212, 255, 0.1) !important;
|
||||
color: var(--primary);
|
||||
transform: translateX(8px);
|
||||
box-shadow: 0 4px 12px rgba(0, 212, 255, 0.2);
|
||||
.menu-icon {
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item:hover::before) {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
|
||||
:deep(.el-menu-item.is-active) {
|
||||
background: var(--gradient-cyan) !important;
|
||||
color: var(--bg-page) !important;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 4px 16px rgba(0, 212, 255, 0.4);
|
||||
}
|
||||
|
||||
:deep(.el-menu-item.is-active::before) {
|
||||
transform: scaleY(1);
|
||||
.menu-label {
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
background: var(--bg-page);
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user