优化前端代码架构 - 提取CSS变量和工具类

主要改进:
- 新增 variables.css 统一管理所有主题相关的CSS变量
- 新增 utilities.css 提供可复用的工具类组件
- 重构所有Vue组件,移除重复的CSS代码
- 统一使用CSS变量实现一致的粉色主题(#FF6B9D)
- 改进代码组织结构,提升可维护性
- 优化样式继承和复用机制

修改文件:
- 新增:frontend/src/styles/variables.css, utilities.css
- 重构:App.vue, 所有视图组件和组件文件
- 更新:style.css, element-plus.css

技术亮点:
- 模块化CSS架构,使用@import导入
- 统一的颜色、间距、阴影、过渡效果变量
- 卡片、按钮、布局等通用工具类
- 响应式设计支持
This commit is contained in:
祀梦
2026-01-27 21:58:28 +08:00
parent e1d9a63e3b
commit 209f03a238
14 changed files with 610 additions and 1264 deletions

View File

@@ -1,64 +1,148 @@
<template>
<div class="settings">
<PageHeader title="设置" icon="⚙️" />
<div class="page-container">
<PageHeader title="系统设置" icon="⚙️" />
<el-card class="settings-card" shadow="hover">
<el-card class="settings-card" shadow="hover" v-loading="loading">
<template #header>
<div class="card-header">
<span class="card-title">📝 关于</span>
<span class="card-title">🎨 基础配置</span>
<el-button type="primary" @click="handleSave" size="large" :loading="saving">
<span class="btn-icon">💾</span>
保存配置
</el-button>
</div>
</template>
<div class="about-content">
<div class="about-item">
<span class="about-label">项目名称</span>
<span class="about-value">代理池管理系统</span>
</div>
<div class="about-item">
<span class="about-label">版本号</span>
<span class="about-value">v1.0.0</span>
</div>
<div class="about-item">
<span class="about-label">后端API</span>
<span class="about-value">http://localhost:3000</span>
</div>
<div class="about-item">
<span class="about-label">前端服务</span>
<span class="about-value">http://localhost:8080</span>
</div>
<div class="about-item">
<span class="about-label">数据库</span>
<span class="about-value">SQLite</span>
</div>
</div>
<el-form :model="settings" label-width="150px" class="settings-form">
<el-form-item label="数据库路径">
<el-input v-model="settings.db_path" placeholder="数据库文件路径" />
</el-form-item>
<el-form-item label="爬取超时">
<el-input-number
v-model="settings.crawl_timeout"
:min="5"
:max="120"
:step="5"
class="setting-input"
/>
<span class="setting-suffix"></span>
</el-form-item>
<el-form-item label="验证超时">
<el-input-number
v-model="settings.validation_timeout"
:min="3"
:max="60"
:step="1"
class="setting-input"
/>
<span class="setting-suffix"></span>
</el-form-item>
<el-form-item label="最大重试次数">
<el-input-number
v-model="settings.max_retries"
:min="0"
:max="10"
class="setting-input"
/>
</el-form-item>
<el-form-item label="默认并发数">
<el-input-number
v-model="settings.default_concurrency"
:min="10"
:max="200"
:step="10"
class="setting-input"
/>
</el-form-item>
<el-form-item label="最低代理分数">
<el-input-number
v-model="settings.min_proxy_score"
:min="0"
:max="10"
:step="1"
class="setting-input"
/>
</el-form-item>
<el-form-item label="代理过期时间">
<el-input-number
v-model="settings.proxy_expiry_days"
:min="1"
:max="30"
:step="1"
class="setting-input"
/>
<span class="setting-suffix"></span>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import PageHeader from '../components/PageHeader.vue'
const loading = ref(false)
const saving = ref(false)
const settings = reactive({
db_path: '',
crawl_timeout: 30,
validation_timeout: 10,
max_retries: 3,
default_concurrency: 50,
min_proxy_score: 5,
proxy_expiry_days: 7
})
async function fetchSettings() {
loading.value = true
try {
const response = await fetch('http://localhost:8923/api/settings')
if (response.ok) {
const data = await response.json()
Object.assign(settings, data)
}
} finally {
loading.value = false
}
}
async function handleSave() {
saving.value = true
try {
const response = await fetch('http://localhost:8923/api/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(settings)
})
if (response.ok) {
ElMessage.success('配置保存成功啦~')
} else {
ElMessage.error('配置保存失败呢~')
}
} finally {
saving.value = false
}
}
onMounted(() => {
fetchSettings()
})
</script>
<style scoped>
.settings {
padding: 20px;
background: var(--theme-bg);
min-height: 100vh;
color: var(--theme-text);
}
.settings-card {
margin-bottom: 20px;
border-radius: 12px;
background: var(--theme-bg-card);
border: 1px solid var(--theme-border);
transition: all 0.3s ease;
}
.settings-card:hover {
box-shadow: 0 4px 16px rgba(255, 107, 157, 0.15);
transform: translateY(-2px);
border-color: var(--theme-primary);
border-radius: var(--radius-xl);
}
.card-header {
@@ -69,132 +153,26 @@ import PageHeader from '../components/PageHeader.vue'
.card-title {
font-size: 18px;
font-weight: 700;
color: var(--theme-primary);
font-weight: 600;
color: var(--primary);
}
.settings-content {
.settings-form {
padding: 20px;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
border-bottom: 1px solid var(--theme-border);
transition: all 0.3s ease;
}
.setting-item:hover {
background-color: var(--theme-bg-light);
border-radius: 8px;
padding: 20px 10px;
margin: 0 -10px;
}
.setting-item:last-child {
border-bottom: none;
}
.setting-info {
flex: 1;
padding-right: 20px;
}
.setting-label {
font-size: 16px;
font-weight: 700;
color: var(--theme-text);
margin-bottom: 5px;
}
.setting-desc {
font-size: 14px;
color: var(--theme-text-secondary);
max-width: 800px;
}
.setting-input {
width: 150px;
width: 300px;
}
.setting-actions {
display: flex;
gap: 20px;
justify-content: center;
padding-top: 20px;
border-top: 1px solid var(--theme-border);
}
.save-btn {
padding: 12px 30px;
font-size: 16px;
border-radius: 8px;
background-color: var(--theme-primary);
border: none;
color: var(--theme-bg);
font-weight: 700;
transition: all 0.3s ease;
}
.save-btn:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.reset-btn {
padding: 12px 30px;
font-size: 16px;
border-radius: 8px;
background-color: var(--theme-bg-light);
border: 1px solid var(--theme-border);
color: var(--theme-text);
font-weight: 700;
transition: all 0.3s ease;
}
.reset-btn:hover {
transform: translateY(-3px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
.setting-suffix {
margin-left: 10px;
color: var(--text-secondary);
}
.btn-icon {
font-size: 18px;
font-size: 20px;
margin-right: 8px;
}
.about-content {
padding: 20px;
}
.about-item {
display: flex;
justify-content: space-between;
padding: 15px 0;
border-bottom: 1px solid var(--theme-border);
transition: all 0.3s ease;
}
.about-item:hover {
background-color: var(--theme-bg-light);
border-radius: 8px;
padding: 15px 10px;
margin: 0 -10px;
}
.about-item:last-child {
border-bottom: none;
}
.about-label {
font-size: 16px;
color: var(--theme-text-secondary);
font-weight: 600;
}
.about-value {
font-size: 16px;
color: var(--theme-primary);
font-weight: 700;
}
</style>