refactor: 更新 logo 图标并调整布局

- 将 logo 图标从 logo.png 更改为 xiaoyugan.png
-调整了多个组件的布局结构
- 更新了部分样式,如按钮、文本等
This commit is contained in:
steve
2025-08-22 18:12:01 +08:00
parent 498849dc1e
commit db2d5687b9
6 changed files with 164 additions and 164 deletions

2
package-lock.json generated
View File

@@ -7,7 +7,7 @@
"": { "": {
"name": "xyzw-token-manager", "name": "xyzw-token-manager",
"version": "2.0.0", "version": "2.0.0",
"license": "MIT", "license": "CC-BY-NC-SA-4.0",
"dependencies": { "dependencies": {
"@vicons/ionicons5": "^0.12.0", "@vicons/ionicons5": "^0.12.0",
"@vicons/material": "^0.12.0", "@vicons/material": "^0.12.0",

View File

@@ -5,13 +5,13 @@
<div class="nav-container"> <div class="nav-container">
<div class="nav-brand"> <div class="nav-brand">
<img <img
src="/icons/logo.png" src="/icons/xiaoyugan.png"
alt="XYZW" alt="XYZW"
class="brand-logo" class="brand-logo"
> >
<span class="brand-text">XYZW 控制台</span> <span class="brand-text">XYZW 控制台</span>
</div> </div>
<div class="nav-menu"> <div class="nav-menu">
<router-link <router-link
to="/dashboard" to="/dashboard"
@@ -62,15 +62,15 @@
<span>个人设置</span> <span>个人设置</span>
</router-link> </router-link>
</div> </div>
<div class="nav-user"> <div class="nav-user">
<n-dropdown <n-dropdown
:options="userMenuOptions" :options="userMenuOptions"
@select="handleUserAction" @select="handleUserAction"
> >
<div class="user-info"> <div class="user-info">
<n-avatar <n-avatar
size="medium" size="medium"
fallback-src="/icons/xiaoyugan.png" fallback-src="/icons/xiaoyugan.png"
/> />
<span class="username">{{ tokenStore.selectedToken?.name || '未选择Token' }}</span> <span class="username">{{ tokenStore.selectedToken?.name || '未选择Token' }}</span>
@@ -92,14 +92,14 @@
<p>今天是 {{ currentDate }}继续您的游戏管理之旅吧</p> <p>今天是 {{ currentDate }}继续您的游戏管理之旅吧</p>
</div> </div>
<div class="welcome-actions"> <div class="welcome-actions">
<n-button <n-button
type="primary" type="primary"
size="large" size="large"
@click="router.push('/game-features')" @click="router.push('/game-features')"
> >
进入游戏功能 进入游戏功能
</n-button> </n-button>
<n-button <n-button
size="large" size="large"
@click="router.push('/tokens')" @click="router.push('/tokens')"
> >
@@ -147,7 +147,7 @@
快速操作 快速操作
</h2> </h2>
<div class="actions-grid"> <div class="actions-grid">
<div <div
v-for="action in quickActions" v-for="action in quickActions"
:key="action.id" :key="action.id"
class="action-card" class="action-card"
@@ -178,12 +178,12 @@
刷新 刷新
</n-button> </n-button>
</div> </div>
<div <div
v-if="recentActivities.length" v-if="recentActivities.length"
class="activity-list" class="activity-list"
> >
<div <div
v-for="activity in recentActivities" v-for="activity in recentActivities"
:key="activity.id" :key="activity.id"
class="activity-item" class="activity-item"
@@ -204,7 +204,7 @@
</div> </div>
</div> </div>
</div> </div>
<div <div
v-else v-else
class="empty-activity" class="empty-activity"
@@ -222,10 +222,10 @@ import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useMessage } from 'naive-ui' import { useMessage } from 'naive-ui'
import { useTokenStore } from '@/stores/tokenStore' import { useTokenStore } from '@/stores/tokenStore'
import { import {
Home, Home,
PersonCircle, PersonCircle,
Cube, Cube,
Settings, Settings,
ChevronDown, ChevronDown,
Ribbon, Ribbon,
@@ -428,7 +428,7 @@ const formatTime = (timestamp) => {
const minutes = Math.floor(diff / (1000 * 60)) const minutes = Math.floor(diff / (1000 * 60))
const hours = Math.floor(diff / (1000 * 60 * 60)) const hours = Math.floor(diff / (1000 * 60 * 60))
const days = Math.floor(diff / (1000 * 60 * 60 * 24)) const days = Math.floor(diff / (1000 * 60 * 60 * 24))
if (days > 0) { if (days > 0) {
return `${days}天前` return `${days}天前`
} else if (hours > 0) { } else if (hours > 0) {
@@ -512,12 +512,12 @@ onMounted(async () => {
color: var(--text-secondary); color: var(--text-secondary);
text-decoration: none; text-decoration: none;
transition: all var(--transition-fast); transition: all var(--transition-fast);
&:hover { &:hover {
background: var(--bg-tertiary); background: var(--bg-tertiary);
color: var(--text-primary); color: var(--text-primary);
} }
&.active { &.active {
background: var(--primary-color-light); background: var(--primary-color-light);
color: var(--primary-color); color: var(--primary-color);
@@ -536,7 +536,7 @@ onMounted(async () => {
border-radius: var(--border-radius-medium); border-radius: var(--border-radius-medium);
cursor: pointer; cursor: pointer;
transition: background var(--transition-fast); transition: background var(--transition-fast);
&:hover { &:hover {
background: var(--bg-tertiary); background: var(--bg-tertiary);
} }
@@ -579,7 +579,7 @@ onMounted(async () => {
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
margin-bottom: var(--spacing-sm); margin-bottom: var(--spacing-sm);
} }
p { p {
font-size: var(--font-size-lg); font-size: var(--font-size-lg);
opacity: 0.9; opacity: 0.9;
@@ -609,7 +609,7 @@ onMounted(async () => {
padding: var(--spacing-lg); padding: var(--spacing-lg);
box-shadow: var(--shadow-light); box-shadow: var(--shadow-light);
transition: all var(--transition-normal); transition: all var(--transition-normal);
&:hover { &:hover {
box-shadow: var(--shadow-medium); box-shadow: var(--shadow-medium);
transform: translateY(-2px); transform: translateY(-2px);
@@ -620,7 +620,7 @@ onMounted(async () => {
width: 48px; width: 48px;
height: 48px; height: 48px;
margin-bottom: var(--spacing-md); margin-bottom: var(--spacing-md);
:deep(svg) { :deep(svg) {
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -643,11 +643,11 @@ onMounted(async () => {
.stat-change { .stat-change {
font-size: var(--font-size-sm); font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
&.positive { &.positive {
color: var(--success-color); color: var(--success-color);
} }
&.negative { &.negative {
color: var(--error-color); color: var(--error-color);
} }
@@ -678,7 +678,7 @@ onMounted(async () => {
box-shadow: var(--shadow-light); box-shadow: var(--shadow-light);
cursor: pointer; cursor: pointer;
transition: all var(--transition-normal); transition: all var(--transition-normal);
&:hover { &:hover {
box-shadow: var(--shadow-medium); box-shadow: var(--shadow-medium);
transform: translateY(-2px); transform: translateY(-2px);
@@ -690,7 +690,7 @@ onMounted(async () => {
height: 40px; height: 40px;
color: var(--primary-color); color: var(--primary-color);
margin-bottom: var(--spacing-md); margin-bottom: var(--spacing-md);
:deep(svg) { :deep(svg) {
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -704,7 +704,7 @@ onMounted(async () => {
color: var(--text-primary); color: var(--text-primary);
margin-bottom: var(--spacing-xs); margin-bottom: var(--spacing-xs);
} }
p { p {
color: var(--text-secondary); color: var(--text-secondary);
font-size: var(--font-size-sm); font-size: var(--font-size-sm);
@@ -739,7 +739,7 @@ onMounted(async () => {
padding: var(--spacing-md); padding: var(--spacing-md);
border-radius: var(--border-radius-medium); border-radius: var(--border-radius-medium);
transition: background var(--transition-fast); transition: background var(--transition-fast);
&:hover { &:hover {
background: var(--bg-tertiary); background: var(--bg-tertiary);
} }
@@ -753,22 +753,22 @@ onMounted(async () => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-shrink: 0; flex-shrink: 0;
&.success { &.success {
background: rgba(24, 160, 88, 0.1); background: rgba(24, 160, 88, 0.1);
color: var(--success-color); color: var(--success-color);
} }
&.warning { &.warning {
background: rgba(240, 160, 32, 0.1); background: rgba(240, 160, 32, 0.1);
color: var(--warning-color); color: var(--warning-color);
} }
&.info { &.info {
background: rgba(32, 128, 240, 0.1); background: rgba(32, 128, 240, 0.1);
color: var(--info-color); color: var(--info-color);
} }
:deep(svg) { :deep(svg) {
width: 16px; width: 16px;
height: 16px; height: 16px;
@@ -801,7 +801,7 @@ onMounted(async () => {
flex-direction: column; flex-direction: column;
text-align: center; text-align: center;
} }
.stats-grid { .stats-grid {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
} }
@@ -811,30 +811,30 @@ onMounted(async () => {
.dashboard-main { .dashboard-main {
padding: var(--spacing-md); padding: var(--spacing-md);
} }
.nav-menu { .nav-menu {
display: none; display: none;
} }
.welcome-section { .welcome-section {
padding: var(--spacing-xl); padding: var(--spacing-xl);
} }
.welcome-text h1 { .welcome-text h1 {
font-size: var(--font-size-2xl); font-size: var(--font-size-2xl);
} }
.welcome-actions { .welcome-actions {
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
} }
.stats-grid { .stats-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.actions-grid { .actions-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
</style> </style>

View File

@@ -6,35 +6,35 @@
<div class="nav-content"> <div class="nav-content">
<div class="nav-brand"> <div class="nav-brand">
<img <img
src="/icons/logo.png" src="/icons/xiaoyugan.png"
alt="XYZW" alt="XYZW"
class="brand-logo" class="brand-logo"
> >
<span class="brand-text">XYZW 游戏管理系统</span> <span class="brand-text">XYZW 游戏管理系统</span>
</div> </div>
<div class="nav-actions"> <div class="nav-actions">
<template v-if="!authStore.isAuthenticated"> <template v-if="!authStore.isAuthenticated">
<n-button <n-button
text text
type="primary" type="primary"
size="large" size="large"
@click="router.push('/login')" @click="router.push('/login')"
> >
登录 登录
</n-button> </n-button>
<n-button <n-button
type="primary" type="primary"
size="large" size="large"
@click="router.push('/register')" @click="router.push('/register')"
> >
注册 注册
</n-button> </n-button>
</template> </template>
<template v-else> <template v-else>
<n-button <n-button
type="primary" type="primary"
size="large" size="large"
@click="router.push('/dashboard')" @click="router.push('/dashboard')"
> >
进入控制台 进入控制台
@@ -59,18 +59,18 @@
让游戏变得更简单让管理变得更高效 让游戏变得更简单让管理变得更高效
</p> </p>
<div class="hero-actions"> <div class="hero-actions">
<n-button <n-button
type="primary" type="primary"
size="large" size="large"
class="hero-button" class="hero-button"
@click="router.push(authStore.isAuthenticated ? '/dashboard' : '/register')" @click="router.push(authStore.isAuthenticated ? '/dashboard' : '/register')"
> >
{{ authStore.isAuthenticated ? '进入控制台' : '立即开始' }} {{ authStore.isAuthenticated ? '进入控制台' : '立即开始' }}
</n-button> </n-button>
<n-button <n-button
text text
type="primary" type="primary"
size="large" size="large"
class="hero-button" class="hero-button"
@click="scrollToFeatures" @click="scrollToFeatures"
> >
@@ -78,7 +78,7 @@
</n-button> </n-button>
</div> </div>
</div> </div>
<div class="hero-visual"> <div class="hero-visual">
<div class="feature-cards"> <div class="feature-cards">
<div <div
@@ -114,11 +114,11 @@
为您提供全方位的游戏管理解决方案 为您提供全方位的游戏管理解决方案
</p> </p>
</div> </div>
<div class="features-grid"> <div class="features-grid">
<div <div
v-for="feature in features" v-for="feature in features"
:key="feature.id" :key="feature.id"
class="feature-item" class="feature-item"
> >
<div class="feature-icon"> <div class="feature-icon">
@@ -162,7 +162,7 @@
<div class="footer-content"> <div class="footer-content">
<div class="footer-brand"> <div class="footer-brand">
<img <img
src="/icons/logo.png" src="/icons/xiaoyugan.png"
alt="XYZW" alt="XYZW"
class="footer-logo" class="footer-logo"
> >
@@ -199,12 +199,12 @@
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { import {
PersonCircle, PersonCircle,
Cube, Cube,
Ribbon, Ribbon,
Home, Home,
Settings Settings
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
const router = useRouter() const router = useRouter()
@@ -272,8 +272,8 @@ const stats = ref([
// 滚动到功能区域 // 滚动到功能区域
const scrollToFeatures = () => { const scrollToFeatures = () => {
if (featuresSection.value) { if (featuresSection.value) {
featuresSection.value.scrollIntoView({ featuresSection.value.scrollIntoView({
behavior: 'smooth' behavior: 'smooth'
}) })
} }
} }
@@ -399,7 +399,7 @@ onMounted(() => {
border-radius: var(--border-radius-large); border-radius: var(--border-radius-large);
padding: var(--spacing-lg); padding: var(--spacing-lg);
transition: all var(--transition-normal); transition: all var(--transition-normal);
&:hover { &:hover {
transform: translateY(-4px); transform: translateY(-4px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
@@ -411,7 +411,7 @@ onMounted(() => {
height: 48px; height: 48px;
color: #fff; color: #fff;
margin-bottom: var(--spacing-md); margin-bottom: var(--spacing-md);
:deep(svg) { :deep(svg) {
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -468,7 +468,7 @@ onMounted(() => {
padding: var(--spacing-xl); padding: var(--spacing-xl);
border-radius: var(--border-radius-large); border-radius: var(--border-radius-large);
transition: all var(--transition-normal); transition: all var(--transition-normal);
&:hover { &:hover {
transform: translateY(-4px); transform: translateY(-4px);
box-shadow: var(--shadow-heavy); box-shadow: var(--shadow-heavy);
@@ -480,7 +480,7 @@ onMounted(() => {
height: 64px; height: 64px;
margin: 0 auto var(--spacing-lg); margin: 0 auto var(--spacing-lg);
color: var(--primary-color); color: var(--primary-color);
:deep(svg) { :deep(svg) {
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -564,7 +564,7 @@ onMounted(() => {
.footer-link { .footer-link {
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
transition: color var(--transition-fast); transition: color var(--transition-fast);
&:hover { &:hover {
color: white; color: white;
} }
@@ -583,23 +583,23 @@ onMounted(() => {
grid-template-columns: 1fr; grid-template-columns: 1fr;
text-align: center; text-align: center;
} }
.hero-title { .hero-title {
font-size: 2.5rem; font-size: 2.5rem;
} }
.hero-actions { .hero-actions {
justify-content: center; justify-content: center;
} }
.footer-content { .footer-content {
flex-direction: column; flex-direction: column;
gap: var(--spacing-lg); gap: var(--spacing-lg);
} }
.nav-actions { .nav-actions {
flex-direction: column; flex-direction: column;
gap: var(--spacing-xs); gap: var(--spacing-xs);
} }
} }
</style> </style>

View File

@@ -6,7 +6,7 @@
<div class="card-header"> <div class="card-header">
<div class="brand"> <div class="brand">
<img <img
src="/icons/logo.png" src="/icons/xiaoyugan.png"
alt="XYZW" alt="XYZW"
class="brand-logo" class="brand-logo"
> >
@@ -87,8 +87,8 @@
</n-divider> </n-divider>
<div class="social-login"> <div class="social-login">
<n-button <n-button
size="large" size="large"
class="social-button" class="social-button"
@click="handleSocialLogin('qq')" @click="handleSocialLogin('qq')"
> >
@@ -99,9 +99,9 @@
</template> </template>
QQ登录 QQ登录
</n-button> </n-button>
<n-button <n-button
size="large" size="large"
class="social-button" class="social-button"
@click="handleSocialLogin('wechat')" @click="handleSocialLogin('wechat')"
> >
@@ -133,7 +133,7 @@
<h2>为什么选择 XYZW</h2> <h2>为什么选择 XYZW</h2>
<p>专业的游戏管理平台让游戏变得更轻松</p> <p>专业的游戏管理平台让游戏变得更轻松</p>
</div> </div>
<div class="features-list"> <div class="features-list">
<div <div
v-for="feature in features" v-for="feature in features"
@@ -237,7 +237,7 @@ const handleLogin = async () => {
try { try {
await loginFormRef.value.validate() await loginFormRef.value.validate()
const result = await authStore.login({ const result = await authStore.login({
username: loginForm.username, username: loginForm.username,
password: loginForm.password, password: loginForm.password,
@@ -246,7 +246,7 @@ const handleLogin = async () => {
if (result.success) { if (result.success) {
message.success('登录成功') message.success('登录成功')
// 跳转到dashboard或之前访问的页面 // 跳转到dashboard或之前访问的页面
const redirect = router.currentRoute.value.query.redirect || '/dashboard' const redirect = router.currentRoute.value.query.redirect || '/dashboard'
router.push(redirect) router.push(redirect)
@@ -370,7 +370,7 @@ onMounted(() => {
.social-button { .social-button {
height: 44px; height: 44px;
border: 1px solid var(--border-light); border: 1px solid var(--border-light);
&:hover { &:hover {
border-color: var(--primary-color); border-color: var(--primary-color);
} }
@@ -379,7 +379,7 @@ onMounted(() => {
.register-prompt { .register-prompt {
text-align: center; text-align: center;
color: var(--text-secondary); color: var(--text-secondary);
span { span {
margin-right: var(--spacing-sm); margin-right: var(--spacing-sm);
} }
@@ -394,13 +394,13 @@ onMounted(() => {
.showcase-header { .showcase-header {
text-align: center; text-align: center;
margin-bottom: var(--spacing-xl); margin-bottom: var(--spacing-xl);
h2 { h2 {
font-size: var(--font-size-3xl); font-size: var(--font-size-3xl);
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
margin-bottom: var(--spacing-md); margin-bottom: var(--spacing-md);
} }
p { p {
font-size: var(--font-size-lg); font-size: var(--font-size-lg);
opacity: 0.9; opacity: 0.9;
@@ -423,7 +423,7 @@ onMounted(() => {
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.2);
transition: all var(--transition-normal); transition: all var(--transition-normal);
&:hover { &:hover {
transform: translateX(8px); transform: translateX(8px);
background: rgba(255, 255, 255, 0.15); background: rgba(255, 255, 255, 0.15);
@@ -435,7 +435,7 @@ onMounted(() => {
height: 48px; height: 48px;
color: white; color: white;
flex-shrink: 0; flex-shrink: 0;
:deep(svg) { :deep(svg) {
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -444,13 +444,13 @@ onMounted(() => {
.feature-content { .feature-content {
flex: 1; flex: 1;
h3 { h3 {
font-size: var(--font-size-lg); font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
margin-bottom: var(--spacing-sm); margin-bottom: var(--spacing-sm);
} }
p { p {
opacity: 0.8; opacity: 0.8;
line-height: var(--line-height-relaxed); line-height: var(--line-height-relaxed);
@@ -515,11 +515,11 @@ onMounted(() => {
grid-template-columns: 1fr; grid-template-columns: 1fr;
max-width: 500px; max-width: 500px;
} }
.features-showcase { .features-showcase {
order: -1; order: -1;
} }
.showcase-header h2 { .showcase-header h2 {
font-size: var(--font-size-2xl); font-size: var(--font-size-2xl);
} }
@@ -529,26 +529,26 @@ onMounted(() => {
.login-container { .login-container {
padding: var(--spacing-md); padding: var(--spacing-md);
} }
.login-card { .login-card {
padding: var(--spacing-xl); padding: var(--spacing-xl);
} }
.brand-title { .brand-title {
font-size: var(--font-size-xl); font-size: var(--font-size-xl);
} }
.social-login { .social-login {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.feature-item { .feature-item {
flex-direction: column; flex-direction: column;
text-align: center; text-align: center;
} }
.decoration-circle { .decoration-circle {
display: none; display: none;
} }
} }
</style> </style>

View File

@@ -5,7 +5,7 @@
<div class="card-header"> <div class="card-header">
<div class="brand"> <div class="brand">
<img <img
src="/icons/logo.png" src="/icons/xiaoyugan.png"
alt="XYZW" alt="XYZW"
class="brand-logo" class="brand-logo"
> >
@@ -209,12 +209,12 @@ const handleRegister = async () => {
try { try {
await registerFormRef.value.validate() await registerFormRef.value.validate()
if (!registerForm.agreeTerms) { if (!registerForm.agreeTerms) {
message.warning('请先同意服务条款和隐私政策') message.warning('请先同意服务条款和隐私政策')
return return
} }
const result = await authStore.register({ const result = await authStore.register({
username: registerForm.username, username: registerForm.username,
email: registerForm.email, email: registerForm.email,
@@ -299,7 +299,7 @@ const handleRegister = async () => {
.form-options { .form-options {
margin-bottom: var(--spacing-xl); margin-bottom: var(--spacing-xl);
:deep(.n-checkbox) { :deep(.n-checkbox) {
line-height: var(--line-height-relaxed); line-height: var(--line-height-relaxed);
} }
@@ -315,7 +315,7 @@ const handleRegister = async () => {
.login-prompt { .login-prompt {
text-align: center; text-align: center;
color: var(--text-secondary); color: var(--text-secondary);
span { span {
margin-right: var(--spacing-sm); margin-right: var(--spacing-sm);
} }
@@ -325,9 +325,9 @@ const handleRegister = async () => {
.register-card { .register-card {
padding: var(--spacing-xl); padding: var(--spacing-xl);
} }
.brand-title { .brand-title {
font-size: var(--font-size-xl); font-size: var(--font-size-xl);
} }
} }
</style> </style>

View File

@@ -5,7 +5,7 @@
<div class="page-header"> <div class="page-header">
<div class="header-content"> <div class="header-content">
<img <img
src="/icons/logo.png" src="/icons/xiaoyugan.png"
alt="XYZW" alt="XYZW"
class="brand-logo" class="brand-logo"
> >
@@ -37,7 +37,7 @@
</n-alert> </n-alert>
</div> </div>
</div> </div>
<n-form <n-form
ref="importFormRef" ref="importFormRef"
:model="importForm" :model="importForm"
@@ -55,7 +55,7 @@
clearable clearable
/> />
</n-form-item> </n-form-item>
<n-form-item <n-form-item
label="Base64 Token" label="Base64 Token"
path="base64Token" path="base64Token"
@@ -68,7 +68,7 @@
clearable clearable
/> />
</n-form-item> </n-form-item>
<!-- 可选信息 --> <!-- 可选信息 -->
<n-collapse> <n-collapse>
<n-collapse-item <n-collapse-item
@@ -82,7 +82,7 @@
placeholder="服务器名称" placeholder="服务器名称"
/> />
</n-form-item> </n-form-item>
<n-form-item label="等级"> <n-form-item label="等级">
<n-input-number <n-input-number
v-model:value="importForm.level" v-model:value="importForm.level"
@@ -91,7 +91,7 @@
placeholder="角色等级" placeholder="角色等级"
/> />
</n-form-item> </n-form-item>
<n-form-item label="职业"> <n-form-item label="职业">
<n-select <n-select
v-model:value="importForm.profession" v-model:value="importForm.profession"
@@ -99,7 +99,7 @@
placeholder="选择职业" placeholder="选择职业"
/> />
</n-form-item> </n-form-item>
<n-form-item label="WebSocket URL (可选)"> <n-form-item label="WebSocket URL (可选)">
<n-input <n-input
v-model:value="importForm.wsUrl" v-model:value="importForm.wsUrl"
@@ -114,7 +114,7 @@
</div> </div>
</n-collapse-item> </n-collapse-item>
</n-collapse> </n-collapse>
<div class="form-actions"> <div class="form-actions">
<n-button <n-button
type="primary" type="primary"
@@ -128,7 +128,7 @@
</template> </template>
导入Token 导入Token
</n-button> </n-button>
<n-button <n-button
v-if="tokenStore.hasTokens" v-if="tokenStore.hasTokens"
size="large" size="large"
@@ -160,7 +160,7 @@
</template> </template>
添加Token 添加Token
</n-button> </n-button>
<n-dropdown <n-dropdown
:options="bulkOptions" :options="bulkOptions"
@select="handleBulkAction" @select="handleBulkAction"
@@ -174,13 +174,13 @@
</n-dropdown> </n-dropdown>
</div> </div>
</div> </div>
<div class="tokens-grid"> <div class="tokens-grid">
<div <div
v-for="token in tokenStore.gameTokens" v-for="token in tokenStore.gameTokens"
:key="token.id" :key="token.id"
class="token-card" class="token-card"
:class="{ :class="{
active: token.id === tokenStore.selectedTokenId, active: token.id === tokenStore.selectedTokenId,
connected: getConnectionStatus(token.id) === 'connected' connected: getConnectionStatus(token.id) === 'connected'
}" }"
@@ -206,7 +206,7 @@
>{{ token.profession }}</span> >{{ token.profession }}</span>
</div> </div>
</div> </div>
<div class="card-actions"> <div class="card-actions">
<n-dropdown <n-dropdown
:options="getTokenActions(token)" :options="getTokenActions(token)"
@@ -220,24 +220,24 @@
</n-dropdown> </n-dropdown>
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="token-display"> <div class="token-display">
<span class="token-label">Token:</span> <span class="token-label">Token:</span>
<code class="token-value">{{ maskToken(token.token) }}</code> <code class="token-value">{{ maskToken(token.token) }}</code>
</div> </div>
<div class="connection-status"> <div class="connection-status">
<div class="status-indicator"> <div class="status-indicator">
<span <span
class="status-dot" class="status-dot"
:class="getConnectionStatus(token.id)" :class="getConnectionStatus(token.id)"
/> />
<span class="status-text"> <span class="status-text">
{{ getConnectionStatusText(token.id) }} {{ getConnectionStatusText(token.id) }}
</span> </span>
</div> </div>
<n-button <n-button
size="small" size="small"
:type="getConnectionStatus(token.id) === 'connected' ? 'warning' : 'primary'" :type="getConnectionStatus(token.id) === 'connected' ? 'warning' : 'primary'"
@@ -246,7 +246,7 @@
{{ getConnectionStatus(token.id) === 'connected' ? '断开' : '连接' }} {{ getConnectionStatus(token.id) === 'connected' ? '断开' : '连接' }}
</n-button> </n-button>
</div> </div>
<div class="token-timestamps"> <div class="token-timestamps">
<div class="timestamp-item"> <div class="timestamp-item">
<span class="timestamp-label">创建</span> <span class="timestamp-label">创建</span>
@@ -258,7 +258,7 @@
</div> </div>
</div> </div>
</div> </div>
<div <div
v-if="token.id === tokenStore.selectedTokenId" v-if="token.id === tokenStore.selectedTokenId"
class="card-footer" class="card-footer"
@@ -342,7 +342,7 @@
<n-input v-model:value="editForm.wsUrl" /> <n-input v-model:value="editForm.wsUrl" />
</n-form-item> </n-form-item>
</n-form> </n-form>
<template #footer> <template #footer>
<div class="modal-actions"> <div class="modal-actions">
<n-button @click="showEditModal = false"> <n-button @click="showEditModal = false">
@@ -442,11 +442,11 @@ const bulkOptions = [
// 方法 // 方法
const handleImport = async () => { const handleImport = async () => {
if (!importFormRef.value) return if (!importFormRef.value) return
try { try {
await importFormRef.value.validate() await importFormRef.value.validate()
isImporting.value = true isImporting.value = true
const result = tokenStore.importBase64Token( const result = tokenStore.importBase64Token(
importForm.name, importForm.name,
importForm.base64Token, importForm.base64Token,
@@ -457,7 +457,7 @@ const handleImport = async () => {
wsUrl: importForm.wsUrl wsUrl: importForm.wsUrl
} }
) )
if (result.success) { if (result.success) {
message.success(result.message) message.success(result.message)
resetImportForm() resetImportForm()
@@ -503,7 +503,7 @@ const getConnectionStatusText = (tokenId) => {
const toggleConnection = (token) => { const toggleConnection = (token) => {
const status = getConnectionStatus(token.id) const status = getConnectionStatus(token.id)
if (status === 'connected') { if (status === 'connected') {
tokenStore.closeWebSocketConnection(token.id) tokenStore.closeWebSocketConnection(token.id)
message.info('WebSocket连接已断开') message.info('WebSocket连接已断开')
@@ -552,10 +552,10 @@ const editToken = (token) => {
const saveEdit = async () => { const saveEdit = async () => {
if (!editFormRef.value || !editingToken.value) return if (!editFormRef.value || !editingToken.value) return
try { try {
await editFormRef.value.validate() await editFormRef.value.validate()
tokenStore.updateToken(editingToken.value.id, { tokenStore.updateToken(editingToken.value.id, {
name: editForm.name, name: editForm.name,
server: editForm.server, server: editForm.server,
@@ -563,7 +563,7 @@ const saveEdit = async () => {
profession: editForm.profession, profession: editForm.profession,
wsUrl: editForm.wsUrl wsUrl: editForm.wsUrl
}) })
message.success('Token信息已更新') message.success('Token信息已更新')
showEditModal.value = false showEditModal.value = false
editingToken.value = null editingToken.value = null
@@ -627,12 +627,12 @@ const exportTokens = () => {
const data = tokenStore.exportTokens() const data = tokenStore.exportTokens()
const dataStr = JSON.stringify(data, null, 2) const dataStr = JSON.stringify(data, null, 2)
const dataBlob = new Blob([dataStr], { type: 'application/json' }) const dataBlob = new Blob([dataStr], { type: 'application/json' })
const link = document.createElement('a') const link = document.createElement('a')
link.href = URL.createObjectURL(dataBlob) link.href = URL.createObjectURL(dataBlob)
link.download = `tokens_backup_${new Date().toISOString().split('T')[0]}.json` link.download = `tokens_backup_${new Date().toISOString().split('T')[0]}.json`
link.click() link.click()
message.success('Token数据已导出') message.success('Token数据已导出')
} catch (error) { } catch (error) {
message.error('导出失败') message.error('导出失败')
@@ -709,7 +709,7 @@ const goToDashboard = () => {
// 生命周期 // 生命周期
onMounted(() => { onMounted(() => {
tokenStore.initTokenStore() tokenStore.initTokenStore()
// 如果没有token显示导入表单 // 如果没有token显示导入表单
if (!tokenStore.hasTokens) { if (!tokenStore.hasTokens) {
showImportForm.value = true showImportForm.value = true
@@ -782,7 +782,7 @@ onMounted(() => {
.card-header { .card-header {
text-align: center; text-align: center;
margin-bottom: var(--spacing-xl); margin-bottom: var(--spacing-xl);
h2 { h2 {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -792,16 +792,16 @@ onMounted(() => {
font-size: var(--font-size-xl); font-size: var(--font-size-xl);
margin-bottom: var(--spacing-sm); margin-bottom: var(--spacing-sm);
} }
p { p {
color: var(--text-secondary); color: var(--text-secondary);
margin: 0 0 var(--spacing-md) 0; margin: 0 0 var(--spacing-md) 0;
} }
.help-info { .help-info {
margin-top: var(--spacing-md); margin-top: var(--spacing-md);
text-align: left; text-align: left;
code { code {
background: rgba(24, 160, 88, 0.1); background: rgba(24, 160, 88, 0.1);
color: var(--success-color); color: var(--success-color);
@@ -838,7 +838,7 @@ onMounted(() => {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: var(--spacing-xl); margin-bottom: var(--spacing-xl);
h2 { h2 {
color: var(--text-primary); color: var(--text-primary);
font-size: var(--font-size-xl); font-size: var(--font-size-xl);
@@ -863,17 +863,17 @@ onMounted(() => {
padding: var(--spacing-lg); padding: var(--spacing-lg);
cursor: pointer; cursor: pointer;
transition: all var(--transition-normal); transition: all var(--transition-normal);
&:hover { &:hover {
box-shadow: var(--shadow-medium); box-shadow: var(--shadow-medium);
transform: translateY(-2px); transform: translateY(-2px);
} }
&.active { &.active {
border-color: var(--primary-color); border-color: var(--primary-color);
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1); box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
} }
&.connected { &.connected {
border-left: 4px solid var(--success-color); border-left: 4px solid var(--success-color);
} }
@@ -956,15 +956,15 @@ onMounted(() => {
height: 8px; height: 8px;
border-radius: 50%; border-radius: 50%;
background: var(--text-tertiary); background: var(--text-tertiary);
&.connected { &.connected {
background: var(--success-color); background: var(--success-color);
} }
&.connecting { &.connecting {
background: var(--warning-color); background: var(--warning-color);
} }
&.error { &.error {
background: var(--error-color); background: var(--error-color);
} }
@@ -1022,23 +1022,23 @@ onMounted(() => {
.container { .container {
padding: 0 var(--spacing-md); padding: 0 var(--spacing-md);
} }
.tokens-grid { .tokens-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.optional-fields { .optional-fields {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.section-header { .section-header {
flex-direction: column; flex-direction: column;
gap: var(--spacing-md); gap: var(--spacing-md);
align-items: stretch; align-items: stretch;
} }
.token-timestamps { .token-timestamps {
flex-direction: column; flex-direction: column;
} }
} }
</style> </style>