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.
8.5 KiB
8.5 KiB
04-首页(原型)
目标
实现 Web 首页原型,展示用户体质信息、快捷入口和健康提示。
前置要求
- 路由和布局配置完成
- 模拟数据服务已创建
- 登录页面完成
实施步骤
创建体质状态 Store
创建 src/stores/constitution.ts:
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { ConstitutionResult } from '@/types'
export const useConstitutionStore = defineStore('constitution', () => {
const result = ref<ConstitutionResult | null>(null)
function setResult(data: ConstitutionResult) {
result.value = data
localStorage.setItem('constitution_result', JSON.stringify(data))
}
function clearResult() {
result.value = null
localStorage.removeItem('constitution_result')
}
// 初始化时从 localStorage 恢复
function init() {
const saved = localStorage.getItem('constitution_result')
if (saved) {
result.value = JSON.parse(saved)
}
}
init()
return { result, setResult, clearResult }
})
创建首页
创建 src/views/home/HomeView.vue:
<template>
<div class="home-page">
<!-- 体质卡片 -->
<el-card class="constitution-card" :body-style="{ padding: '24px' }">
<template v-if="constitutionStore.result">
<div class="constitution-header">
<span class="label">我的体质</span>
<el-link type="primary" @click="router.push('/constitution/result')">
查看详情 →
</el-link>
</div>
<div class="constitution-body">
<el-icon size="40" color="#10B981"><TrendCharts /></el-icon>
<div class="constitution-info">
<h2>{{ constitutionNames[constitutionStore.result.primaryType] }}</h2>
<p>{{ constitutionDescriptions[constitutionStore.result.primaryType].description }}</p>
</div>
</div>
</template>
<template v-else>
<div class="no-constitution" @click="router.push('/constitution')">
<el-icon size="48" color="#9CA3AF"><Document /></el-icon>
<p>还未进行体质测试</p>
<span>点击开始测试,了解您的体质类型</span>
</div>
</template>
</el-card>
<!-- 快捷入口 -->
<div class="quick-actions">
<el-card
v-for="action in quickActions"
:key="action.label"
class="action-card"
shadow="hover"
@click="action.onClick"
>
<div class="action-icon" :style="{ backgroundColor: action.bgColor }">
<el-icon :size="24" :color="action.color">
<component :is="action.icon" />
</el-icon>
</div>
<div class="action-text">
<h4>{{ action.label }}</h4>
<p>{{ action.desc }}</p>
</div>
</el-card>
</div>
<!-- 健康提示 -->
<el-card class="tip-card">
<div class="tip-content">
<el-icon size="24" color="#F59E0B"><Sunrise /></el-icon>
<div class="tip-text">
<h4>今日健康提示</h4>
<p>{{ healthTip }}</p>
</div>
</div>
</el-card>
<!-- 推荐产品 -->
<el-card class="products-card">
<template #header>
<div class="products-header">
<span>{{ constitutionStore.result ? '适合您的调养产品' : '热门保健品' }}</span>
<el-link type="primary" @click="openMall">查看更多 →</el-link>
</div>
</template>
<div class="products-list">
<div
v-for="product in recommendedProducts"
:key="product.id"
class="product-item"
@click="openMall(product.mallUrl)"
>
<div class="product-image">
<el-icon size="32" color="#10B981"><FirstAidKit /></el-icon>
</div>
<p class="product-name">{{ product.name }}</p>
<p class="product-price">¥{{ product.price }}</p>
</div>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { ChatDotRound, TrendCharts, Document, Sunrise, FirstAidKit, Shop } from '@element-plus/icons-vue'
import { useConstitutionStore } from '@/stores/constitution'
import { constitutionNames, constitutionDescriptions } from '@/mock/constitution'
import { getProductsByConstitution, mockProducts } from '@/mock/products'
const router = useRouter()
const constitutionStore = useConstitutionStore()
const quickActions = [
{
icon: ChatDotRound,
label: 'AI问诊',
desc: '24小时智能健康问答',
color: '#3B82F6',
bgColor: '#DBEAFE',
onClick: () => router.push('/chat')
},
{
icon: TrendCharts,
label: '体质测试',
desc: '科学分析您的体质类型',
color: '#10B981',
bgColor: '#ECFDF5',
onClick: () => router.push('/constitution')
},
{
icon: Document,
label: '健康档案',
desc: '查看个人健康记录',
color: '#8B5CF6',
bgColor: '#EDE9FE',
onClick: () => router.push('/profile/health-record')
},
{
icon: Shop,
label: '健康商城',
desc: '选购调养保健品',
color: '#F59E0B',
bgColor: '#FEF3C7',
onClick: () => window.open('https://mall.example.com')
}
]
const healthTip = computed(() => {
if (constitutionStore.result) {
return constitutionDescriptions[constitutionStore.result.primaryType].suggestions[0]
}
return '保持良好的作息习惯,每天喝足8杯水,适当运动有益身心健康。'
})
const recommendedProducts = computed(() => {
if (constitutionStore.result) {
return getProductsByConstitution(constitutionStore.result.primaryType)
}
return mockProducts.slice(0, 4)
})
const openMall = (url?: string) => {
window.open(url || 'https://mall.example.com')
}
</script>
<style scoped lang="scss">
.home-page {
display: grid;
gap: 20px;
}
.constitution-card {
.constitution-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.label {
color: #6B7280;
}
}
.constitution-body {
display: flex;
align-items: center;
gap: 16px;
h2 {
font-size: 24px;
color: #1F2937;
margin-bottom: 4px;
}
p {
color: #6B7280;
font-size: 14px;
}
}
.no-constitution {
text-align: center;
padding: 32px;
cursor: pointer;
&:hover {
background: #F9FAFB;
border-radius: 8px;
}
p {
margin: 12px 0 4px;
color: #6B7280;
}
span {
color: #9CA3AF;
font-size: 14px;
}
}
}
.quick-actions {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
.action-card {
cursor: pointer;
text-align: center;
padding: 8px;
.action-icon {
width: 56px;
height: 56px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 12px;
}
h4 {
font-size: 15px;
color: #1F2937;
margin-bottom: 4px;
}
p {
font-size: 12px;
color: #9CA3AF;
}
}
}
.tip-card {
background: #FFFBEB;
.tip-content {
display: flex;
align-items: flex-start;
gap: 12px;
h4 {
color: #92400E;
margin-bottom: 4px;
}
p {
color: #B45309;
font-size: 14px;
line-height: 1.5;
}
}
}
.products-card {
.products-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.products-list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
.product-item {
text-align: center;
padding: 16px;
border-radius: 12px;
cursor: pointer;
&:hover {
background: #F9FAFB;
}
.product-image {
width: 64px;
height: 64px;
background: #ECFDF5;
border-radius: 32px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 8px;
}
.product-name {
font-size: 13px;
color: #1F2937;
margin-bottom: 4px;
}
.product-price {
color: #EF4444;
font-weight: 600;
}
}
}
}
</style>
验收标准
- 首页 UI 正常显示
- 体质卡片显示正确
- 快捷入口点击跳转正常
- 健康提示显示正常
- 推荐产品显示正常
预计耗时
30-35 分钟
下一步
完成后进入 03-Web原型开发/05-体质辨识页面.md