You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
12 KiB
12 KiB
07-个人中心页面(原型)
目标
实现 Web 端个人中心和健康档案管理页面原型。
前置要求
- 路由配置完成
- 认证状态 Store 已创建
- 体质状态 Store 已创建
实施步骤
步骤 1:个人中心页面
创建 src/views/profile/ProfileView.vue:
<template>
<div class="profile-page">
<!-- 用户信息卡片 -->
<el-card class="user-card">
<div class="user-info">
<el-avatar :size="80">
{{ authStore.user?.nickname?.charAt(0) || 'U' }}
</el-avatar>
<div class="user-text">
<h2>{{ authStore.user?.nickname || '用户' }}</h2>
<p>{{ authStore.user?.phone }}</p>
<el-tag v-if="constitutionStore.result" type="success">
{{ constitutionNames[constitutionStore.result.primaryType] }}
</el-tag>
</div>
<el-button :icon="Edit" circle @click="handleEdit" />
</div>
</el-card>
<!-- 健康管理 -->
<el-card class="menu-card">
<template #header>健康管理</template>
<div class="menu-list">
<div class="menu-item" @click="router.push('/profile/health-record')">
<div class="menu-icon" style="background: #ECFDF5">
<el-icon size="20" color="#10B981"><Document /></el-icon>
</div>
<div class="menu-text">
<h4>健康档案</h4>
<p>查看和管理您的健康信息</p>
</div>
<el-icon><ArrowRight /></el-icon>
</div>
<el-divider />
<div class="menu-item" @click="router.push('/constitution/result')">
<div class="menu-icon" style="background: #EDE9FE">
<el-icon size="20" color="#8B5CF6"><TrendCharts /></el-icon>
</div>
<div class="menu-text">
<h4>体质报告</h4>
<p>{{ constitutionStore.result ? `当前体质:${constitutionNames[constitutionStore.result.primaryType]}` : '暂无测评记录' }}</p>
</div>
<el-icon><ArrowRight /></el-icon>
</div>
<el-divider />
<div class="menu-item" @click="router.push('/chat')">
<div class="menu-icon" style="background: #DBEAFE">
<el-icon size="20" color="#3B82F6"><ChatDotRound /></el-icon>
</div>
<div class="menu-text">
<h4>对话历史</h4>
<p>查看AI咨询记录</p>
</div>
<el-icon><ArrowRight /></el-icon>
</div>
</div>
</el-card>
<!-- 其他设置 -->
<el-card class="menu-card">
<template #header>其他</template>
<div class="menu-list">
<div class="menu-item" @click="openMall">
<div class="menu-icon" style="background: #FEF3C7">
<el-icon size="20" color="#F59E0B"><Shop /></el-icon>
</div>
<div class="menu-text">
<h4>健康商城</h4>
<p>选购适合您的保健品</p>
</div>
<el-icon><ArrowRight /></el-icon>
</div>
<el-divider />
<div class="menu-item" @click="showAbout">
<div class="menu-icon" style="background: #F3F4F6">
<el-icon size="20" color="#6B7280"><InfoFilled /></el-icon>
</div>
<div class="menu-text">
<h4>关于我们</h4>
<p>了解健康AI助手</p>
</div>
<el-icon><ArrowRight /></el-icon>
</div>
</div>
</el-card>
<!-- 退出登录 -->
<el-button type="danger" plain class="logout-btn" @click="handleLogout">
退出登录
</el-button>
<p class="version">版本 1.0.0(原型版)</p>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Edit, Document, TrendCharts, ChatDotRound, Shop, InfoFilled, ArrowRight } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/auth'
import { useConstitutionStore } from '@/stores/constitution'
import { constitutionNames } from '@/mock/constitution'
const router = useRouter()
const authStore = useAuthStore()
const constitutionStore = useConstitutionStore()
const handleEdit = () => {
ElMessage.info('编辑功能将在后续版本中提供')
}
const openMall = () => {
window.open('https://mall.example.com')
}
const showAbout = () => {
ElMessageBox.alert(
'健康AI助手 v1.0.0<br><br>结合中医体质辨识理论,为您提供个性化健康建议。',
'关于我们',
{ dangerouslyUseHTMLString: true }
)
}
const handleLogout = () => {
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
authStore.logout()
router.push('/login')
})
}
</script>
<style scoped lang="scss">
.profile-page {
max-width: 600px;
margin: 0 auto;
}
.user-card {
margin-bottom: 20px;
.user-info {
display: flex;
align-items: center;
gap: 20px;
.el-avatar {
background: #10B981;
font-size: 28px;
}
.user-text {
flex: 1;
h2 {
font-size: 20px;
margin-bottom: 4px;
}
p {
color: #6B7280;
margin-bottom: 8px;
}
}
}
}
.menu-card {
margin-bottom: 20px;
.menu-list {
.menu-item {
display: flex;
align-items: center;
gap: 16px;
padding: 12px 0;
cursor: pointer;
&:hover {
background: #F9FAFB;
margin: 0 -20px;
padding: 12px 20px;
}
.menu-icon {
width: 44px;
height: 44px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.menu-text {
flex: 1;
h4 {
font-size: 15px;
margin-bottom: 2px;
}
p {
font-size: 13px;
color: #9CA3AF;
}
}
}
}
}
.logout-btn {
width: 100%;
margin-bottom: 16px;
}
.version {
text-align: center;
font-size: 12px;
color: #9CA3AF;
}
</style>
步骤 2:健康档案页面
创建 src/views/profile/HealthRecordView.vue:
<template>
<div class="health-record-page">
<el-page-header @back="router.back()">
<template #content>健康档案</template>
</el-page-header>
<!-- 基础信息 -->
<el-card class="info-card">
<template #header>
<div class="card-header">
<el-icon><User /></el-icon>
<span>基础信息</span>
</div>
</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="姓名">{{ mockProfile.name }}</el-descriptions-item>
<el-descriptions-item label="性别">{{ mockProfile.gender }}</el-descriptions-item>
<el-descriptions-item label="年龄">{{ mockProfile.age }}岁</el-descriptions-item>
<el-descriptions-item label="身高">{{ mockProfile.height }}cm</el-descriptions-item>
<el-descriptions-item label="体重">{{ mockProfile.weight }}kg</el-descriptions-item>
<el-descriptions-item label="BMI">{{ bmi }}</el-descriptions-item>
<el-descriptions-item label="血型">{{ mockProfile.bloodType }}</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 体质信息 -->
<el-card class="info-card">
<template #header>
<div class="card-header">
<el-icon><TrendCharts /></el-icon>
<span>体质信息</span>
</div>
</template>
<div v-if="constitutionStore.result" class="constitution-info">
<el-tag type="success" size="large">
{{ constitutionNames[constitutionStore.result.primaryType] }}
</el-tag>
<p class="constitution-desc">
{{ constitutionDescriptions[constitutionStore.result.primaryType].description }}
</p>
<p class="assessed-time">
测评时间:{{ formatTime(constitutionStore.result.assessedAt) }}
</p>
</div>
<el-empty v-else description="暂无体质测评记录">
<el-button type="primary" @click="router.push('/constitution')">
开始测评
</el-button>
</el-empty>
</el-card>
<!-- 既往病史 -->
<el-card class="info-card">
<template #header>
<div class="card-header">
<el-icon><FirstAidKit /></el-icon>
<span>既往病史</span>
</div>
</template>
<div class="tag-list">
<el-tag v-for="disease in mockProfile.medicalHistory" :key="disease">
{{ disease }}
</el-tag>
</div>
</el-card>
<!-- 过敏信息 -->
<el-card class="info-card">
<template #header>
<div class="card-header">
<el-icon color="#EF4444"><Warning /></el-icon>
<span>过敏信息</span>
</div>
</template>
<div class="tag-list">
<el-tag v-for="allergy in mockProfile.allergyRecords" :key="allergy" type="danger">
{{ allergy }}
</el-tag>
</div>
</el-card>
<!-- 生活习惯 -->
<el-card class="info-card">
<template #header>
<div class="card-header">
<el-icon><Clock /></el-icon>
<span>生活习惯</span>
</div>
</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="入睡时间">{{ mockProfile.sleepTime }}</el-descriptions-item>
<el-descriptions-item label="起床时间">{{ mockProfile.wakeTime }}</el-descriptions-item>
<el-descriptions-item label="运动频率">{{ mockProfile.exerciseFrequency }}</el-descriptions-item>
</el-descriptions>
</el-card>
<el-alert type="info" :closable="false" class="note-alert">
以上为模拟数据,后续将支持编辑和同步
</el-alert>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { User, TrendCharts, FirstAidKit, Warning, Clock } from '@element-plus/icons-vue'
import dayjs from 'dayjs'
import { useAuthStore } from '@/stores/auth'
import { useConstitutionStore } from '@/stores/constitution'
import { constitutionNames, constitutionDescriptions } from '@/mock/constitution'
const router = useRouter()
const authStore = useAuthStore()
const constitutionStore = useConstitutionStore()
// 模拟数据
const mockProfile = {
name: authStore.user?.nickname || '用户',
gender: '男',
age: 45,
height: 170,
weight: 68,
bloodType: 'A型',
medicalHistory: ['高血压', '轻度脂肪肝'],
allergyRecords: ['青霉素'],
sleepTime: '23:00',
wakeTime: '07:00',
exerciseFrequency: '每周2-3次'
}
const bmi = computed(() => {
const h = mockProfile.height / 100
return (mockProfile.weight / (h * h)).toFixed(1)
})
const formatTime = (time: string) => dayjs(time).format('YYYY-MM-DD HH:mm')
</script>
<style scoped lang="scss">
.health-record-page {
max-width: 800px;
margin: 0 auto;
}
.el-page-header {
margin-bottom: 20px;
}
.info-card {
margin-bottom: 20px;
.card-header {
display: flex;
align-items: center;
gap: 8px;
}
.constitution-info {
text-align: center;
padding: 20px;
.constitution-desc {
margin: 16px 0 8px;
color: #4B5563;
}
.assessed-time {
font-size: 12px;
color: #9CA3AF;
}
}
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
}
.note-alert {
margin-top: 20px;
}
</style>
验收标准
- 个人中心页面正常显示
- 菜单跳转正常
- 退出登录正常
- 健康档案页面正常显示
- 体质信息正确展示
预计耗时
30-35 分钟
完成
至此,Web 原型开发所有页面文档创建完成!
可以开始第四阶段:后端开发。