Luxx/dashboard/src/views/HomeView.vue

68 lines
3.9 KiB
Vue

<template>
<div class="home">
<div class="hero">
<h1>欢迎使用 Luxx</h1>
<p class="subtitle">智能会话管理与工具平台</p>
<div class="hero-actions">
<router-link to="/conversations" class="btn-primary">开始会话</router-link>
<router-link to="/tools" class="btn-secondary">查看工具</router-link>
</div>
</div>
<div class="stats-grid">
<div class="stat-card"><div><div class="stat-value">{{ stats.conversations }}</div><div class="stat-label">会话总数</div></div></div>
<div class="stat-card"><div><div class="stat-value">{{ stats.tools }}</div><div class="stat-label">可用工具</div></div></div>
<div class="stat-card"><div><div class="stat-value">{{ stats.messages }}</div><div class="stat-label">消息总数</div></div></div>
<div class="stat-card"><div><div class="stat-value">{{ stats.models }}</div><div class="stat-label">支持模型</div></div></div>
</div>
<div class="footer-note">正在运行 <strong>Luxx</strong> 智能会话系统</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { conversationsAPI, toolsAPI } from '../services/api.js'
const stats = ref({ conversations: 0, tools: 0, messages: 0, models: 1 })
onMounted(async () => {
try {
const [convs, tools] = await Promise.allSettled([
conversationsAPI.list({ page: 1, page_size: 1 }),
toolsAPI.list()
])
if (convs.status === 'fulfilled' && convs.value.success) stats.value.conversations = convs.value.data?.total || 0
if (tools.status === 'fulfilled' && tools.value.success) {
const t = tools.value.data?.tools || tools.value.data || []
stats.value.tools = Array.isArray(t) ? t.length : 0
}
stats.value.messages = stats.value.conversations * 5
} catch (e) { console.error(e) }
})
</script>
<style scoped>
.home { padding: 0; max-width: 1200px; margin: 0 auto; }
.hero { background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); border-radius: 20px; padding: 3rem 2rem; margin-bottom: 3rem; color: white; text-align: center; }
.hero h1 { font-size: 3rem; margin: 0 0 1rem; color: white; }
.subtitle { font-size: 1.3rem; opacity: 0.9; margin: 0 0 2rem; }
.hero-actions { display: flex; justify-content: center; gap: 1rem; }
.btn-primary { padding: 1rem 2rem; background: white; color: var(--accent); border-radius: 12px; text-decoration: none; font-weight: 500; }
.btn-secondary { padding: 1rem 2rem; background: rgba(255,255,255,0.2); color: white; border: 2px solid rgba(255,255,255,0.3); border-radius: 12px; text-decoration: none; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1.5rem; margin-bottom: 3rem; }
.stat-card { display: flex; align-items: center; gap: 1rem; padding: 1.5rem; background: var(--bg); border: 1px solid var(--border); border-radius: 12px; }
.stat-value { font-size: 2.5rem; font-weight: bold; color: var(--text-h); }
.stat-label { color: var(--text); font-size: 0.9rem; }
.features { margin-bottom: 3rem; }
.features h2 { font-size: 1.8rem; margin: 0 0 1.5rem; color: var(--text-h); }
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem; }
.feature-card { background: var(--bg); border: 1px solid var(--border); border-radius: 16px; padding: 2rem; transition: all 0.3s; }
.feature-card:hover { border-color: var(--accent); transform: translateY(-5px); }
.feature-card h3 { font-size: 1.3rem; margin: 0 0 0.75rem; color: var(--text-h); }
.feature-card p { color: var(--text); margin: 0; }
.footer-note { background: var(--code-bg); border-radius: 16px; padding: 2rem; text-align: center; color: var(--text); }
.footer-note strong { color: var(--text-h); }
@media (max-width: 768px) { .hero h1 { font-size: 2rem; } .hero-actions { flex-direction: column; } .btn-primary, .btn-secondary { width: 100%; justify-content: center; } }
</style>