feat: enhance achievement display and update API documentation

This commit is contained in:
nepiedg
2026-04-26 23:18:25 +08:00
parent 374168d959
commit 895f5e6d09
3 changed files with 162 additions and 72 deletions
+105 -69
View File
@@ -129,17 +129,28 @@
<view class="record-bio-status-card">
<view class="record-bio-breath-line"></view>
<view class="record-bio-profile-row">
<view class="record-bio-avatar"><text>{{ recordAvatarText }}</text></view>
<view class="record-bio-avatar-medal">
<image class="record-bio-avatar-bg" src="/static/achievements/theme-medallion.png" mode="aspectFill" />
<text class="record-bio-avatar-icon">{{ recordTitle.icon }}</text>
</view>
<view class="record-bio-profile-main">
<text class="record-bio-rank">节奏观察者 <text class="record-bio-level">{{ recordLevelText }}</text></text>
<text class="record-bio-subtitle">{{ recordStatusLabel }} · 今日 {{ todayCount }}/{{ dailyTarget || '-' }} </text>
<text class="record-bio-rank">{{ recordTitle.name }} <text class="record-bio-level">{{ recordTitle.level }}</text></text>
<text class="record-bio-subtitle">{{ recordTitle.description }}</text>
</view>
<view class="record-bio-target-pill">
<text class="record-bio-target-icon"></text>
<text class="record-bio-target-value">{{ recordControlScore }}%</text>
<text class="record-bio-target-value"> {{ recordReducedCigs }} </text>
</view>
</view>
<view class="record-bio-title-panel">
<view class="record-bio-title-copy">
<text class="record-bio-title-kicker">REDUCTION TITLE</text>
<text class="record-bio-title-name">{{ recordTitle.badge }}</text>
</view>
<text class="record-bio-title-hint">{{ recordNextTitleHint }}</text>
</view>
<view class="record-bio-hp-box">
<view class="record-bio-hp-track">
<view class="record-bio-hp-fill" :style="{ width: recordControlScore + '%' }"></view>
@@ -208,23 +219,6 @@
</view>
</view>
<view v-if="achievementData" class="record-bio-achievement-card">
<view class="record-bio-section-head">
<view>
<text class="record-bio-section-title">{{ achievementData.theme_icon }} {{ achievementCardTitle }}</text>
<text class="record-bio-section-subtitle">{{ achievementData.theme_name }} · {{ achievementData.days }} </text>
</view>
<text class="record-bio-section-chip">{{ achievementData.current?.name || '--' }}</text>
</view>
<view v-if="achievementData.next" class="record-bio-ach-progress">
<view class="record-bio-ach-bar">
<view class="record-bio-ach-fill" :style="{ width: (achievementData.progress * 100) + '%' }"></view>
</view>
<text class="record-bio-ach-hint">距下一级{{ achievementData.next.name }}还需 {{ achievementData.next.required_days - achievementData.days }} </text>
</view>
<text v-else class="record-bio-ach-hint">已达最高等级</text>
</view>
<view class="record-bio-tip-card">
<text class="record-bio-tip-icon"></text>
<text class="record-bio-tip-text">{{ recordHealthTip }}</text>
@@ -321,12 +315,12 @@ const packPriceYuan = computed(() => (profileStore.profile?.pack_price_cent || 2
// ========== 记录模式 ==========
const todayCount = computed(() => homeSummary.value.today_count ?? 0)
const resistedCount = computed(() => homeSummary.value.resisted_count ?? 0)
const dailyTarget = computed(() => {
const target = homeSummary.value.daily_target
if (target != null) return target
return profileStore.profile?.baseline_cigs_per_day || 0
})
const recordReducedCigs = computed(() => Math.max(0, (dailyTarget.value || 0) - todayCount.value))
const changeText = computed(() => {
const reduced = homeSummary.value.reduced_from_yesterday
if (reduced == null) return '较昨日暂无对比'
@@ -382,11 +376,9 @@ const recordRhythmText = computed(() => {
return changeText.value
})
const achievementCardTitle = computed(() => isQuitMode.value ? '无烟等级' : '长期记录等级')
const reducePercent = computed(() => {
if (dailyTarget.value <= 0) return 0
return Math.round(Math.max(0, dailyTarget.value - todayCount.value) / dailyTarget.value * 100)
return Math.round(recordReducedCigs.value / dailyTarget.value * 100)
})
const recordHealthTip = computed(() => {
@@ -411,8 +403,6 @@ const recordStatusLabel = computed(() => {
return '需要降速'
})
const recordAvatarText = computed(() => achievementData.value?.theme_icon || '控')
const recordLevelText = computed(() => `Lv.${Math.max(1, Math.floor((achievementData.value?.days || 0) / 7) + 1)}`)
const recordBioBuffText = computed(() => nextSmokeTimeText.value ? `下一次建议 ${nextSmokeTimeText.value}` : '正在建立控烟节奏')
const recordControlRingStyle = computed(() => {
const angle = Math.round(recordControlScore.value * 3.6)
@@ -429,11 +419,32 @@ const recordYesterdayText = computed(() => {
const recordBioHealthItems = computed(() => [
{ name: '今日目标', value: `${todayCount.value}/${dailyTarget.value || '-'}` },
{ name: '今日忍住', value: `${resistedCount.value}` },
{ name: '较昨日', value: recordYesterdayText.value },
{ name: '建议时间', value: nextSmokeTimeText.value || '--:--' }
])
const recordTitle = computed(() => {
const current = achievementData.value?.current
const metrics = achievementData.value?.metrics || {}
return {
icon: current?.icon || achievementData.value?.theme_icon || '◎',
name: current?.name || '节奏观察者',
level: achievementData.value?.score !== undefined ? `积分 ${achievementData.value.score}` : '积分 0',
badge: achievementData.value?.theme_name || '成就称号',
description: metrics.scored_days > 0
? `累计少抽 ${metrics.total_reduced_cigs || 0} 根,稳定记录 ${metrics.stable_days || 0} 天。`
: '完成记录后,会按少抽数量解锁称号。'
}
})
const recordNextTitleHint = computed(() => {
const next = achievementData.value?.next
if (!next) return achievementData.value?.current ? '已达当前最高称号' : '记录后开始进阶'
const score = Number(achievementData.value?.score) || 0
const required = Number(next.required_score ?? next.required_days) || 0
return `距「${next.name}」还差 ${Math.max(0, required - score)}`
})
// ========== 戒烟模式 ==========
// 服务端连续无烟天数优先,失败时从本地缓存推算
@@ -2757,7 +2768,6 @@ onShareAppMessage(() => ({
.record-bio-status-card,
.record-bio-console-card,
.record-bio-health-card,
.record-bio-achievement-card,
.record-bio-tip-card {
position: relative;
overflow: hidden;
@@ -2791,16 +2801,28 @@ onShareAppMessage(() => ({
gap: 18rpx;
}
.record-bio-avatar {
width: 76rpx;
height: 76rpx;
border-radius: 28rpx;
.record-bio-avatar-medal {
position: relative;
width: 96rpx;
height: 96rpx;
flex-shrink: 0;
@include flex-center;
background: linear-gradient(135deg, #FBBF24, #67E8F9);
box-shadow: 0 12rpx 28rpx rgba(217, 119, 6, 0.14);
color: #ffffff;
font-size: 34rpx;
font-weight: 900;
}
.record-bio-avatar-bg {
position: absolute;
inset: 0;
width: 96rpx;
height: 96rpx;
border-radius: 30rpx;
box-shadow: 0 12rpx 28rpx rgba(15, 118, 110, 0.12);
}
.record-bio-avatar-icon {
position: relative;
z-index: 1;
font-size: 44rpx;
line-height: 1;
}
.record-bio-profile-main {
@@ -2847,6 +2869,50 @@ onShareAppMessage(() => ({
color: #0F766E;
}
.record-bio-title-panel {
margin-top: 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
padding: 18rpx 20rpx;
border-radius: 26rpx;
background:
radial-gradient(circle at 12% 30%, rgba(103, 232, 249, 0.2), transparent 34%),
linear-gradient(135deg, rgba(255, 251, 235, 0.82), rgba(236, 253, 245, 0.72));
border: 1rpx solid rgba(251, 191, 36, 0.18);
}
.record-bio-title-copy {
min-width: 0;
@include flex-col;
gap: 4rpx;
}
.record-bio-title-kicker {
font-size: 18rpx;
font-weight: 900;
letter-spacing: 1.2rpx;
color: #0891B2;
}
.record-bio-title-name {
font-size: 27rpx;
line-height: 1.25;
font-weight: 900;
color: #0F766E;
}
.record-bio-title-hint {
flex-shrink: 0;
max-width: 280rpx;
font-size: 20rpx;
line-height: 1.4;
font-weight: 800;
text-align: right;
color: #B45309;
}
.record-bio-hp-box {
margin-top: 30rpx;
padding: 18rpx;
@@ -3022,8 +3088,7 @@ onShareAppMessage(() => ({
font-weight: 900;
}
.record-bio-health-card,
.record-bio-achievement-card {
.record-bio-health-card {
padding: 28rpx;
border-radius: 34rpx;
}
@@ -3132,35 +3197,6 @@ onShareAppMessage(() => ({
color: #0F766E;
}
.record-bio-achievement-card {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.78), rgba(250, 245, 255, 0.5));
}
.record-bio-ach-progress {
margin-top: 22rpx;
}
.record-bio-ach-bar {
height: 14rpx;
border-radius: 999rpx;
overflow: hidden;
background: rgba(226, 232, 240, 0.8);
}
.record-bio-ach-fill {
height: 100%;
border-radius: inherit;
background: linear-gradient(90deg, var(--record-lavender), var(--record-clear));
}
.record-bio-ach-hint {
display: block;
margin-top: 12rpx;
font-size: 21rpx;
line-height: 1.5;
color: #64748B;
}
.record-bio-tip-card {
display: flex;
align-items: flex-start;