feat: enhance achievement display and update API documentation
This commit is contained in:
+105
-69
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user