feat: UI美化

This commit is contained in:
tbphp
2025-07-03 23:23:02 +08:00
parent e06038ddb9
commit 5b0fcc5739
14 changed files with 1894 additions and 123 deletions

View File

@@ -4,12 +4,81 @@ import LineChart from "@/components/LineChart.vue";
</script>
<template>
<base-info-card />
<line-chart class="chart" />
<div class="dashboard-container">
<div class="dashboard-header">
<h2 class="dashboard-title">仪表盘</h2>
<p class="dashboard-subtitle">系统概览与实时监控</p>
</div>
<div class="dashboard-content">
<base-info-card />
<line-chart class="dashboard-chart" />
</div>
</div>
</template>
<style scoped>
.chart {
margin-top: 20px;
.dashboard-container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
.dashboard-header {
margin-bottom: 32px;
text-align: center;
}
.dashboard-title {
font-size: 2.25rem;
font-weight: 700;
background: var(--primary-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin: 0 0 8px 0;
letter-spacing: -0.5px;
}
.dashboard-subtitle {
font-size: 1.1rem;
color: #64748b;
margin: 0;
font-weight: 500;
}
.dashboard-content {
display: flex;
flex-direction: column;
gap: 24px;
}
.dashboard-chart {
animation: fadeInUp 0.6s ease-out 0.2s both;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.dashboard-title {
font-size: 1.75rem;
}
.dashboard-subtitle {
font-size: 1rem;
}
.dashboard-content {
gap: 16px;
}
}
</style>

View File

@@ -1,3 +1,118 @@
<script setup lang="ts">
// 这里可以添加密钥管理相关的逻辑
</script>
<template>
<div>keys</div>
<div class="keys-container">
<div class="page-header">
<h2 class="page-title">密钥管理</h2>
<p class="page-subtitle">管理API密钥和访问凭证</p>
</div>
<div class="content-placeholder">
<div class="placeholder-card modern-card">
<div class="placeholder-icon">🔑</div>
<h3 class="placeholder-title">密钥管理功能</h3>
<p class="placeholder-description">
此功能正在开发中将提供完整的API密钥管理功能包括添加删除编辑和监控密钥使用情况
</p>
<div class="placeholder-features">
<div class="feature-item"> 密钥添加与删除</div>
<div class="feature-item">📊 使用情况统计</div>
<div class="feature-item">🔒 安全性验证</div>
<div class="feature-item">🔄 自动轮换</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.keys-container {
max-width: 1000px;
margin: 0 auto;
}
.page-header {
margin-bottom: 32px;
text-align: center;
}
.page-title {
font-size: 2.25rem;
font-weight: 700;
background: var(--primary-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin: 0 0 8px 0;
letter-spacing: -0.5px;
}
.page-subtitle {
font-size: 1.1rem;
color: #64748b;
margin: 0;
font-weight: 500;
}
.content-placeholder {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
}
.placeholder-card {
text-align: center;
max-width: 500px;
padding: 48px 32px;
background: rgba(255, 255, 255, 0.98);
}
.placeholder-icon {
font-size: 4rem;
margin-bottom: 24px;
display: block;
}
.placeholder-title {
font-size: 1.5rem;
font-weight: 600;
color: #1e293b;
margin: 0 0 16px 0;
}
.placeholder-description {
font-size: 1rem;
color: #64748b;
line-height: 1.6;
margin: 0 0 32px 0;
}
.placeholder-features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
}
.feature-item {
padding: 12px 16px;
background: rgba(102, 126, 234, 0.1);
border-radius: var(--border-radius-md);
color: #667eea;
font-weight: 500;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.placeholder-card {
margin: 0 16px;
padding: 32px 24px;
}
.placeholder-features {
grid-template-columns: 1fr;
}
}
</style>

View File

@@ -12,7 +12,7 @@ const { login } = useAuthService();
const handleLogin = async () => {
if (!authKey.value) {
message.error("Please enter Auth Key");
message.error("请输入授权密钥");
return;
}
loading.value = true;
@@ -21,42 +21,224 @@ const handleLogin = async () => {
if (success) {
router.push("/");
} else {
message.error("Login failed, please check your Auth Key");
message.error("登录失败,请检查您的授权密钥");
}
};
</script>
<template>
<div class="login-container">
<n-card class="login-card" title="登录">
<n-space vertical>
<n-input
v-model:value="authKey"
type="password"
placeholder="Auth Key"
@keyup.enter="handleLogin"
/>
<n-button class="login-btn" type="primary" block @click="handleLogin" :loading="loading">
Login
</n-button>
</n-space>
</n-card>
<div class="login-background">
<div class="login-decoration" />
<div class="login-decoration-2" />
</div>
<div class="login-content">
<div class="login-header">
<h1 class="login-title">GPT Load</h1>
<p class="login-subtitle">智能负载均衡管理平台</p>
</div>
<n-card class="login-card modern-card" :bordered="false">
<template #header>
<div class="card-header">
<h2 class="card-title">欢迎回来</h2>
<p class="card-subtitle">请输入您的授权密钥以继续</p>
</div>
</template>
<n-space vertical size="large">
<n-input
v-model:value="authKey"
type="password"
size="large"
placeholder="请输入授权密钥"
class="modern-input"
@keyup.enter="handleLogin"
>
<template #prefix>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path
d="M6 10v-4a6 6 0 1 1 12 0v4h1a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h1zm6-6a4 4 0 0 0-4 4v4h8v-4a4 4 0 0 0-4-4z"
/>
</svg>
</template>
</n-input>
<n-button
class="login-btn modern-button"
type="primary"
size="large"
block
@click="handleLogin"
:loading="loading"
:disabled="loading"
>
<template v-if="!loading">
<span>立即登录</span>
</template>
</n-button>
</n-space>
</n-card>
<div class="login-footer">
<p class="footer-text">© 2024 GPT Load. All rights reserved.</p>
</div>
</div>
</div>
</template>
<style scoped>
.login-container {
height: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
}
.login-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
}
.login-decoration {
position: absolute;
top: -50%;
right: -20%;
width: 800px;
height: 800px;
background: var(--primary-gradient);
border-radius: 50%;
opacity: 0.1;
animation: float 6s ease-in-out infinite;
}
.login-decoration-2 {
position: absolute;
bottom: -50%;
left: -20%;
width: 600px;
height: 600px;
background: var(--secondary-gradient);
border-radius: 50%;
opacity: 0.08;
animation: float 8s ease-in-out infinite reverse;
}
@keyframes float {
0%,
100% {
transform: translateY(0px) rotate(0deg);
}
50% {
transform: translateY(-20px) rotate(5deg);
}
}
.login-content {
position: relative;
z-index: 1;
width: 100%;
max-width: 420px;
padding: 0 20px;
}
.login-header {
text-align: center;
margin-bottom: 40px;
}
.login-title {
font-size: 2.5rem;
font-weight: 700;
background: var(--primary-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 8px;
letter-spacing: -0.5px;
}
.login-subtitle {
font-size: 1.1rem;
color: #64748b;
margin: 0;
font-weight: 500;
}
.login-card {
max-width: 400px;
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.card-header {
text-align: center;
padding-bottom: 8px;
}
.card-title {
font-size: 1.5rem;
font-weight: 600;
color: #1e293b;
margin: 0 0 8px 0;
}
.card-subtitle {
font-size: 0.95rem;
color: #64748b;
margin: 0;
}
.login-btn {
margin-top: 10px;
background: var(--primary-gradient);
border: none;
font-weight: 600;
letter-spacing: 0.5px;
height: 48px;
font-size: 1rem;
}
.login-btn:hover {
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
transform: translateY(-1px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}
.login-footer {
text-align: center;
margin-top: 32px;
}
.footer-text {
font-size: 0.875rem;
color: #94a3b8;
margin: 0;
}
:deep(.n-input) {
--n-border-radius: 12px;
--n-height: 48px;
}
:deep(.n-input__input-el) {
font-size: 1rem;
}
:deep(.n-input__prefix) {
color: #64748b;
}
:deep(.n-card-header) {
padding-bottom: 16px;
}
:deep(.n-card__content) {
padding-top: 0;
}
</style>

View File

@@ -1,3 +1,118 @@
<script setup lang="ts">
// 这里可以添加日志管理相关的逻辑
</script>
<template>
<div>logs</div>
<div class="logs-container">
<div class="page-header">
<h2 class="page-title">系统日志</h2>
<p class="page-subtitle">查看系统运行日志和操作记录</p>
</div>
<div class="content-placeholder">
<div class="placeholder-card modern-card">
<div class="placeholder-icon">📋</div>
<h3 class="placeholder-title">日志管理功能</h3>
<p class="placeholder-description">
此功能正在开发中将提供完整的系统日志查看和管理功能包括实时日志历史记录和日志分析
</p>
<div class="placeholder-features">
<div class="feature-item">📝 实时日志流</div>
<div class="feature-item">🔍 日志搜索过滤</div>
<div class="feature-item">📈 错误统计分析</div>
<div class="feature-item">💾 日志导出功能</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.logs-container {
max-width: 1000px;
margin: 0 auto;
}
.page-header {
margin-bottom: 32px;
text-align: center;
}
.page-title {
font-size: 2.25rem;
font-weight: 700;
background: var(--primary-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin: 0 0 8px 0;
letter-spacing: -0.5px;
}
.page-subtitle {
font-size: 1.1rem;
color: #64748b;
margin: 0;
font-weight: 500;
}
.content-placeholder {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
}
.placeholder-card {
text-align: center;
max-width: 500px;
padding: 48px 32px;
background: rgba(255, 255, 255, 0.98);
}
.placeholder-icon {
font-size: 4rem;
margin-bottom: 24px;
display: block;
}
.placeholder-title {
font-size: 1.5rem;
font-weight: 600;
color: #1e293b;
margin: 0 0 16px 0;
}
.placeholder-description {
font-size: 1rem;
color: #64748b;
line-height: 1.6;
margin: 0 0 32px 0;
}
.placeholder-features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
}
.feature-item {
padding: 12px 16px;
background: rgba(102, 126, 234, 0.1);
border-radius: var(--border-radius-md);
color: #667eea;
font-weight: 500;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.placeholder-card {
margin: 0 16px;
padding: 32px 24px;
}
.placeholder-features {
grid-template-columns: 1fr;
}
}
</style>

View File

@@ -42,63 +42,286 @@ async function handleSubmit() {
</script>
<template>
<div>
<n-form ref="formRef" :model="form" label-placement="left" label-width="110">
<n-card
v-for="(category, cIndex) in settingList"
:key="cIndex"
:bordered="false"
:title="category.category_name"
>
<n-space>
<n-form-item
v-for="item in category.settings"
:key="item.key"
:path="item.key"
style="margin-right: 10px"
:rule="{
required: true,
message: `请输入${item.name}`,
}"
>
<template #label>
<n-tooltip trigger="hover">
<template #trigger>
<span>{{ item.name }}</span>
</template>
<span>{{ item.description }}</span>
</n-tooltip>
<div class="settings-container">
<div class="settings-header">
<h2 class="settings-title">系统设置</h2>
<p class="settings-subtitle">配置系统参数和选项</p>
</div>
<div class="settings-content">
<n-form ref="formRef" :model="form" label-placement="top" class="settings-form">
<div v-for="(category, cIndex) in settingList" :key="cIndex" class="settings-category">
<n-card class="category-card modern-card" :bordered="false" size="small">
<template #header>
<div class="category-header">
<h3 class="category-title">{{ category.category_name }}</h3>
<div class="category-divider" />
</div>
</template>
<n-input-number
v-if="item.type === 'int'"
v-model:value="form[item.key]"
:min="item.min_value! >= 0 ? item.min_value : undefined"
style="width: 120px"
placeholder=""
clearable
/>
<n-input
v-else
v-model:value="form[item.key]"
style="width: 120px"
placeholder=""
clearable
/>
</n-form-item>
</n-space>
</n-card>
</n-form>
<div class="settings-grid">
<n-form-item
v-for="item in category.settings"
:key="item.key"
:path="item.key"
class="setting-item"
:rule="{
required: true,
message: `请输入${item.name}`,
}"
>
<template #label>
<div class="setting-label">
<span class="label-text">{{ item.name }}</span>
<n-tooltip trigger="hover" placement="top">
<template #trigger>
<div class="label-help">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"
/>
</svg>
</div>
</template>
<div class="tooltip-content">{{ item.description }}</div>
</n-tooltip>
</div>
</template>
<n-input-number
v-if="item.type === 'int'"
v-model:value="form[item.key]"
:min="item.min_value! >= 0 ? item.min_value : undefined"
class="modern-input setting-input"
placeholder="请输入数值"
clearable
/>
<n-input
v-else
v-model:value="form[item.key]"
class="modern-input setting-input"
placeholder="请输入内容"
clearable
/>
</n-form-item>
</div>
</n-card>
</div>
</n-form>
<div class="settings-actions">
<n-button
v-show="settingList.length > 0"
type="primary"
size="large"
class="save-button modern-button"
:loading="isSaving"
:disabled="isSaving"
@click="handleSubmit"
>
<template #icon>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
<path
d="M17 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"
/>
</svg>
</template>
{{ isSaving ? "保存中..." : "保存设置" }}
</n-button>
</div>
</div>
</div>
<n-flex justify="center">
<n-button
v-show="settingList.length > 0"
type="primary"
style="width: 200px"
:loading="isSaving"
:disabled="isSaving"
@click="handleSubmit"
>
保存设置
</n-button>
</n-flex>
</template>
<style scoped>
.settings-container {
max-width: 1000px;
margin: 0 auto;
}
.settings-header {
margin-bottom: 32px;
text-align: center;
}
.settings-title {
font-size: 2.25rem;
font-weight: 700;
background: var(--primary-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin: 0 0 8px 0;
letter-spacing: -0.5px;
}
.settings-subtitle {
font-size: 1.1rem;
color: #64748b;
margin: 0;
font-weight: 500;
}
.settings-content {
display: flex;
flex-direction: column;
gap: 24px;
}
.settings-category {
animation: fadeInUp 0.6s ease-out both;
margin-bottom: 24px;
}
.settings-category:nth-child(2) {
animation-delay: 0.1s;
}
.settings-category:nth-child(3) {
animation-delay: 0.2s;
}
.settings-category:nth-child(4) {
animation-delay: 0.3s;
}
.category-card {
background: rgba(255, 255, 255, 0.98);
}
.category-header {
display: flex;
flex-direction: column;
gap: 12px;
}
.category-title {
font-size: 1.3rem;
font-weight: 600;
color: #1e293b;
margin: 0;
}
.category-divider {
height: 3px;
background: var(--primary-gradient);
border-radius: 2px;
width: 60px;
}
.settings-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 12px 10px;
}
.setting-item {
margin-bottom: 0;
}
.setting-label {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
}
.label-text {
font-weight: 600;
color: #374151;
font-size: 0.95rem;
}
.label-help {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 50%;
background: rgba(102, 126, 234, 0.1);
color: #667eea;
cursor: help;
transition: all 0.2s ease;
}
.label-help:hover {
background: rgba(102, 126, 234, 0.2);
transform: scale(1.1);
}
.tooltip-content {
max-width: 250px;
font-size: 0.875rem;
line-height: 1.5;
}
.setting-input {
width: 100%;
}
.settings-actions {
display: flex;
justify-content: center;
padding-top: 24px;
border-top: 1px solid rgba(0, 0, 0, 0.06);
}
.save-button {
min-width: 200px;
background: var(--primary-gradient);
border: none;
font-weight: 600;
letter-spacing: 0.5px;
height: 48px;
font-size: 1rem;
}
.save-button:hover {
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
transform: translateY(-1px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.settings-title {
font-size: 1.75rem;
}
.settings-grid {
grid-template-columns: 1fr;
gap: 20px;
}
.save-button {
width: 100%;
}
}
:deep(.n-form-item-label) {
padding: 0;
}
:deep(.n-input) {
--n-border-radius: 12px;
}
:deep(.n-input-number) {
--n-border-radius: 12px;
}
:deep(.n-card-header) {
padding-bottom: 5px;
}
</style>