This commit is contained in:
Your Name
2026-05-27 17:29:27 +08:00
parent 19af50c344
commit 36be8fedad
5 changed files with 519 additions and 325 deletions
+325 -217
View File
@@ -224,8 +224,10 @@
<view class="hero-row">
<view class="hero-main">
<view class="hero-greet-row">
<text class="hero-greet-emoji">{{ timeGreetingEmoji }}</text>
<text class="hero-greet-text">{{ timeGreeting }}</text>
<view class="hero-greet-icon-wrap">
<TongjiIcon :name="timeGreetingMeta.icon" size="md" color="#FFFFFF" />
</view>
<text class="hero-greet-text">{{ timeGreetingMeta.text }}</text>
</view>
<text class="hero-title">{{ patientName || '健康档案' }}</text>
<view class="hero-subtitle-row">
@@ -236,14 +238,18 @@
class="hero-status-chip"
:class="todayChecked ? 'done' : 'pending'"
>
<text class="hero-status-icon">{{ todayChecked ? '✓' : '!' }}</text>
<view class="hero-status-icon">
<TongjiIcon :name="todayChecked ? 'check-circle' : 'info'" size="sm" color="#ffffff" />
</view>
<text class="hero-status-text">{{ todayChecked ? '今日已记录' : '今日待记录' }}</text>
</view>
</view>
</view>
<view class="hero-pill-group">
<view class="hero-pill input" @click="openInputForm()">
<text class="hero-pill-icon"></text>
<view class="hero-pill-icon-wrap">
<TongjiIcon name="plus" size="sm" color="#0d9488" />
</view>
<text class="hero-pill-text">录入今日</text>
</view>
<view
@@ -251,11 +257,15 @@
:class="{ speaking: ttsSpeaking, disabled: !ttsEnabled }"
@click="ttsSpeaking ? ttsStop() : speakLatestSummary()"
>
<text class="hero-pill-icon">{{ ttsSpeaking ? '⏸' : '🔊' }}</text>
<view class="hero-pill-icon-wrap">
<TongjiIcon :name="ttsSpeaking ? 'pause' : 'volume'" size="sm" :color="ttsSpeaking ? '#0f172a' : '#ffffff'" />
</view>
<text class="hero-pill-text">{{ ttsSpeaking ? '停止' : '朗读' }}</text>
</view>
<view class="hero-pill" @click="onRefresh">
<text class="hero-pill-icon"></text>
<view class="hero-pill-icon-wrap">
<TongjiIcon name="refresh" size="sm" color="#ffffff" />
</view>
<text class="hero-pill-text">刷新</text>
</view>
</view>
@@ -508,24 +518,42 @@
</view>
</view>
<!-- 控糖树成长区 -->
<!-- 控糖树成长区10 级成长 + 路线图-->
<view class="tree-growth-zone">
<view class="tree-graphic-wrap tree-tappable" @click="onTreeWhisper">
<view class="tree-lv-badge">Lv.{{ treeLevel }}</view>
<view class="tree-mood-pill">
<text class="tree-mood-emoji">{{ treeEmoji }}</text>
<text class="tree-mood-text">{{ treeMood }}</text>
</view>
<SugarTreeGraphic :level="treeLevel" size="lg" :watering="treeWatering" />
<view v-if="floatReward" :key="floatReward.key" class="float-reward">
<text>+{{ floatReward.text }}</text>
</view>
<text class="tree-tap-hint">我聊聊天</text>
<text class="tree-tap-hint">树听鼓励 </text>
</view>
<view class="tree-progress-wrap">
<view class="tree-level-info">
<text class="tree-level-tag">Lv.{{ treeLevel }} {{ treeLevelName }}</text>
<text class="tree-progress-text" v-if="treeLevel < 4">{{ treeProgress }}/100 XP</text>
<text class="tree-progress-text" v-else>已满级</text>
<text class="tree-level-tag">{{ treeLevelName }}</text>
<text class="tree-progress-text">{{ treeProgressLabel }}</text>
</view>
<view class="tree-progress-track">
<view class="tree-progress-bar" :style="{ width: treeProgress + '%' }" />
</view>
<text v-if="treeNextHint" class="tree-next-hint">{{ treeNextHint }}</text>
<scroll-view scroll-x class="tree-roadmap-scroll" :show-scrollbar="false">
<view class="tree-roadmap-row">
<view
v-for="node in TREE_LEVEL_ROADMAP"
:key="node.level"
class="tree-roadmap-node"
:class="{ done: node.level < treeLevel, current: node.level === treeLevel, locked: node.level > treeLevel }"
>
<text class="tree-roadmap-emoji">{{ node.emoji }}</text>
<text class="tree-roadmap-lv">Lv{{ node.level }}</text>
</view>
</view>
</scroll-view>
<view class="tree-water-action">
<button class="water-btn" :class="{ disabled: treeWatering, pulse: claimablePoints > 0 }" @click="waterTree">
<TongjiIcon name="droplet" size="sm" color="#FFFFFF" />
@@ -1313,6 +1341,7 @@ import { onLoad, onShow, onHide, onUnload, onPullDownRefresh, onShareAppMessage
import SugarTreeGraphic from '../components/SugarTreeGraphic.vue'
import TongjiIcon from '../components/TongjiIcon.vue'
import CelebrateBurst from '../components/CelebrateBurst.vue'
import { TREE_LEVELS, TREE_MAX_LEVEL, calcTreeFromPoints, pickTreeWhisper } from '../utils/treeLevels.js'
const { proxy } = getCurrentInstance()
@@ -1776,9 +1805,6 @@ async function applyCard(card) {
summary_line: '分享战报给家人,邀请他们为您点赞鼓劲',
recent: []
}
gamifyTreeLevel.value = null
gamifyTreeProgress.value = null
gamifyTreeLevelName.value = ''
gamifyStatusTip.value = ''
await fetchGamifyState()
}
@@ -1875,25 +1901,18 @@ function onRefresh() {
}
// ============ 时段问候 / 打卡 / 健康日历 ============
const timeGreeting = computed(() => {
const h = new Date().getHours()
if (h < 5) return '夜深了'
if (h < 11) return '早上好'
if (h < 13) return '中午好'
if (h < 18) return '午好'
if (h < 22) return '晚上好'
return '夜深了'
})
/** 时段问候:文案 + 统一 TongjiIcon(与全页图标体系一致)*/
function buildTimeGreetingMeta(hour = new Date().getHours()) {
const h = Number(hour) || 0
if (h < 5) return { text: '夜深了', icon: 'moon' }
if (h < 11) return { text: '早上好', icon: 'sun' }
if (h < 13) return { text: '午好', icon: 'sun' }
if (h < 18) return { text: '下午好', icon: 'sun' }
if (h < 22) return { text: '晚上好', icon: 'sunset' }
return { text: '夜深了', icon: 'moon' }
}
const timeGreetingEmoji = computed(() => {
const h = new Date().getHours()
if (h < 5) return '🌙'
if (h < 11) return '☀️'
if (h < 13) return '🌤'
if (h < 18) return '🌅'
if (h < 22) return '🌆'
return '🌙'
})
const timeGreetingMeta = computed(() => buildTimeGreetingMeta())
// 复用已有的 formatDate(date) -> 'YYYY-MM-DD'
@@ -3163,10 +3182,8 @@ const gamifyBadges = ref([])
const gamifyTaskAwards = ref({})
const gamifyTodayTasks = ref([])
const gamifyClaimablePoints = ref(0)
const gamifyTreeLevel = ref(null)
const gamifyTreeProgress = ref(null)
const gamifyTreeLevelName = ref('')
const gamifyStatusTip = ref('')
const TREE_LEVEL_ROADMAP = TREE_LEVELS
const treeWatering = ref(false)
function isApiSuccess(res) {
@@ -3192,15 +3209,6 @@ function applyGamifyPayload(data) {
}))
: []
gamifyClaimablePoints.value = Number(data.claimable_points) || 0
if (data.tree_level != null && data.tree_level !== '') {
gamifyTreeLevel.value = Math.min(4, Math.max(0, Number(data.tree_level) || 0))
}
if (data.tree_progress != null && data.tree_progress !== '') {
gamifyTreeProgress.value = Math.min(100, Math.max(0, Number(data.tree_progress) || 0))
}
if (data.tree_level_name) {
gamifyTreeLevelName.value = String(data.tree_level_name)
}
if (data.message) {
gamifyStatusTip.value = String(data.message)
}
@@ -3300,8 +3308,20 @@ const unlockedBadges = computed(() => {
if (gamifyPoints.value >= 100) {
list.push({ id: 'p100', label: '积分破百', icon: 'trophy', color: '#7C3AED' })
}
if (gamifyPoints.value >= 200) {
list.push({ id: 'p200', label: '积分200+', icon: 'sparkles', color: '#7C3AED' })
}
if (treeLevel.value >= 3) {
list.push({ id: 'tree', label: treeLevelName.value, icon: 'glucose', color: '#0D9488' })
list.push({ id: 'tree3', label: '小树苗', icon: 'glucose', color: '#0D9488' })
}
if (treeLevel.value >= 5) {
list.push({ id: 'tree5', label: '拔节高', icon: 'activity', color: '#059669' })
}
if (treeLevel.value >= 7) {
list.push({ id: 'tree7', label: '初绽香', icon: 'heart', color: '#EA580C' })
}
if (treeState.value.isMax) {
list.push({ id: 'tree9', label: '圆满树', icon: 'trophy', color: '#CA8A04' })
}
return list
})
@@ -3315,14 +3335,6 @@ const taskJustDoneId = ref('')
let celebrateTimer = null
let taskPopTimer = null
const TREE_WHISPERS = [
'再浇一点水,我就长高啦。',
'您今天真棒,小树都开心得晃叶子。',
'坚持记录,我会开出稳糖花。',
'空腹餐后都记全,我会长得更快。',
'家人点赞的时候,我也会发光。'
]
const INVITE_TREE_WHISPERS = [
'Ta 正在努力控糖,您的鼓励很重要。',
'好习惯比完美数值更珍贵,一起加油。',
@@ -3365,7 +3377,7 @@ function popTaskDone(taskId) {
function onTreeWhisper() {
if (treeWatering.value) return
const line = TREE_WHISPERS[Math.floor(Math.random() * TREE_WHISPERS.length)]
const line = pickTreeWhisper(treeLevel.value)
showUserToast(line, { duration: 2800 })
hapticLight()
}
@@ -3411,11 +3423,11 @@ async function waterTree() {
triggerCelebrate('浇水成功', tip || `+${addedPoints} 稳糖积分,小树咕咚喝饱啦`)
const claimedIds = Array.isArray(data.claimed_tasks) ? data.claimed_tasks : []
claimedIds.forEach((id) => popTaskDone(id))
const newLevel = treeLevel.value
if (newLevel > oldLevel) {
const names = ['嫩芽期', '成长雏形', '枝繁叶茂', '含苞待放', '花开稳糖']
setTimeout(() => triggerCelebrate('小树升级啦', `恭喜进入 ${names[newLevel] || treeLevelName.value}`), 520)
}
const newLevel = treeLevel.value
if (newLevel > oldLevel) {
const meta = TREE_LEVELS[newLevel] || TREE_LEVELS[0]
setTimeout(() => triggerCelebrate('小树升级啦', `Lv.${newLevel} ${meta.name} · ${meta.desc}`), 520)
}
setTimeout(() => {
if (todayTasks.value.every((t) => t.completed && t.claimed)) {
triggerCelebrate('今日任务大满贯', '明天继续来浇浇水吧')
@@ -3460,25 +3472,20 @@ async function waterTree() {
}
}
const treeLevel = computed(() => {
if (gamifyTreeLevel.value != null) {
return Math.min(4, Math.max(0, gamifyTreeLevel.value))
}
return Math.min(4, Math.floor(gamifyPoints.value / 100))
const treeState = computed(() => calcTreeFromPoints(gamifyPoints.value))
const treeLevel = computed(() => treeState.value.level)
const treeProgress = computed(() => treeState.value.progress)
const treeLevelName = computed(() => treeState.value.name)
const treeEmoji = computed(() => treeState.value.emoji)
const treeMood = computed(() => treeState.value.mood)
const treeProgressLabel = computed(() => {
if (treeState.value.isMax) return '已满级 · 圆满树'
return `${treeState.value.xpInLevel}/${treeState.value.xpNeed} XP`
})
const treeProgress = computed(() => {
if (treeLevel.value >= 4) return 100
if (gamifyTreeProgress.value != null) {
return Math.min(100, Math.max(0, gamifyTreeProgress.value))
}
return gamifyPoints.value % 100
})
const treeLevelName = computed(() => {
if (gamifyTreeLevelName.value) return gamifyTreeLevelName.value
const names = ['嫩芽期', '成长雏形', '枝繁叶茂', '含苞待放', '花开稳糖']
return names[treeLevel.value] || '嫩芽期'
const treeNextHint = computed(() => {
if (treeState.value.isMax) return '您已养成满级稳糖树,习惯就是最好的奖励'
if (claimablePoints.value > 0) return `浇水后可再积 ${claimablePoints.value} 分,离升级更近一步`
return `再积 ${treeState.value.pointsToNext} 分,升级「${treeState.value.nextName}`
})
// ============ 分享 / 家人点赞 / 邀请观看 ============
@@ -3969,7 +3976,7 @@ async function onFamilyLike() {
border-color: #fde68a;
color: var(--slate-900);
animation: voice-pulse 1.4s ease-in-out infinite;
.hero-pill-text, .hero-pill-icon {
.hero-pill-text {
color: var(--slate-900);
}
}
@@ -3981,11 +3988,6 @@ async function onFamilyLike() {
border: 2rpx solid #ffffff;
padding: 16rpx 32rpx;
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.1);
.hero-pill-icon {
color: var(--primary);
font-weight: 800;
font-size: 38rpx;
}
.hero-pill-text {
color: var(--primary);
font-weight: 700;
@@ -3999,10 +4001,13 @@ async function onFamilyLike() {
0%, 100% { box-shadow: 0 0 0 0 rgba(251, 191, 36, 0.55); }
50% { box-shadow: 0 0 0 12rpx rgba(251, 191, 36, 0); }
}
.hero-pill-icon {
font-size: 34rpx;
color: #ffffff;
line-height: 1;
.hero-pill-icon-wrap {
width: 36rpx;
height: 36rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.hero-pill-text {
font-size: 32rpx;
@@ -4012,88 +4017,81 @@ async function onFamilyLike() {
/* === Bento 风格指标卡 === */
.stat-grid {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
margin: -60rpx 24rpx 0;
position: relative;
z-index: 3;
}
.stat-card {
flex: 1 1 calc(50% - 8rpx);
min-width: 0;
background: #ffffff;
border-radius: var(--radius-card);
padding: 30rpx 24rpx 26rpx;
padding: 30rpx 26rpx;
box-shadow: var(--shadow-premium);
display: flex;
flex-direction: column;
gap: 12rpx;
position: relative;
overflow: hidden;
border-top: 8rpx solid transparent;
justify-content: space-between;
min-height: 250rpx;
border: 1rpx solid rgba(15, 23, 42, 0.04);
box-sizing: border-box;
transition: var(--transition-smooth);
position: relative;
overflow: hidden;
&:active {
transform: translateY(-2rpx);
box-shadow: 0 16rpx 36rpx rgba(15, 23, 42, 0.08);
}
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
right: -30rpx;
width: 120rpx;
height: 120rpx;
border-radius: 999rpx;
opacity: 0.06;
pointer-events: none;
}
.stat-fasting {
border-top-color: var(--primary);
&::before { background: var(--primary); }
background: radial-gradient(circle at 100% 0%, #e6fdf9 0%, #ffffff 65%);
}
.stat-postprandial {
border-top-color: var(--warning);
&::before { background: var(--warning); }
background: radial-gradient(circle at 100% 0%, #fff7ed 0%, #ffffff 65%);
}
.stat-other {
flex: 1 1 100% !important;
flex-direction: row !important;
grid-column: span 2;
background: radial-gradient(circle at 100% 0%, #f5f3ff 0%, #ffffff 65%);
display: grid !important;
grid-template-areas:
"head value"
"foot value" !important;
grid-template-columns: 1fr auto !important;
grid-template-rows: auto auto !important;
align-items: center !important;
justify-content: space-between !important;
padding: 24rpx 32rpx !important;
border-top: none !important;
border-left: 8rpx solid var(--violet) !important;
gap: 16rpx !important;
&::before { display: none !important; }
min-height: 170rpx !important;
padding: 26rpx 32rpx !important;
.stat-card-head {
display: flex;
flex-direction: row;
align-items: center;
gap: 12rpx;
flex: 1;
grid-area: head !important;
display: flex !important;
align-items: center !important;
justify-content: flex-start !important;
margin: 0 !important;
padding: 0 !important;
gap: 12rpx !important;
}
.stat-card-value-row {
margin-left: auto;
display: flex;
align-items: baseline;
gap: 6rpx;
margin-top: 0;
grid-area: value !important;
display: flex !important;
align-items: baseline !important;
margin: 0 !important;
padding: 0 !important;
gap: 6rpx !important;
}
.stat-card-foot {
display: flex;
flex-direction: row;
align-items: center;
gap: 12rpx;
margin-top: 0;
grid-area: foot !important;
display: flex !important;
align-items: center !important;
justify-content: flex-start !important;
margin-top: 12rpx !important;
padding: 0 !important;
gap: 16rpx !important;
}
}
.stat-card.is-high {
box-shadow: var(--shadow-glow-danger);
border-color: rgba(239, 68, 68, 0.1);
border-color: rgba(239, 68, 68, 0.08);
}
.stat-card-head {
display: flex;
@@ -4102,52 +4100,61 @@ async function onFamilyLike() {
gap: 8rpx;
}
.stat-card-tag {
display: flex;
display: inline-flex;
align-items: center;
gap: 6rpx;
padding: 8rpx 18rpx;
border-radius: 12rpx;
font-size: 26rpx;
font-weight: 700;
}
.stat-fasting .stat-card-tag {
background: rgba(13, 148, 136, 0.09);
color: #0d9488;
}
.stat-postprandial .stat-card-tag {
background: rgba(245, 158, 11, 0.09);
color: #d97706;
}
.stat-other .stat-card-tag {
background: rgba(99, 102, 241, 0.09);
color: #4f46e5;
}
.stat-card-tag-dot {
width: 16rpx;
height: 16rpx;
border-radius: var(--radius-pill);
display: none;
}
.stat-fasting .stat-card-tag-dot { background: var(--primary); }
.stat-postprandial .stat-card-tag-dot { background: var(--warning); }
.stat-other .stat-card-tag-dot { background: var(--violet); }
.stat-card-tag-text {
font-size: 26rpx;
color: var(--slate-900);
font-weight: 700;
}
.stat-card-flag {
display: inline-flex;
align-items: center;
gap: 4rpx;
padding: 4rpx 12rpx 4rpx 8rpx;
gap: 6rpx;
padding: 6rpx 14rpx;
border-radius: var(--radius-pill);
background: var(--danger-grad);
box-shadow: 0 4rpx 10rpx rgba(239, 68, 68, 0.2);
background: rgba(239, 68, 68, 0.08);
border: 1rpx solid rgba(239, 68, 68, 0.15);
}
.stat-card-flag-icon {
font-size: 20rpx;
color: #ffffff;
color: var(--danger);
font-weight: 700;
line-height: 1;
}
.stat-card-flag-text {
font-size: 22rpx;
color: #ffffff;
color: var(--danger);
font-weight: 700;
}
.stat-card-value-row {
display: flex;
align-items: baseline;
gap: 8rpx;
flex-wrap: wrap;
gap: 6rpx;
margin-top: 16rpx;
margin-bottom: 8rpx;
}
.stat-card-value {
font-size: 60rpx; /* 更大数字 */
font-size: 64rpx;
font-weight: 900;
background: linear-gradient(180deg, var(--slate-900) 0%, #334155 100%);
-webkit-background-clip: text;
@@ -4183,25 +4190,25 @@ async function onFamilyLike() {
display: inline-flex;
align-items: center;
gap: 4rpx;
padding: 6rpx 12rpx 6rpx 9rpx;
padding: 6rpx 14rpx;
border-radius: var(--radius-pill);
background: var(--slate-100);
transition: var(--transition-smooth);
&.trend-up {
background: #fef3c7;
background: rgba(239, 68, 68, 0.08);
.stat-card-trend-arrow,
.stat-card-trend-delta {
color: #b45309;
color: var(--danger);
}
}
&.trend-down {
background: #dcfce7;
background: rgba(16, 185, 129, 0.08);
.stat-card-trend-arrow,
.stat-card-trend-delta {
color: #15803d;
color: var(--success);
}
}
&.trend-flat {
background: var(--slate-100);
.stat-card-trend-arrow,
.stat-card-trend-delta {
color: var(--slate-600);
@@ -5043,8 +5050,16 @@ async function onFamilyLike() {
gap: 12rpx;
margin-bottom: 6rpx;
}
.hero-greet-emoji {
font-size: 36rpx;
.hero-greet-icon-wrap {
width: 52rpx;
height: 52rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.18);
border: 1rpx solid rgba(255, 255, 255, 0.28);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.hero-greet-text {
font-size: 30rpx;
@@ -5080,16 +5095,12 @@ async function onFamilyLike() {
}
}
.hero-status-icon {
width: 36rpx;
height: 36rpx;
border-radius: var(--radius-pill);
background: rgba(255, 255, 255, 0.4);
width: 32rpx;
height: 32rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
line-height: 1;
font-weight: 800;
flex-shrink: 0;
}
.hero-status-text {
line-height: 1.2;
@@ -5879,23 +5890,117 @@ async function onFamilyLike() {
}
.tree-growth-zone {
display: flex;
align-items: center;
gap: 24rpx;
background: var(--slate-50);
align-items: flex-start;
gap: 20rpx;
background: linear-gradient(145deg, #f0fdfa 0%, #f8fafc 55%, #fffbeb 100%);
border-radius: 24rpx;
padding: 20rpx 24rpx;
border: 1rpx solid var(--slate-100);
padding: 24rpx 20rpx;
border: 2rpx solid #99f6e4;
}
.tree-graphic-wrap {
position: relative;
flex-shrink: 0;
width: 140rpx;
height: 154rpx;
width: 152rpx;
min-height: 200rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.tree-lv-badge {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
z-index: 2;
padding: 4rpx 14rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #0ea5a4, #14b8a6);
color: #ffffff;
font-size: 20rpx;
font-weight: 800;
box-shadow: 0 4rpx 12rpx rgba(14, 165, 164, 0.35);
}
.tree-mood-pill {
position: absolute;
top: 36rpx;
right: -8rpx;
z-index: 2;
display: flex;
align-items: center;
gap: 4rpx;
padding: 4rpx 10rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.92);
border: 1rpx solid #e2e8f0;
box-shadow: 0 2rpx 8rpx rgba(15, 23, 42, 0.08);
}
.tree-mood-emoji {
font-size: 22rpx;
line-height: 1;
}
.tree-mood-text {
font-size: 20rpx;
font-weight: 700;
color: #64748b;
}
.tree-next-hint {
display: block;
margin-top: 8rpx;
font-size: 22rpx;
color: #0f766e;
font-weight: 600;
line-height: 1.45;
}
.tree-roadmap-scroll {
width: 100%;
margin-top: 14rpx;
white-space: nowrap;
}
.tree-roadmap-row {
display: inline-flex;
gap: 10rpx;
padding-bottom: 4rpx;
}
.tree-roadmap-node {
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 4rpx;
min-width: 56rpx;
padding: 8rpx 6rpx;
border-radius: 14rpx;
background: #f1f5f9;
border: 1rpx solid #e2e8f0;
opacity: 0.55;
&.done {
opacity: 0.85;
background: #ecfdf5;
border-color: #a7f3d0;
}
&.current {
opacity: 1;
background: linear-gradient(180deg, #ccfbf1, #ffffff);
border-color: #14b8a6;
box-shadow: 0 4rpx 14rpx rgba(14, 165, 164, 0.25);
transform: scale(1.06);
}
&.locked {
opacity: 0.4;
}
}
.tree-roadmap-emoji {
font-size: 26rpx;
line-height: 1;
}
.tree-roadmap-lv {
font-size: 18rpx;
font-weight: 700;
color: #64748b;
.tree-roadmap-node.current & {
color: #0f766e;
}
}
.tree-tappable:active {
opacity: 0.92;
}
@@ -5956,14 +6061,14 @@ async function onFamilyLike() {
color: #b45309;
}
.badge-wall {
margin-top: 4rpx;
margin-top: 12rpx;
}
.badge-wall-title {
display: block;
font-size: 26rpx;
font-size: 32rpx;
font-weight: 800;
color: #475569;
margin-bottom: 12rpx;
margin-bottom: 16rpx;
padding-left: 6rpx;
}
.badge-wall-scroll {
@@ -5972,24 +6077,24 @@ async function onFamilyLike() {
}
.badge-wall-row {
display: inline-flex;
gap: 12rpx;
padding-bottom: 4rpx;
gap: 14rpx;
padding-bottom: 8rpx;
}
.badge-chip {
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
min-width: 120rpx;
padding: 14rpx 16rpx;
gap: 10rpx;
min-width: 140rpx;
padding: 18rpx 20rpx;
background: #ffffff;
border: 1rpx solid var(--slate-200);
border-radius: 16rpx;
border-radius: 18rpx;
box-shadow: var(--shadow-sm);
}
.badge-chip-icon {
width: 52rpx;
height: 52rpx;
width: 58rpx;
height: 58rpx;
border-radius: var(--radius-pill);
background: var(--slate-50);
display: flex;
@@ -5997,7 +6102,7 @@ async function onFamilyLike() {
justify-content: center;
}
.badge-chip-label {
font-size: 22rpx;
font-size: 28rpx;
font-weight: 700;
color: #334155;
white-space: nowrap;
@@ -6016,15 +6121,15 @@ async function onFamilyLike() {
gap: 12rpx;
}
.tree-level-tag {
font-size: 26rpx;
font-size: 30rpx;
font-weight: 700;
color: var(--slate-900);
background: var(--slate-200);
padding: 4rpx 14rpx;
padding: 6rpx 18rpx;
border-radius: var(--radius-pill);
}
.tree-progress-text {
font-size: 24rpx;
font-size: 28rpx;
color: var(--slate-600);
font-weight: 600;
}
@@ -6246,14 +6351,14 @@ async function onFamilyLike() {
}
.pg-share-title {
display: block;
font-size: 28rpx;
font-weight: 700;
font-size: 34rpx;
font-weight: 800;
color: #0f766e;
}
.pg-share-sub {
display: block;
margin-top: 4rpx;
font-size: 22rpx;
margin-top: 6rpx;
font-size: 28rpx;
color: var(--slate-600);
}
@@ -6261,12 +6366,12 @@ async function onFamilyLike() {
.family-like-strip {
display: flex;
align-items: center;
gap: 16rpx;
gap: 18rpx;
margin: 20rpx 24rpx 0;
padding: 22rpx 24rpx;
padding: 24rpx 28rpx;
background: #ffffff;
border-radius: 24rpx;
border: 1rpx solid #fecdd3;
border: 1.5rpx solid #fecdd3;
box-shadow: 0 4rpx 16rpx rgba(225, 29, 72, 0.06);
transition: var(--transition-smooth);
&.has-likes {
@@ -6277,7 +6382,7 @@ async function onFamilyLike() {
}
}
.family-like-icon {
font-size: 36rpx;
font-size: 40rpx;
}
.family-like-body {
flex: 1;
@@ -6285,40 +6390,42 @@ async function onFamilyLike() {
}
.family-like-title {
display: block;
font-size: 28rpx;
font-weight: 700;
font-size: 34rpx;
font-weight: 800;
color: #9f1239;
}
.family-like-sub {
display: block;
margin-top: 6rpx;
font-size: 24rpx;
margin-top: 8rpx;
font-size: 30rpx;
color: var(--slate-600);
font-weight: 500;
}
.family-like-recent {
margin-top: 8rpx;
margin-top: 10rpx;
display: flex;
flex-wrap: wrap;
gap: 8rpx;
gap: 10rpx;
}
.family-like-recent-item {
font-size: 22rpx;
font-size: 26rpx;
color: #be123c;
background: #ffe4e6;
padding: 4rpx 12rpx;
padding: 6rpx 16rpx;
border-radius: var(--radius-pill);
font-weight: 600;
}
.family-like-arrow {
font-size: 32rpx;
font-size: 36rpx;
color: var(--slate-400);
}
.encourage-strip {
display: flex;
align-items: flex-start;
gap: 14rpx;
gap: 16rpx;
margin: 16rpx 24rpx 0;
padding: 20rpx 22rpx;
padding: 24rpx 24rpx;
background: #ecfdf5;
border-radius: 20rpx;
border: 1rpx solid #a7f3d0;
@@ -6328,20 +6435,21 @@ async function onFamilyLike() {
}
}
.encourage-icon {
font-size: 32rpx;
font-size: 36rpx;
}
.encourage-text {
display: block;
font-size: 26rpx;
font-weight: 600;
font-size: 32rpx;
font-weight: 700;
color: #065f46;
line-height: 1.45;
line-height: 1.5;
}
.encourage-tip {
display: block;
margin-top: 6rpx;
font-size: 22rpx;
margin-top: 8rpx;
font-size: 28rpx;
color: var(--slate-600);
font-weight: 500;
}
/* === 共享/邀请查看样式 === */