881 lines
18 KiB
Vue
881 lines
18 KiB
Vue
<template>
|
||
<view class="page">
|
||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||
<view class="header">
|
||
<view class="back-btn" @tap="goBack">
|
||
<text class="back-icon">‹</text>
|
||
</view>
|
||
<text class="header-title">数据统计分析</text>
|
||
<view class="header-spacer"></view>
|
||
</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 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"></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"></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"></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"></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)
|
||
|
||
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)
|
||
}
|
||
}
|
||
|
||
function goBack() {
|
||
uni.switchTab({ url: '/pages/index/index' })
|
||
}
|
||
|
||
watch(currentTab, () => {
|
||
fetchStats()
|
||
})
|
||
|
||
onMounted(() => {
|
||
const sys = uni.getSystemInfoSync()
|
||
statusBarHeight.value = sys.statusBarHeight || 0
|
||
fetchStats()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page {
|
||
min-height: 100vh;
|
||
background-color: #F5F7FB;
|
||
padding: 0 28rpx 180rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.status-bar {
|
||
background-color: #F5F7FB;
|
||
height: 0;
|
||
}
|
||
|
||
.header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 16rpx 0 12rpx;
|
||
}
|
||
|
||
.back-btn {
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
border-radius: 20rpx;
|
||
background-color: #FFFFFF;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.06);
|
||
}
|
||
|
||
.back-icon {
|
||
font-size: 36rpx;
|
||
color: #111827;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.header-title {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
}
|
||
|
||
.header-spacer {
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
}
|
||
|
||
.segment {
|
||
display: flex;
|
||
background-color: #EEF2F7;
|
||
padding: 8rpx;
|
||
border-radius: 20rpx;
|
||
gap: 8rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.segment-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 16rpx 0;
|
||
font-size: 26rpx;
|
||
font-weight: 500;
|
||
color: #64748B;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.segment-active {
|
||
background-color: #FFFFFF;
|
||
color: #111827;
|
||
box-shadow: 0 8rpx 16rpx rgba(15, 23, 42, 0.06);
|
||
}
|
||
|
||
.insight-card {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
align-items: flex-start;
|
||
background-color: #ECFDF3;
|
||
border: 2rpx solid #D4F6E2;
|
||
border-radius: 24rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 28rpx;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.status-good {
|
||
background-color: #E8FFF1;
|
||
color: #16A34A;
|
||
}
|
||
|
||
.status-warn {
|
||
background-color: #FEF3C7;
|
||
color: #D97706;
|
||
}
|
||
|
||
.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;
|
||
box-shadow: 0 12rpx 24rpx rgba(15, 23, 42, 0.06);
|
||
}
|
||
|
||
.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: 260rpx;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.trend-bar-item {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
position: relative;
|
||
}
|
||
|
||
.trend-bar {
|
||
width: 36rpx;
|
||
background: linear-gradient(180deg, #2DD36F 0%, #1A9F54 100%);
|
||
border-radius: 12rpx 12rpx 8rpx 8rpx;
|
||
}
|
||
|
||
.trend-bubble {
|
||
position: absolute;
|
||
top: 0;
|
||
transform: translateY(-12rpx);
|
||
background-color: #0F172A;
|
||
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;
|
||
box-shadow: 0 12rpx 24rpx rgba(15, 23, 42, 0.05);
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.savings-icon::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: 8rpx;
|
||
top: 8rpx;
|
||
width: 16rpx;
|
||
height: 12rpx;
|
||
border-radius: 4rpx;
|
||
border: 3rpx solid #F59E0B;
|
||
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(15, 23, 42, 0.06);
|
||
}
|
||
|
||
.ring-value {
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
}
|
||
|
||
.health-card {
|
||
background-color: #FFFFFF;
|
||
border-radius: 28rpx;
|
||
padding: 24rpx;
|
||
margin-top: 24rpx;
|
||
box-shadow: 0 12rpx 24rpx rgba(15, 23, 42, 0.05);
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.health-icon::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: 12rpx;
|
||
top: 6rpx;
|
||
width: 4rpx;
|
||
height: 16rpx;
|
||
background-color: #16A34A;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.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: #CBD5F5;
|
||
}
|
||
|
||
.stats-grid {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
margin-top: 24rpx;
|
||
}
|
||
|
||
.mini-card {
|
||
flex: 1;
|
||
background-color: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
padding: 24rpx;
|
||
box-shadow: 0 12rpx 24rpx rgba(15, 23, 42, 0.04);
|
||
}
|
||
|
||
.mini-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 12rpx;
|
||
position: relative;
|
||
}
|
||
|
||
.mini-icon-fire {
|
||
background-color: #FEE2E2;
|
||
}
|
||
|
||
.mini-icon-fire::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: 12rpx;
|
||
top: 8rpx;
|
||
width: 16rpx;
|
||
height: 20rpx;
|
||
border-radius: 50% 50% 50% 50%;
|
||
background-color: #F97316;
|
||
}
|
||
|
||
.mini-icon-block {
|
||
background-color: #E0E7FF;
|
||
}
|
||
|
||
.mini-icon-block::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: 12rpx;
|
||
top: 12rpx;
|
||
width: 16rpx;
|
||
height: 16rpx;
|
||
border-radius: 50%;
|
||
border: 3rpx solid #6366F1;
|
||
}
|
||
|
||
.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>
|
||
|
||
|
||
|