Files
happy-life-star/web/src/views/Home.vue
T

570 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="home-container">
<!-- 导航栏 -->
<header class="header glass">
<div class="header-content">
<div class="logo">
<h1 class="gradient-text">情绪博物馆</h1>
<span class="subtitle">AI心理健康助手</span>
</div>
<nav class="nav-menu">
<a-button type="text" class="nav-item" @click="$router.push('/chat')">
<MessageOutlined />
AI对话
</a-button>
<a-button type="text" class="nav-item" @click="$router.push('/history')">
<HistoryOutlined />
历史记录
</a-button>
<a-button type="text" class="nav-item" @click="$router.push('/analysis')">
<BarChartOutlined />
情绪分析
</a-button>
<!-- 用户状态区域 -->
<div class="user-area">
<template v-if="userStore.isLoggedIn">
<a-dropdown>
<a-button type="text" class="nav-item user-btn">
<UserOutlined />
{{ userStore.userInfo.username || userStore.userInfo.account }}
</a-button>
<template #overlay>
<a-menu>
<a-menu-item key="profile">
<UserOutlined />
个人资料
</a-menu-item>
<a-menu-divider />
<a-menu-item key="logout" @click="handleLogout">
<LogoutOutlined />
退出登录
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
<template v-else>
<a-button type="text" class="nav-item" @click="$router.push('/login')">
<LoginOutlined />
登录
</a-button>
</template>
</div>
</nav>
</div>
</header>
<!-- 主要内容 -->
<main class="main-content">
<div class="hero-section">
<div class="hero-content fade-in-up">
<h2 class="hero-title">
欢迎来到情绪博物馆
</h2>
<p class="hero-description">
您的专属AI心理健康助手提供24/7情绪支持心理分析和个性化建议
</p>
<div class="hero-actions">
<a-button
type="primary"
size="large"
class="start-chat-btn"
@click="startChat"
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; margin-right: 16px;"
>
<MessageOutlined />
开始对话
</a-button>
<a-button
size="large"
class="learn-more-btn"
@click="scrollToFeatures"
style="background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.3); color: white;"
>
了解更多
</a-button>
</div>
</div>
<!-- 装饰性元素 -->
<div class="hero-decoration">
<div class="floating-card card bounce-in" style="animation-delay: 0.2s">
<HeartOutlined class="icon" />
<span>情绪识别</span>
</div>
<div class="floating-card card bounce-in" style="animation-delay: 0.4s">
<BulbOutlined class="icon" />
<span>智能建议</span>
</div>
<div class="floating-card card bounce-in" style="animation-delay: 0.6s">
<SafetyOutlined class="icon" />
<span>隐私保护</span>
</div>
</div>
</div>
<!-- 功能特性 -->
<section class="features-section" ref="featuresRef">
<div class="section-header">
<h3 class="section-title gradient-text">核心功能</h3>
<p class="section-description">专业的AI技术贴心的情绪关怀</p>
</div>
<div class="features-grid">
<div class="feature-card card" v-for="feature in features" :key="feature.id">
<div class="feature-icon">
<component :is="feature.icon" />
</div>
<h4 class="feature-title">{{ feature.title }}</h4>
<p class="feature-description">{{ feature.description }}</p>
</div>
</div>
</section>
<!-- 统计数据 -->
<section class="stats-section">
<div class="stats-container glass">
<div class="stat-item" v-for="stat in stats" :key="stat.label">
<div class="stat-number gradient-text">{{ stat.value }}</div>
<div class="stat-label">{{ stat.label }}</div>
</div>
</div>
</section>
<!-- API测试组件 (仅开发环境) -->
<section v-if="showApiTest" class="api-test-section">
<ApiTest />
</section>
</main>
<!-- 页脚 -->
<footer class="footer">
<div class="footer-content">
<p>&copy; 2025 情绪博物馆. 用心守护每一份情绪</p>
</div>
</footer>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import {
MessageOutlined,
HistoryOutlined,
BarChartOutlined,
HeartOutlined,
BulbOutlined,
SafetyOutlined,
RobotOutlined,
LineChartOutlined,
ClockCircleOutlined,
LockOutlined,
UserOutlined,
LoginOutlined,
LogoutOutlined
} from '@ant-design/icons-vue'
import { ENV_CONFIG } from '@/config/env'
import { useUserStore } from '@/stores/user'
import ApiTest from '@/components/ApiTest.vue'
const router = useRouter()
const userStore = useUserStore()
const featuresRef = ref(null)
// 是否显示API测试组件 (仅开发环境)
const showApiTest = computed(() => ENV_CONFIG.isDevelopment)
// 功能特性数据
const features = ref([
{
id: 1,
icon: RobotOutlined,
title: 'AI智能对话',
description: '基于先进的自然语言处理技术,提供自然流畅的对话体验'
},
{
id: 2,
icon: LineChartOutlined,
title: '情绪分析',
description: '实时分析您的情绪状态,提供专业的心理健康评估'
},
{
id: 3,
icon: ClockCircleOutlined,
title: '24/7支持',
description: '全天候在线服务,随时随地为您提供情绪支持和心理疏导'
},
{
id: 4,
icon: LockOutlined,
title: '隐私保护',
description: '严格保护用户隐私,所有对话内容都经过加密处理'
}
])
// 统计数据
const stats = ref([
{ value: '10,000+', label: '用户信赖' },
{ value: '50,000+', label: '对话次数' },
{ value: '95%', label: '满意度' },
{ value: '24/7', label: '在线服务' }
])
// 开始对话
const startChat = () => {
console.log('开始对话按钮被点击')
router.push('/chat')
}
// 滚动到功能区域
const scrollToFeatures = () => {
featuresRef.value?.scrollIntoView({ behavior: 'smooth' })
}
// 处理登出
const handleLogout = async () => {
try {
await userStore.logout()
message.success('已退出登录')
} catch (error) {
console.error('登出失败:', error)
message.error('登出失败')
}
}
onMounted(() => {
// 页面加载动画
document.body.style.overflow = 'hidden'
setTimeout(() => {
document.body.style.overflow = 'auto'
}, 1000)
})
</script>
<style lang="scss" scoped>
.home-container {
min-height: 100vh;
background: var(--gradient-primary);
position: relative;
overflow-x: hidden;
}
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
padding: var(--spacing-md) 0;
.header-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--spacing-lg);
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
h1 {
font-size: 24px;
margin: 0;
}
.subtitle {
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
margin-left: var(--spacing-sm);
}
}
.nav-menu {
display: flex;
align-items: center;
gap: var(--spacing-lg);
.nav-item {
color: rgba(255, 255, 255, 0.9) !important;
border: none !important;
box-shadow: none !important;
background: transparent !important;
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius-small);
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: var(--spacing-xs);
&:hover {
background: rgba(255, 255, 255, 0.1) !important;
color: white !important;
}
}
.user-area {
margin-left: var(--spacing-md);
.user-btn {
background: rgba(255, 255, 255, 0.1) !important;
border: 1px solid rgba(255, 255, 255, 0.2) !important;
&:hover {
background: rgba(255, 255, 255, 0.2) !important;
}
}
}
}
}
.main-content {
padding-top: 80px;
}
.hero-section {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: var(--spacing-xxl) var(--spacing-lg);
.hero-content {
text-align: center;
max-width: 600px;
color: white;
.hero-title {
font-size: 48px;
font-weight: 700;
margin-bottom: var(--spacing-lg);
line-height: 1.2;
}
.hero-description {
font-size: 18px;
margin-bottom: var(--spacing-xxl);
opacity: 0.9;
line-height: 1.6;
}
.hero-actions {
display: flex;
gap: var(--spacing-md);
justify-content: center;
flex-wrap: wrap;
.start-chat-btn {
height: 50px;
padding: 0 var(--spacing-xl);
font-size: 16px;
}
.learn-more-btn {
height: 50px;
padding: 0 var(--spacing-xl);
font-size: 16px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.3);
color: white;
&:hover {
background: rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.5);
}
}
}
}
.hero-decoration {
position: absolute;
top: 50%;
right: 10%;
transform: translateY(-50%);
.floating-card {
position: absolute;
padding: var(--spacing-md);
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
display: flex;
align-items: center;
gap: var(--spacing-sm);
white-space: nowrap;
.icon {
font-size: 20px;
}
&:nth-child(1) {
top: -60px;
right: 0;
}
&:nth-child(2) {
top: 20px;
right: -40px;
}
&:nth-child(3) {
top: 100px;
right: 20px;
}
}
}
}
.features-section {
padding: var(--spacing-xxl) var(--spacing-lg);
background: rgba(255, 255, 255, 0.05);
.section-header {
text-align: center;
margin-bottom: var(--spacing-xxl);
color: white;
.section-title {
font-size: 36px;
margin-bottom: var(--spacing-md);
}
.section-description {
font-size: 16px;
opacity: 0.8;
}
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-xl);
max-width: 1000px;
margin: 0 auto;
.feature-card {
text-align: center;
background: rgba(255, 255, 255, 0.95);
.feature-icon {
font-size: 48px;
color: var(--primary-color);
margin-bottom: var(--spacing-md);
}
.feature-title {
font-size: 20px;
margin-bottom: var(--spacing-md);
color: var(--text-primary);
}
.feature-description {
color: var(--text-secondary);
line-height: 1.6;
}
}
}
}
.stats-section {
padding: var(--spacing-xxl) var(--spacing-lg);
.stats-container {
max-width: 800px;
margin: 0 auto;
padding: var(--spacing-xl);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: var(--spacing-xl);
text-align: center;
.stat-item {
.stat-number {
font-size: 36px;
font-weight: 700;
margin-bottom: var(--spacing-sm);
}
.stat-label {
color: rgba(255, 255, 255, 0.8);
font-size: 14px;
}
}
}
}
.api-test-section {
padding: var(--spacing-xxl) var(--spacing-lg);
background: rgba(255, 255, 255, 0.05);
:deep(.ant-card) {
background: rgba(255, 255, 255, 0.95);
border: none;
border-radius: var(--border-radius-large);
box-shadow: var(--shadow-large);
}
}
.footer {
padding: var(--spacing-xl) var(--spacing-lg);
background: rgba(0, 0, 0, 0.2);
.footer-content {
text-align: center;
color: rgba(255, 255, 255, 0.7);
}
}
// 响应式设计
@media (max-width: 768px) {
.header {
.header-content {
padding: 0 var(--spacing-md);
}
.nav-menu {
gap: var(--spacing-md);
.nav-item {
padding: var(--spacing-xs) var(--spacing-sm);
font-size: 14px;
}
}
}
.hero-section {
.hero-content {
.hero-title {
font-size: 32px;
}
.hero-description {
font-size: 16px;
}
}
.hero-decoration {
display: none;
}
}
.features-section {
.features-grid {
grid-template-columns: 1fr;
gap: var(--spacing-lg);
}
}
.stats-section {
.stats-container {
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-lg);
}
}
}
</style>