Remove outdated UI components and screens for activity history, AI quit assistant, home dashboard, profile & settings, and smoking statistics. Update API to include a new endpoint for home dashboard data retrieval. Enhance smoke record dialog with improved layout and styling adjustments.

This commit is contained in:
nepiedg
2026-01-30 01:15:52 +08:00
parent 0d482e3a1c
commit ff68f7f3ae
17 changed files with 821 additions and 1306 deletions
+110 -47
View File
@@ -16,8 +16,8 @@
<view class="user-info">
<image class="avatar" :src="userAvatar" mode="aspectFill"></image>
<view class="greeting">
<text class="greeting-text">{{ greeting }}{{ userName }}</text>
<text class="greeting-sub">保持连胜纪录🔥</text>
<text class="greeting-text">{{ greetingTitle }}</text>
<text class="greeting-sub">{{ greetingSubtitle }}</text>
</view>
</view>
<view class="settings-btn" @tap="goSettings">
@@ -25,10 +25,10 @@
</view>
</view>
<view v-if="showAiTip" class="ai-tip">
<view v-if="shouldShowAiTip" class="ai-tip">
<view class="ai-tip-icon">🤖</view>
<view class="ai-tip-content">
<text class="ai-tip-title">发现规律</text>
<text class="ai-tip-title">{{ aiTipTitle }}</text>
<text class="ai-tip-desc">{{ aiTipText }}</text>
</view>
<view class="ai-tip-close" @tap="closeAiTip">×</view>
@@ -55,7 +55,7 @@
<view class="stat-value-row">
<text class="stat-value">{{ todayCount }}</text>
<text class="stat-target">/ {{ dailyTarget }}</text>
<view class="stat-change stat-change-down">{{ changeText }}</view>
<view class="stat-change" :class="changeClass">{{ changeText }}</view>
</view>
<view class="stat-progress">
<view class="stat-progress-bar stat-progress-red" :style="{ width: progressWidth }"></view>
@@ -98,27 +98,27 @@
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useDashboardStore } from '@/stores/dashboard'
import { useProfileStore } from '@/stores/profile'
import { useUserStore } from '@/stores/user'
import { useLogin } from '@/hooks/useLogin'
import * as api from '@/api'
const dashboardStore = useDashboardStore()
const profileStore = useProfileStore()
const userStore = useUserStore()
const { waitForLogin } = useLogin()
const loading = ref(true)
const showAiTip = ref(true)
const aiTipText = ref('你的烟瘾通常在下午2点达到高峰。我们为你准备了一个快速呼吸练习。')
const resistedCount = ref(5)
const statusBarHeight = ref(0)
const showDialog = ref(false)
const dialogType = ref('smoke') // 'smoke' 或 'resisted'
const homeData = ref(null)
let timerInterval = null
const timerSeconds = ref(0)
const timerBaseSeconds = ref(-1)
const aiTipFallback = '你的烟瘾通常在下午2点达到高峰。我们为你准备了一个快速呼吸练习。'
const greeting = computed(() => {
const hour = new Date().getHours()
@@ -130,18 +130,50 @@ const greeting = computed(() => {
})
const userName = computed(() => {
return userStore.user?.nickname || 'Alex'
return homeData.value?.greeting?.nickname || userStore.user?.nickname || 'Alex'
})
const userAvatar = computed(() => {
return userStore.user?.avatar_url || '/static/images/default-avatar.png'
return homeData.value?.greeting?.avatar_url || userStore.user?.avatar_url || '/static/images/default-avatar.png'
})
const todayCount = computed(() => dashboardStore.todayCount || 3)
const dailyTarget = computed(() => profileStore.profile?.baseline_cigs_per_day || 10)
const greetingTitle = computed(() => {
return homeData.value?.greeting?.title || `${greeting.value}${userName.value}`
})
const greetingSubtitle = computed(() => {
return homeData.value?.greeting?.subtitle || '保持连胜纪录!🔥'
})
const aiTipTitle = computed(() => {
return homeData.value?.advice_card?.title || '发现规律'
})
const aiTipText = computed(() => {
const advice = homeData.value?.advice_card
if (advice?.message) return advice.message
if (advice?.status === 'locked') return '看广告解锁 AI 建议'
const motivation = homeData.value?.motivation?.message
return motivation || aiTipFallback
})
const shouldShowAiTip = computed(() => showAiTip.value && !!aiTipText.value)
const homeSummary = computed(() => homeData.value?.summary || {})
const homeTimer = computed(() => homeData.value?.timer || {})
const todayCount = computed(() => homeSummary.value.today_count ?? 0)
const dailyTarget = computed(() => {
const target = homeSummary.value.daily_target
if (target !== undefined && target !== null) return target
return profileStore.profile?.baseline_cigs_per_day || 0
})
const resistedCount = computed(() => homeSummary.value.resisted_count ?? 0)
const progressWidth = computed(() => {
const percent = Math.min((todayCount.value / dailyTarget.value) * 100, 100)
const target = dailyTarget.value
if (!target) return '0%'
const percent = Math.min((todayCount.value / target) * 100, 100)
return `${percent}%`
})
@@ -151,12 +183,21 @@ const resistedProgressWidth = computed(() => {
})
const changeText = computed(() => {
return '较昨日 -2'
const reduced = homeSummary.value.reduced_from_yesterday
if (reduced === undefined || reduced === null) return '较昨日 --'
if (reduced === 0) return '较昨日 0'
return homeSummary.value.exceeded_yesterday ? `较昨日 +${reduced}` : `较昨日 -${reduced}`
})
const changeClass = computed(() => {
return homeSummary.value.exceeded_yesterday ? 'stat-change-up' : 'stat-change-down'
})
const timerDisplay = computed(() => {
const baseMinutes = dashboardStore.minutesSinceLast ?? 165
const totalSeconds = baseMinutes * 60 + timerSeconds.value
if (timerBaseSeconds.value < 0) {
return '--:--:--'
}
const totalSeconds = timerBaseSeconds.value + timerSeconds.value
const hours = Math.floor(totalSeconds / 3600)
const minutes = Math.floor((totalSeconds % 3600) / 60)
const seconds = totalSeconds % 60
@@ -168,11 +209,16 @@ const timerGradient = computed(() => {
})
const nextSmokeTimeText = computed(() => {
if (dashboardStore.nextSmokeTime?.suggested_at) {
const date = new Date(dashboardStore.nextSmokeTime.suggested_at)
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
const timer = homeTimer.value
if (!timer) return ''
if (timer.next_suggested_clock) return timer.next_suggested_clock
if (timer.next_suggested_at) {
const date = new Date(timer.next_suggested_at)
if (!isNaN(date.getTime())) {
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
}
}
return '16:30'
return ''
})
const dialogTitle = computed(() => {
@@ -180,6 +226,10 @@ const dialogTitle = computed(() => {
})
function startTimer() {
stopTimer()
if (timerBaseSeconds.value < 0) {
return
}
timerInterval = setInterval(() => {
timerSeconds.value++
}, 1000)
@@ -202,29 +252,48 @@ function openResistedDialog() {
showDialog.value = true
}
function applyHomeData(data) {
homeData.value = data
const seconds = data?.timer?.seconds_since_last
timerBaseSeconds.value = typeof seconds === 'number' ? seconds : -1
timerSeconds.value = 0
startTimer()
}
async function fetchHomeData() {
const res = await api.getHome()
const data = res.data || {}
applyHomeData(data)
return data
}
async function handleSubmit(submitData) {
try {
if (dialogType.value === 'smoke') {
await api.createLog(submitData)
dashboardStore.resetTimer()
timerBaseSeconds.value = 0
timerSeconds.value = 0
startTimer()
uni.showToast({ title: '记录成功', icon: 'success' })
try {
await Promise.all([
dashboardStore.fetchDashboard(true),
dashboardStore.fetchNextSmokeTime()
])
await fetchHomeData()
} catch (e) {
console.error('refreshAfterSmokeLog error:', e)
console.error('refreshHomeData error:', e)
}
} else {
await api.createResistedLog({
smoke_time: submitData.smoke_time,
smoke_at: submitData.smoke_at,
remark: submitData.remark
remark: submitData.remark,
level: submitData.level,
num: submitData.num
})
resistedCount.value++
uni.showToast({ title: '太棒了!', icon: 'success' })
try {
await fetchHomeData()
} catch (e) {
console.error('refreshHomeData error:', e)
}
}
} catch (e) {
console.error('handleSubmit error:', e)
@@ -242,33 +311,27 @@ async function initPage() {
try {
await waitForLogin()
const [profileRes, dashboardRes, nextTimeRes] = await Promise.all([
api.getProfile(),
api.getDashboard(),
api.getNextSmokeTime()
])
const profile = profileRes.data.profile
const isCompleted = profileRes.data.is_completed ||
const home = await fetchHomeData()
const profileData = home.profile || {}
const profile = profileData.profile
const isCompleted = profileData.is_completed ||
(profile && profile.onboarding_completed_at) ||
(profile && profile.baseline_cigs_per_day > 0)
if (!profileRes.data.exists || !isCompleted) {
if (!profileData.exists || !isCompleted) {
uni.redirectTo({ url: '/pages/onboarding/index' })
return
}
profileStore.exists = profileRes.data.exists
profileStore.isCompleted = profileRes.data.is_completed
profileStore.profile = profileRes.data.profile
dashboardStore.setDashboard(dashboardRes.data)
dashboardStore.setNextSmokeTime(nextTimeRes.data)
startTimer()
profileStore.exists = !!profileData.exists
profileStore.isCompleted = !!isCompleted
profileStore.awakeMinutes = profileData.awake_minutes || 960
profileStore.baselineIntervalMinutes = profileData.baseline_interval_minutes || 60
if (profile) {
profileStore.profile = profile
}
} catch (e) {
console.error('initPage error:', e)
startTimer()
} finally {
loading.value = false
}