更新
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
/* === 共享/邀请查看样式 === */
|
||||
|
||||
Reference in New Issue
Block a user