refactor(ClientView): 重构客户端视图界面样式和组件结构
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 38s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 1m25s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m31s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 1m25s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m46s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 1m15s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m40s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m39s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m49s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 1m27s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m56s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m16s

- 替换 naive-ui 组件为自定义玻璃态设计组件
- 添加粒子动画背景效果
- 优化页面布局结构和响应式设计
- 移除未使用的 NCard、NTable、NStatistic 等组件导入
- 重构规则表格和插件列表的展示方式
- 更新模态框标题和简化配置表单
- 调整头部导航和底部栏样式
- 优化卡片组件的视觉效果和交互反馈
This commit is contained in:
Flik
2026-01-22 16:16:17 +08:00
parent 381c6911af
commit 4500f48d4c
5 changed files with 1595 additions and 728 deletions

View File

@@ -1,8 +1,7 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import {
NCard, NButton, NSpace, NTag, NGrid, NGi, NEmpty, NSpin, NIcon,
NAlert, useMessage, useDialog
NTag, NIcon, useMessage, useDialog
} from 'naive-ui'
import { CloudDownloadOutline, RefreshOutline, ServerOutline } from '@vicons/ionicons5'
import {
@@ -88,109 +87,140 @@ onMounted(() => {
</script>
<template>
<div class="settings-view">
<div class="page-header">
<h2>系统设置</h2>
<p>管理服务端配置和系统更新</p>
<div class="settings-page">
<!-- Particles -->
<div class="particles">
<div class="particle particle-1"></div>
<div class="particle particle-2"></div>
<div class="particle particle-3"></div>
</div>
<n-spin :show="loading">
<!-- 当前版本信息 -->
<n-card title="版本信息" class="settings-card">
<template #header-extra>
<n-icon size="20" color="#6366f1"><ServerOutline /></n-icon>
</template>
<n-grid v-if="versionInfo" :cols="6" :x-gap="16" responsive="screen" cols-s="2" cols-m="3">
<n-gi>
<div class="info-item">
<span class="label">版本号</span>
<span class="value">{{ versionInfo.version }}</span>
</div>
</n-gi>
<n-gi>
<div class="info-item">
<span class="label">Git 提交</span>
<span class="value">{{ versionInfo.git_commit?.slice(0, 8) || 'N/A' }}</span>
</div>
</n-gi>
<n-gi>
<div class="info-item">
<span class="label">构建时间</span>
<span class="value">{{ versionInfo.build_time || 'N/A' }}</span>
</div>
</n-gi>
<n-gi>
<div class="info-item">
<span class="label">Go 版本</span>
<span class="value">{{ versionInfo.go_version }}</span>
</div>
</n-gi>
<n-gi>
<div class="info-item">
<span class="label">操作系统</span>
<span class="value">{{ versionInfo.os }}</span>
</div>
</n-gi>
<n-gi>
<div class="info-item">
<span class="label">架构</span>
<span class="value">{{ versionInfo.arch }}</span>
</div>
</n-gi>
</n-grid>
<n-empty v-else description="加载中..." />
</n-card>
<div class="settings-content">
<!-- Header -->
<div class="page-header">
<h1 class="page-title">系统设置</h1>
<p class="page-subtitle">管理服务端配置和系统更新</p>
</div>
<!-- 服务端更新 -->
<n-card title="服务端更新" class="settings-card">
<template #header-extra>
<n-button size="small" :loading="checkingServer" @click="handleCheckServerUpdate">
<template #icon><n-icon><RefreshOutline /></n-icon></template>
<!-- Version Info Card -->
<div class="glass-card">
<div class="card-header">
<h3>版本信息</h3>
<n-icon size="20" color="#a78bfa"><ServerOutline /></n-icon>
</div>
<div class="card-body">
<div v-if="loading" class="loading-state">加载中...</div>
<div v-else-if="versionInfo" class="info-grid">
<div class="info-item">
<span class="info-label">版本号</span>
<span class="info-value">{{ versionInfo.version }}</span>
</div>
<div class="info-item">
<span class="info-label">Git 提交</span>
<span class="info-value mono">{{ versionInfo.git_commit?.slice(0, 8) || 'N/A' }}</span>
</div>
<div class="info-item">
<span class="info-label">构建时间</span>
<span class="info-value">{{ versionInfo.build_time || 'N/A' }}</span>
</div>
<div class="info-item">
<span class="info-label">Go 版本</span>
<span class="info-value">{{ versionInfo.go_version }}</span>
</div>
<div class="info-item">
<span class="info-label">操作系统</span>
<span class="info-value">{{ versionInfo.os }}</span>
</div>
<div class="info-item">
<span class="info-label">架构</span>
<span class="info-value">{{ versionInfo.arch }}</span>
</div>
</div>
<div v-else class="empty-state">无法加载版本信息</div>
</div>
</div>
<!-- Server Update Card -->
<div class="glass-card">
<div class="card-header">
<h3>服务端更新</h3>
<button class="glass-btn small" :disabled="checkingServer" @click="handleCheckServerUpdate">
<n-icon size="14"><RefreshOutline /></n-icon>
检查更新
</n-button>
</template>
</button>
</div>
<div class="card-body">
<div v-if="!serverUpdate" class="empty-state">
点击检查更新按钮查看是否有新版本
</div>
<template v-else>
<div v-if="serverUpdate.available" class="update-alert success">
发现新版本 {{ serverUpdate.latest }}当前版本 {{ serverUpdate.current }}
</div>
<div v-else class="update-alert info">
当前已是最新版本 {{ serverUpdate.current }}
</div>
<n-empty v-if="!serverUpdate" description="点击检查更新按钮查看是否有新版本" />
<template v-else>
<n-alert v-if="serverUpdate.available" type="success" style="margin-bottom: 16px;">
发现新版本 {{ serverUpdate.latest }}当前版本 {{ serverUpdate.current }}
</n-alert>
<n-alert v-else type="info" style="margin-bottom: 16px;">
当前已是最新版本 {{ serverUpdate.current }}
</n-alert>
<n-space vertical :size="12">
<div v-if="serverUpdate.download_url">
<p style="margin: 0 0 8px 0; color: #666;">
下载文件: {{ serverUpdate.asset_name }}
<n-tag size="small" style="margin-left: 8px;">{{ formatBytes(serverUpdate.asset_size) }}</n-tag>
</p>
<div v-if="serverUpdate.download_url" class="download-info">
下载文件: {{ serverUpdate.asset_name }}
<n-tag size="small" style="margin-left: 8px;">{{ formatBytes(serverUpdate.asset_size) }}</n-tag>
</div>
<div v-if="serverUpdate.release_note" class="release-note">
<p style="margin: 0 0 4px 0; color: #666; font-size: 12px;">更新日志:</p>
<span class="note-label">更新日志:</span>
<pre>{{ serverUpdate.release_note }}</pre>
</div>
<n-button
<button
v-if="serverUpdate.available && serverUpdate.download_url"
type="primary"
:loading="updatingServer"
class="glass-btn primary"
:disabled="updatingServer"
@click="handleApplyServerUpdate"
>
<template #icon><n-icon><CloudDownloadOutline /></n-icon></template>
<n-icon size="16"><CloudDownloadOutline /></n-icon>
下载并更新服务端
</n-button>
</n-space>
</template>
</n-card>
</n-spin>
</button>
</template>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.settings-view {
.settings-page {
min-height: calc(100vh - 108px);
background: linear-gradient(135deg, #1e1b4b 0%, #312e81 30%, #4c1d95 60%, #581c87 100%);
position: relative;
overflow: hidden;
padding: 32px;
}
.particles {
position: absolute;
inset: 0;
pointer-events: none;
}
.particle {
position: absolute;
border-radius: 50%;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.05));
animation: float-particle 20s ease-in-out infinite;
}
.particle-1 { width: 250px; height: 250px; top: -80px; right: -50px; }
.particle-2 { width: 180px; height: 180px; bottom: 10%; left: 5%; animation-delay: -7s; }
.particle-3 { width: 120px; height: 120px; top: 50%; right: 15%; animation-delay: -12s; }
@keyframes float-particle {
0%, 100% { transform: translate(0, 0) scale(1); opacity: 0.3; }
50% { transform: translate(-20px, -60px) scale(0.95); opacity: 0.4; }
}
.settings-content {
position: relative;
z-index: 10;
max-width: 900px;
margin: 0 auto;
}
@@ -199,52 +229,169 @@ onMounted(() => {
margin-bottom: 24px;
}
.page-header h2 {
.page-title {
font-size: 28px;
font-weight: 700;
color: white;
margin: 0 0 8px 0;
font-size: 24px;
font-weight: 600;
color: #1f2937;
}
.page-header p {
.page-subtitle {
color: rgba(255, 255, 255, 0.6);
margin: 0;
color: #6b7280;
font-size: 14px;
}
.settings-card {
margin-bottom: 16px;
/* Glass Card */
.glass-card {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.12);
margin-bottom: 20px;
}
.card-header {
padding: 16px 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
justify-content: space-between;
align-items: center;
}
.card-header h3 {
margin: 0;
font-size: 15px;
font-weight: 600;
color: white;
}
.card-body {
padding: 20px;
}
/* Info Grid */
.info-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
@media (max-width: 600px) {
.info-grid { grid-template-columns: repeat(2, 1fr); }
}
.info-item {
display: flex;
flex-direction: column;
padding: 8px 0;
gap: 4px;
}
.info-item .label {
.info-label {
font-size: 12px;
color: #9ca3af;
margin-bottom: 4px;
color: rgba(255, 255, 255, 0.5);
}
.info-item .value {
.info-value {
font-size: 14px;
color: #1f2937;
color: white;
font-weight: 500;
}
.info-value.mono {
font-family: monospace;
}
/* States */
.loading-state, .empty-state {
text-align: center;
padding: 32px;
color: rgba(255, 255, 255, 0.5);
}
/* Update Alert */
.update-alert {
padding: 12px 16px;
border-radius: 8px;
margin-bottom: 16px;
font-size: 13px;
}
.update-alert.success {
background: rgba(52, 211, 153, 0.15);
border: 1px solid rgba(52, 211, 153, 0.3);
color: #34d399;
}
.update-alert.info {
background: rgba(96, 165, 250, 0.15);
border: 1px solid rgba(96, 165, 250, 0.3);
color: #60a5fa;
}
/* Download Info */
.download-info {
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
margin-bottom: 12px;
}
/* Release Note */
.release-note {
max-height: 150px;
overflow-y: auto;
margin-bottom: 16px;
}
.note-label {
display: block;
font-size: 12px;
color: rgba(255, 255, 255, 0.5);
margin-bottom: 6px;
}
.release-note pre {
margin: 0;
white-space: pre-wrap;
font-size: 12px;
color: #374151;
background: #f9fafb;
color: rgba(255, 255, 255, 0.7);
background: rgba(0, 0, 0, 0.2);
padding: 12px;
border-radius: 6px;
border-radius: 8px;
max-height: 150px;
overflow-y: auto;
}
/* Glass Button */
.glass-btn {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 8px;
padding: 8px 16px;
color: white;
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 6px;
}
.glass-btn:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.2);
}
.glass-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.glass-btn.small {
padding: 6px 12px;
font-size: 12px;
}
.glass-btn.primary {
background: linear-gradient(135deg, #60a5fa 0%, #a78bfa 100%);
border: none;
}
</style>