973 lines
20 KiB
Vue
973 lines
20 KiB
Vue
<template>
|
|
<view class="page">
|
|
<view class="sticky-bar">
|
|
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
|
<view class="segment">
|
|
<view
|
|
v-for="tab in tabs"
|
|
:key="tab.value"
|
|
class="segment-item"
|
|
:class="{ 'segment-active': currentTab === tab.value }"
|
|
@tap="currentTab = tab.value"
|
|
>
|
|
{{ tab.label }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="insight-card">
|
|
<view class="insight-icon">✨</view>
|
|
<view class="insight-content">
|
|
<text class="insight-title">每周洞察</text>
|
|
<text class="insight-desc">{{ insightText }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="section">
|
|
<view class="section-header">
|
|
<view>
|
|
<text class="section-title">吸烟趋势</text>
|
|
<text class="section-sub">{{ trendRangeText }}</text>
|
|
</view>
|
|
<view class="status-chip" :class="statusChipClass">
|
|
<view class="status-dot"></view>
|
|
<text class="status-text">{{ statusText }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="trend-card">
|
|
<view class="trend-header">
|
|
<text class="trend-label">日均吸烟量</text>
|
|
<view class="trend-value-row">
|
|
<text class="trend-value">{{ averageCount }}</text>
|
|
<text class="trend-unit">支/天</text>
|
|
</view>
|
|
</view>
|
|
<view class="trend-chart">
|
|
<view v-for="(item, index) in trendItems" :key="index" class="trend-bar-item">
|
|
<view v-if="item.isHighlight" class="trend-bubble">{{ item.count }}</view>
|
|
<view class="trend-bar" :style="{ height: item.height }"></view>
|
|
<text class="trend-bar-label" :class="{ 'trend-bar-label-active': item.isHighlight }">{{ item.label }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="section">
|
|
<text class="section-title">健康与储蓄</text>
|
|
|
|
<view class="savings-card">
|
|
<view class="savings-header">
|
|
<view class="savings-icon" :class="moneyIconClass">
|
|
<view class="icon-coin"></view>
|
|
</view>
|
|
<text class="savings-title">节省金额</text>
|
|
</view>
|
|
<view class="savings-body">
|
|
<view class="savings-left">
|
|
<text class="savings-value">{{ savedMoneyText }}</text>
|
|
<view class="savings-bar">
|
|
<view class="savings-bar-fill" :style="{ width: moneyPercent + '%' }"></view>
|
|
</view>
|
|
<text class="savings-sub">目标 ¥{{ moneyTargetYuan }} ({{ moneyPercent }}%)</text>
|
|
</view>
|
|
<view class="savings-ring" :style="moneyRingStyle">
|
|
<view class="savings-ring-inner">
|
|
<text class="ring-value">{{ moneyPercent }}%</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="health-card">
|
|
<view class="health-header">
|
|
<view class="health-title-row">
|
|
<view class="health-icon" :class="healthIconClass">
|
|
<view class="icon-plus-vertical"></view>
|
|
<view class="icon-plus-horizontal"></view>
|
|
</view>
|
|
<text class="health-title">健康恢复里程碑</text>
|
|
</view>
|
|
<view class="health-badge">{{ healthStatusText }}</view>
|
|
</view>
|
|
<view class="health-list">
|
|
<view v-for="(item, index) in healthItems" :key="index" class="health-item">
|
|
<view class="health-item-row">
|
|
<text class="health-item-name">{{ item.name }}</text>
|
|
<text class="health-item-percent" :class="{ 'health-item-percent-muted': item.percent < 100 }">{{ item.percent }}%</text>
|
|
</view>
|
|
<view class="health-bar">
|
|
<view
|
|
class="health-bar-fill"
|
|
:class="{ 'health-bar-fill-muted': item.percent < 100 }"
|
|
:style="{ width: item.percent + '%' }"
|
|
></view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="stats-grid">
|
|
<view class="mini-card">
|
|
<view class="mini-icon mini-icon-fire" :class="streakIconClass">
|
|
<view class="icon-flame"></view>
|
|
</view>
|
|
<text class="mini-label">连续记录</text>
|
|
<view class="mini-value-row">
|
|
<text class="mini-value">{{ streakDays }}</text>
|
|
<text class="mini-unit">天</text>
|
|
</view>
|
|
<text class="mini-sub">保持未吸烟</text>
|
|
</view>
|
|
<view class="mini-card">
|
|
<view class="mini-icon mini-icon-block" :class="resistedIconClass">
|
|
<view class="icon-shield"></view>
|
|
</view>
|
|
<text class="mini-label">已拒绝</text>
|
|
<view class="mini-value-row">
|
|
<text class="mini-value">{{ resistedTotal }}</text>
|
|
<text class="mini-unit">次</text>
|
|
</view>
|
|
<text class="mini-sub">成功抵抗烟瘾</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted, watch } from 'vue'
|
|
import { useLogin } from '@/hooks/useLogin'
|
|
import * as api from '@/api'
|
|
|
|
const { waitForLogin } = useLogin()
|
|
const statusBarHeight = ref(0)
|
|
|
|
const tabs = [
|
|
{ label: '周', value: 'week' },
|
|
{ label: '月', value: 'month' },
|
|
{ label: '年', value: 'year' }
|
|
]
|
|
|
|
const currentTab = ref('week')
|
|
const statsData = ref(null)
|
|
|
|
const insightText = computed(() => {
|
|
const change = statsData.value?.change_percent
|
|
if (change === undefined || change === null) {
|
|
return '前5天保持完成记录!周日虽有小量吸烟,但整体趋势依然可控,下周继续保持。'
|
|
}
|
|
if (change <= 0) {
|
|
return '本阶段总体保持良好,趋势持续向好,继续保持节奏。'
|
|
}
|
|
return '近期吸烟量略有上升,试着减少高峰时段的冲动。'
|
|
})
|
|
|
|
const trendRangeText = computed(() => {
|
|
const start = statsData.value?.start
|
|
const end = statsData.value?.end
|
|
if (!start || !end) {
|
|
return ''
|
|
}
|
|
return formatRangeText(start, end)
|
|
})
|
|
|
|
const statusText = computed(() => {
|
|
const change = statsData.value?.change_percent
|
|
if (change === undefined || change === null) return '总体良好'
|
|
return change <= 0 ? '总体良好' : '有所增加'
|
|
})
|
|
|
|
const statusChipClass = computed(() => {
|
|
const change = statsData.value?.change_percent
|
|
if (change === undefined || change === null) return 'status-good'
|
|
return change <= 0 ? 'status-good' : 'status-warn'
|
|
})
|
|
|
|
const averageCount = computed(() => {
|
|
const avg = statsData.value?.daily_average
|
|
if (avg === undefined || avg === null) return 1
|
|
return avg
|
|
})
|
|
|
|
const trendItems = computed(() => {
|
|
const trend = statsData.value?.trend
|
|
const trendUnit = statsData.value?.trend_unit
|
|
if (!trend || trend.length === 0) {
|
|
return [
|
|
{ label: '19日', count: 0, height: '6%', isHighlight: false },
|
|
{ label: '20日', count: 0, height: '6%', isHighlight: false },
|
|
{ label: '21日', count: 0, height: '6%', isHighlight: false },
|
|
{ label: '22日', count: 0, height: '6%', isHighlight: false },
|
|
{ label: '23日', count: 0, height: '6%', isHighlight: false },
|
|
{ label: '24日', count: 0, height: '6%', isHighlight: false },
|
|
{ label: '25日', count: 6, height: '85%', isHighlight: true }
|
|
]
|
|
}
|
|
const maxCount = Math.max(...trend.map(item => item.count), 1)
|
|
return trend.map((item, index) => {
|
|
const height = `${Math.max((item.count / maxCount) * 100, 6)}%`
|
|
return {
|
|
label: formatTrendLabel(item.label, trendUnit),
|
|
count: item.count,
|
|
height,
|
|
isHighlight: index === trend.length - 1
|
|
}
|
|
})
|
|
})
|
|
|
|
const savedMoneyText = computed(() => {
|
|
const money = statsData.value?.money
|
|
if (!money || !money.available) {
|
|
return '¥0.00'
|
|
}
|
|
return `¥${(money.saved_cent / 100).toFixed(2)}`
|
|
})
|
|
|
|
|
|
const moneyTargetCent = computed(() => {
|
|
const money = statsData.value?.money
|
|
if (!money || !money.available) return 0
|
|
const { expected_total, pack_price_cent, cigs_per_pack } = money
|
|
if (!expected_total || !pack_price_cent || !cigs_per_pack) return 0
|
|
return Math.round((expected_total / cigs_per_pack) * pack_price_cent)
|
|
})
|
|
|
|
const moneyPercent = computed(() => {
|
|
const money = statsData.value?.money
|
|
const target = moneyTargetCent.value
|
|
if (!money || !money.available || target <= 0) {
|
|
return 0
|
|
}
|
|
const percent = Math.round((money.saved_cent / target) * 100)
|
|
return Math.min(Math.max(percent, 0), 100)
|
|
})
|
|
|
|
const moneyTargetYuan = computed(() => {
|
|
const target = moneyTargetCent.value
|
|
if (target <= 0) return '0'
|
|
return (target / 100).toFixed(0)
|
|
})
|
|
|
|
const moneyRingStyle = computed(() => {
|
|
const percent = moneyPercent.value
|
|
const angle = Math.round(percent * 3.6)
|
|
return {
|
|
background: `conic-gradient(#F59E0B 0deg ${angle}deg, #F1F5F9 ${angle}deg 360deg)`
|
|
}
|
|
})
|
|
|
|
const healthStatusText = computed(() => {
|
|
const health = statsData.value?.health
|
|
if (!health || !health.available) return '进行中'
|
|
return '进行中'
|
|
})
|
|
|
|
|
|
const healthItems = computed(() => {
|
|
const health = statsData.value?.health
|
|
if (!health || !health.available || !health.milestones || health.milestones.length === 0) {
|
|
return [
|
|
{ name: '心率血压恢复正常', percent: 100 },
|
|
{ name: '血氧水平恢复', percent: 100 },
|
|
{ name: '一氧化碳排出', percent: 95 },
|
|
{ name: '嗅觉味觉改善', percent: 40 },
|
|
{ name: '肺部功能恢复', percent: 10 }
|
|
]
|
|
}
|
|
const minutes = health.smoke_free_minutes || 0
|
|
return health.milestones.map(item => {
|
|
if (item.reached) {
|
|
return { name: item.name, percent: 100 }
|
|
}
|
|
const baseMinutes = item.minutes || 1
|
|
const percent = Math.min(Math.round((minutes / baseMinutes) * 100), 100)
|
|
return { name: item.name, percent }
|
|
})
|
|
})
|
|
|
|
const streakDays = computed(() => statsData.value?.streak_days ?? 12)
|
|
const resistedTotal = computed(() => statsData.value?.resisted_total ?? 24)
|
|
|
|
const moneyIconClass = computed(() => {
|
|
const money = statsData.value?.money
|
|
if (!money || !money.available) return 'icon-muted'
|
|
if (moneyPercent.value >= 60) return 'icon-strong'
|
|
if (moneyPercent.value >= 30) return 'icon-mid'
|
|
return 'icon-low'
|
|
})
|
|
|
|
const healthIconClass = computed(() => {
|
|
const health = statsData.value?.health
|
|
if (!health || !health.available) return 'icon-muted'
|
|
const minutes = health.smoke_free_minutes || 0
|
|
if (minutes >= 1440) return 'icon-strong'
|
|
if (minutes >= 120) return 'icon-mid'
|
|
return 'icon-low'
|
|
})
|
|
|
|
const streakIconClass = computed(() => {
|
|
if (streakDays.value <= 0) return 'icon-muted'
|
|
if (streakDays.value >= 7) return 'icon-strong'
|
|
if (streakDays.value >= 3) return 'icon-mid'
|
|
return 'icon-low'
|
|
})
|
|
|
|
const resistedIconClass = computed(() => {
|
|
if (resistedTotal.value <= 0) return 'icon-muted'
|
|
if (resistedTotal.value >= 10) return 'icon-strong'
|
|
if (resistedTotal.value >= 5) return 'icon-mid'
|
|
return 'icon-low'
|
|
})
|
|
|
|
function formatTrendLabel(label, unit) {
|
|
if (!label) return ''
|
|
if (unit === 'month') {
|
|
const parts = label.split('-')
|
|
if (parts.length >= 2) {
|
|
return `${parseInt(parts[1], 10)}月`
|
|
}
|
|
return label
|
|
}
|
|
if (label.includes('-')) {
|
|
const parts = label.split('-')
|
|
const day = parts[2] || parts[1]
|
|
if (day) {
|
|
return `${parseInt(day, 10)}日`
|
|
}
|
|
}
|
|
return label
|
|
}
|
|
|
|
function formatRangeText(start, end) {
|
|
const startParts = start.split('-')
|
|
const endParts = end.split('-')
|
|
if (startParts.length < 3 || endParts.length < 3) return `${start} - ${end}`
|
|
const startMonth = parseInt(startParts[1], 10)
|
|
const startDay = parseInt(startParts[2], 10)
|
|
const endMonth = parseInt(endParts[1], 10)
|
|
const endDay = parseInt(endParts[2], 10)
|
|
if (startMonth === endMonth) {
|
|
return `${startMonth}月${startDay}日-${endDay}日`
|
|
}
|
|
return `${startMonth}月${startDay}日-${endMonth}月${endDay}日`
|
|
}
|
|
|
|
async function fetchStats() {
|
|
try {
|
|
await waitForLogin()
|
|
const res = await api.getStats({ range: currentTab.value })
|
|
statsData.value = res.data
|
|
} catch (e) {
|
|
console.error('fetchStats error:', e)
|
|
}
|
|
}
|
|
|
|
watch(currentTab, () => {
|
|
fetchStats()
|
|
})
|
|
|
|
onMounted(() => {
|
|
const sys = uni.getSystemInfoSync()
|
|
statusBarHeight.value = Math.max((sys.statusBarHeight || 0) - 20, 0)
|
|
fetchStats()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page {
|
|
min-height: 100vh;
|
|
background: linear-gradient(to bottom, #D1FAE5 0%, #F0FDF4 45%, #FFFFFF 100%);
|
|
padding: 0 32rpx 200rpx;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.status-bar {
|
|
background: linear-gradient(to bottom, #D1FAE5, #F0FDF4);
|
|
height: 0;
|
|
}
|
|
|
|
.sticky-bar {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 20;
|
|
background: linear-gradient(to bottom, #D1FAE5 0%, #F0FDF4 70%, rgba(255, 255, 255, 0.95) 100%);
|
|
padding-bottom: 8rpx;
|
|
}
|
|
|
|
.segment {
|
|
display: flex;
|
|
background-color: #FFFFFF;
|
|
padding: 6rpx;
|
|
border-radius: 20rpx;
|
|
gap: 6rpx;
|
|
margin-bottom: 16rpx;
|
|
border: 2rpx solid #ECFDF3;
|
|
box-shadow: 0 10rpx 22rpx rgba(16, 185, 129, 0.08);
|
|
}
|
|
|
|
.segment-item {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 14rpx 0;
|
|
font-size: 24rpx;
|
|
font-weight: 600;
|
|
color: #64748B;
|
|
border-radius: 16rpx;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.segment-active {
|
|
background-color: #10B981;
|
|
color: #0B2F23;
|
|
box-shadow: 0 6rpx 16rpx rgba(16, 185, 129, 0.25);
|
|
}
|
|
|
|
.insight-card {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
align-items: flex-start;
|
|
background-color: #ECFDF3;
|
|
border: 2rpx solid #D9FBE7;
|
|
border-radius: 24rpx;
|
|
padding: 24rpx;
|
|
margin-bottom: 28rpx;
|
|
box-shadow: 0 10rpx 20rpx rgba(16, 185, 129, 0.1);
|
|
}
|
|
|
|
.insight-icon {
|
|
width: 64rpx;
|
|
height: 64rpx;
|
|
border-radius: 50%;
|
|
background-color: #D9FBE7;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 30rpx;
|
|
}
|
|
|
|
.insight-title {
|
|
font-size: 26rpx;
|
|
font-weight: 600;
|
|
color: #0F172A;
|
|
display: block;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.insight-desc {
|
|
font-size: 24rpx;
|
|
color: #475569;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.section {
|
|
margin-bottom: 32rpx;
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 16rpx;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 30rpx;
|
|
font-weight: 600;
|
|
color: #111827;
|
|
}
|
|
|
|
.section-sub {
|
|
font-size: 22rpx;
|
|
color: #94A3B8;
|
|
margin-top: 6rpx;
|
|
display: block;
|
|
}
|
|
|
|
.status-chip {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
padding: 8rpx 16rpx;
|
|
border-radius: 999rpx;
|
|
font-size: 22rpx;
|
|
font-weight: 600;
|
|
border: 2rpx solid transparent;
|
|
}
|
|
|
|
.status-good {
|
|
background-color: #E8FFF1;
|
|
color: #16A34A;
|
|
border-color: #BBF7D0;
|
|
}
|
|
|
|
.status-warn {
|
|
background-color: #FEF3C7;
|
|
color: #D97706;
|
|
border-color: #FDE68A;
|
|
}
|
|
|
|
.status-dot {
|
|
width: 10rpx;
|
|
height: 10rpx;
|
|
border-radius: 50%;
|
|
background-color: currentColor;
|
|
}
|
|
|
|
.status-text {
|
|
font-size: 22rpx;
|
|
}
|
|
|
|
.trend-card {
|
|
background-color: #FFFFFF;
|
|
border-radius: 28rpx;
|
|
padding: 24rpx;
|
|
border: 2rpx solid #ECFDF3;
|
|
box-shadow: 0 12rpx 24rpx rgba(16, 185, 129, 0.1);
|
|
}
|
|
|
|
.trend-header {
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.trend-label {
|
|
font-size: 24rpx;
|
|
color: #94A3B8;
|
|
display: block;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
.trend-value-row {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 8rpx;
|
|
}
|
|
|
|
.trend-value {
|
|
font-size: 56rpx;
|
|
font-weight: 700;
|
|
color: #111827;
|
|
}
|
|
|
|
.trend-unit {
|
|
font-size: 24rpx;
|
|
color: #94A3B8;
|
|
}
|
|
|
|
.trend-chart {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: space-between;
|
|
height: 240rpx;
|
|
gap: 12rpx;
|
|
}
|
|
|
|
.trend-bar-item {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
position: relative;
|
|
}
|
|
|
|
.trend-bar {
|
|
width: 32rpx;
|
|
background: linear-gradient(180deg, #34D399 0%, #10B981 100%);
|
|
border-radius: 12rpx 12rpx 8rpx 8rpx;
|
|
}
|
|
|
|
.trend-bubble {
|
|
position: absolute;
|
|
top: 0;
|
|
transform: translateY(-12rpx);
|
|
background-color: #0F766E;
|
|
color: #FFFFFF;
|
|
font-size: 20rpx;
|
|
padding: 4rpx 10rpx;
|
|
border-radius: 12rpx;
|
|
}
|
|
|
|
.trend-bar-label {
|
|
margin-top: 12rpx;
|
|
font-size: 22rpx;
|
|
color: #94A3B8;
|
|
}
|
|
|
|
.trend-bar-label-active {
|
|
color: #16A34A;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.savings-card {
|
|
background-color: #FFFFFF;
|
|
border-radius: 28rpx;
|
|
padding: 24rpx;
|
|
margin-top: 20rpx;
|
|
border: 2rpx solid #FEF3C7;
|
|
box-shadow: 0 12rpx 24rpx rgba(245, 158, 11, 0.08);
|
|
}
|
|
|
|
.savings-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12rpx;
|
|
margin-bottom: 16rpx;
|
|
}
|
|
|
|
.savings-icon {
|
|
width: 32rpx;
|
|
height: 32rpx;
|
|
border-radius: 8rpx;
|
|
background-color: #FDE68A;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #D97706;
|
|
}
|
|
|
|
.savings-icon.icon-muted {
|
|
background-color: #F1F5F9;
|
|
color: #94A3B8;
|
|
}
|
|
|
|
.savings-icon.icon-low {
|
|
color: #E59E0B;
|
|
}
|
|
|
|
.savings-icon.icon-mid {
|
|
color: #D97706;
|
|
}
|
|
|
|
.savings-icon.icon-strong {
|
|
color: #B45309;
|
|
}
|
|
|
|
.icon-coin {
|
|
width: 18rpx;
|
|
height: 12rpx;
|
|
border-radius: 4rpx;
|
|
border: 3rpx solid currentColor;
|
|
border-top-width: 6rpx;
|
|
}
|
|
|
|
.savings-title {
|
|
font-size: 26rpx;
|
|
font-weight: 600;
|
|
color: #111827;
|
|
}
|
|
|
|
.savings-body {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.savings-left {
|
|
flex: 1;
|
|
}
|
|
|
|
.savings-value {
|
|
font-size: 44rpx;
|
|
font-weight: 700;
|
|
color: #111827;
|
|
margin-bottom: 16rpx;
|
|
display: block;
|
|
}
|
|
|
|
.savings-bar {
|
|
height: 10rpx;
|
|
background-color: #F1F5F9;
|
|
border-radius: 999rpx;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.savings-bar-fill {
|
|
height: 100%;
|
|
background-color: #F59E0B;
|
|
border-radius: 999rpx;
|
|
}
|
|
|
|
.savings-sub {
|
|
font-size: 22rpx;
|
|
color: #94A3B8;
|
|
margin-top: 10rpx;
|
|
display: block;
|
|
}
|
|
|
|
.savings-ring {
|
|
width: 120rpx;
|
|
height: 120rpx;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.savings-ring-inner {
|
|
width: 88rpx;
|
|
height: 88rpx;
|
|
border-radius: 50%;
|
|
background-color: #FFFFFF;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 6rpx 16rpx rgba(245, 158, 11, 0.12);
|
|
}
|
|
|
|
.ring-value {
|
|
font-size: 22rpx;
|
|
font-weight: 700;
|
|
color: #111827;
|
|
}
|
|
|
|
.health-card {
|
|
background-color: #FFFFFF;
|
|
border-radius: 28rpx;
|
|
padding: 24rpx;
|
|
margin-top: 24rpx;
|
|
border: 2rpx solid #ECFDF3;
|
|
box-shadow: 0 12rpx 24rpx rgba(16, 185, 129, 0.08);
|
|
}
|
|
|
|
.health-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.health-title-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12rpx;
|
|
}
|
|
|
|
.health-icon {
|
|
width: 28rpx;
|
|
height: 28rpx;
|
|
background-color: #DCFCE7;
|
|
border-radius: 8rpx;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #16A34A;
|
|
}
|
|
|
|
.health-icon.icon-muted {
|
|
background-color: #F1F5F9;
|
|
color: #94A3B8;
|
|
}
|
|
|
|
.health-icon.icon-low {
|
|
color: #16A34A;
|
|
}
|
|
|
|
.health-icon.icon-mid {
|
|
color: #16A34A;
|
|
}
|
|
|
|
.health-icon.icon-strong {
|
|
color: #0F766E;
|
|
}
|
|
|
|
.icon-plus-vertical,
|
|
.icon-plus-horizontal {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background-color: currentColor;
|
|
border-radius: 999rpx;
|
|
}
|
|
|
|
.icon-plus-vertical {
|
|
width: 4rpx;
|
|
height: 16rpx;
|
|
}
|
|
|
|
.icon-plus-horizontal {
|
|
width: 16rpx;
|
|
height: 4rpx;
|
|
}
|
|
|
|
.health-title {
|
|
font-size: 26rpx;
|
|
font-weight: 600;
|
|
color: #111827;
|
|
}
|
|
|
|
.health-badge {
|
|
font-size: 20rpx;
|
|
padding: 6rpx 14rpx;
|
|
border-radius: 999rpx;
|
|
background-color: #E8FFF1;
|
|
color: #16A34A;
|
|
font-weight: 600;
|
|
border: 2rpx solid #BBF7D0;
|
|
}
|
|
|
|
.health-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
.health-item-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.health-item-name {
|
|
font-size: 24rpx;
|
|
color: #111827;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.health-item-percent {
|
|
font-size: 22rpx;
|
|
color: #16A34A;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.health-item-percent-muted {
|
|
color: #94A3B8;
|
|
}
|
|
|
|
.health-bar {
|
|
height: 10rpx;
|
|
background-color: #F1F5F9;
|
|
border-radius: 999rpx;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.health-bar-fill {
|
|
height: 100%;
|
|
background-color: #16A34A;
|
|
border-radius: 999rpx;
|
|
}
|
|
|
|
.health-bar-fill-muted {
|
|
background-color: #C7E6D4;
|
|
}
|
|
|
|
.stats-grid {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
margin-top: 24rpx;
|
|
}
|
|
|
|
.mini-card {
|
|
flex: 1;
|
|
background-color: #FFFFFF;
|
|
border-radius: 24rpx;
|
|
padding: 24rpx;
|
|
border: 2rpx solid #ECFDF3;
|
|
box-shadow: 0 12rpx 24rpx rgba(16, 185, 129, 0.06);
|
|
}
|
|
|
|
.mini-icon {
|
|
width: 40rpx;
|
|
height: 40rpx;
|
|
border-radius: 12rpx;
|
|
margin-bottom: 12rpx;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.mini-icon-fire {
|
|
background-color: #FEE2E2;
|
|
color: #F97316;
|
|
}
|
|
|
|
.mini-icon-block {
|
|
background-color: #E0E7FF;
|
|
color: #6366F1;
|
|
}
|
|
|
|
.mini-icon.icon-muted {
|
|
background-color: #F1F5F9;
|
|
color: #94A3B8;
|
|
}
|
|
|
|
.mini-icon-fire.icon-low {
|
|
color: #FB923C;
|
|
}
|
|
|
|
.mini-icon-fire.icon-mid {
|
|
color: #F97316;
|
|
}
|
|
|
|
.mini-icon-fire.icon-strong {
|
|
color: #EA580C;
|
|
}
|
|
|
|
.mini-icon-block.icon-low {
|
|
color: #818CF8;
|
|
}
|
|
|
|
.mini-icon-block.icon-mid {
|
|
color: #6366F1;
|
|
}
|
|
|
|
.mini-icon-block.icon-strong {
|
|
color: #4F46E5;
|
|
}
|
|
|
|
.icon-flame {
|
|
width: 16rpx;
|
|
height: 20rpx;
|
|
background-color: currentColor;
|
|
border-radius: 50% 50% 50% 50%;
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
.icon-shield {
|
|
width: 18rpx;
|
|
height: 18rpx;
|
|
border-radius: 50%;
|
|
border: 3rpx solid currentColor;
|
|
}
|
|
|
|
.mini-label {
|
|
font-size: 22rpx;
|
|
color: #64748B;
|
|
margin-bottom: 10rpx;
|
|
display: block;
|
|
}
|
|
|
|
.mini-value-row {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 8rpx;
|
|
}
|
|
|
|
.mini-value {
|
|
font-size: 40rpx;
|
|
font-weight: 700;
|
|
color: #111827;
|
|
}
|
|
|
|
.mini-unit {
|
|
font-size: 22rpx;
|
|
color: #94A3B8;
|
|
}
|
|
|
|
.mini-sub {
|
|
font-size: 22rpx;
|
|
color: #94A3B8;
|
|
margin-top: 6rpx;
|
|
display: block;
|
|
}
|
|
</style>
|
|
|
|
|
|
|