feat: 添加模式选择功能与页面更新

- 在 onboarding 页面中新增使用模式选择功能,用户可选择“戒烟打卡”或“记录抽烟”模式
- 更新个人资料页面以显示当前模式并允许用户切换模式
- 在 pages.json 中注册新的模式选择页面
- 优化首页和其他相关页面以适应新模式功能
This commit is contained in:
你çšnepiedg
2026-03-18 00:06:01 +08:00
parent d101515d8d
commit 31e504a997
10 changed files with 1818 additions and 465 deletions
+219
View File
@@ -0,0 +1,219 @@
<template>
<view class="page">
<view class="nav-placeholder" :style="{ height: navBarHeight + 'px' }"></view>
<view class="content">
<view class="hero">
<text class="eyebrow">首次进入先选模式</text>
<text class="title">你现在想怎么用这个小程序</text>
<text class="subtitle">先选戒烟打卡记录抽烟后续可以在个人中心随时切换</text>
</view>
<view
class="mode-card"
:class="{ 'mode-card-active': currentMode === 'quit' }"
@tap="selectMode('quit')"
>
<view class="mode-icon mode-icon-quit">🔥</view>
<view class="mode-main">
<text class="mode-title">戒烟打卡</text>
<text class="mode-desc">按天记录今天没抽用连续天数驱动坚持</text>
</view>
<text class="mode-arrow"></text>
</view>
<view
class="mode-card"
:class="{ 'mode-card-active': currentMode === 'record' }"
@tap="selectMode('record')"
>
<view class="mode-icon mode-icon-record">🚬</view>
<view class="mode-main">
<text class="mode-title">记录抽烟</text>
<text class="mode-desc">继续按支数记录观察自己的频率和变化趋势</text>
</view>
<text class="mode-arrow"></text>
</view>
<text class="footer-tip">当前选择{{ currentModeText }}</text>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useLogin } from '@/hooks/useLogin'
import { useUserStore } from '@/stores/user'
import { useProfileStore } from '@/stores/profile'
const userStore = useUserStore()
const profileStore = useProfileStore()
const { waitForLogin } = useLogin()
const navBarHeight = ref(0)
const submitting = ref(false)
const currentMode = computed(() => userStore.mode)
const currentModeText = computed(() => {
if (userStore.mode === 'quit') return '戒烟打卡'
if (userStore.mode === 'record') return '记录抽烟'
return '未选择'
})
function setupNavBar() {
const sys = uni.getSystemInfoSync()
const statusBarH = sys.statusBarHeight || 0
try {
const menuBtn = uni.getMenuButtonBoundingClientRect()
navBarHeight.value = menuBtn.bottom + (menuBtn.top - statusBarH)
} catch (e) {
navBarHeight.value = statusBarH + 44
}
}
async function selectMode(mode) {
if (submitting.value) return
submitting.value = true
userStore.setMode(mode)
try {
const profileData = await profileStore.saveProfile({ mode })
const profile = profileData.profile
const isCompleted = profileData.is_completed ||
(profile && profile.onboarding_completed_at) ||
(profile && profile.baseline_cigs_per_day > 0)
if (!profileData.exists || !isCompleted) {
uni.redirectTo({ url: '/pages/onboarding/index' })
return
}
uni.switchTab({ url: '/pages/index/index' })
} catch (e) {
console.error('selectMode error:', e)
uni.switchTab({ url: '/pages/index/index' })
} finally {
submitting.value = false
}
}
onMounted(async () => {
setupNavBar()
await waitForLogin()
})
</script>
<style scoped>
.page {
min-height: 100vh;
background:
radial-gradient(circle at top left, rgba(16, 185, 129, 0.18), transparent 34%),
linear-gradient(180deg, #ecfdf5 0%, #f7fee7 42%, #ffffff 100%);
}
.nav-placeholder {
background: transparent;
}
.content {
padding: 40rpx 32rpx 56rpx;
}
.hero {
margin-bottom: 40rpx;
}
.eyebrow {
display: inline-flex;
padding: 8rpx 18rpx;
border-radius: 999rpx;
background: rgba(16, 185, 129, 0.12);
color: #047857;
font-size: 22rpx;
margin-bottom: 20rpx;
}
.title {
display: block;
font-size: 48rpx;
font-weight: 700;
color: #111827;
line-height: 1.28;
}
.subtitle {
display: block;
margin-top: 16rpx;
font-size: 28rpx;
line-height: 1.6;
color: #4b5563;
}
.mode-card {
display: flex;
align-items: center;
gap: 24rpx;
padding: 30rpx 28rpx;
margin-bottom: 24rpx;
border-radius: 28rpx;
background: rgba(255, 255, 255, 0.82);
border: 2rpx solid rgba(255, 255, 255, 0.6);
box-shadow: 0 16rpx 44rpx rgba(15, 23, 42, 0.08);
}
.mode-card-active {
border-color: rgba(16, 185, 129, 0.35);
box-shadow: 0 20rpx 52rpx rgba(16, 185, 129, 0.14);
}
.mode-icon {
width: 92rpx;
height: 92rpx;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 42rpx;
flex-shrink: 0;
}
.mode-icon-quit {
background: linear-gradient(135deg, #d1fae5 0%, #86efac 100%);
}
.mode-icon-record {
background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
}
.mode-main {
flex: 1;
}
.mode-title {
display: block;
font-size: 34rpx;
font-weight: 700;
color: #111827;
}
.mode-desc {
display: block;
margin-top: 10rpx;
font-size: 25rpx;
line-height: 1.5;
color: #6b7280;
}
.mode-arrow {
font-size: 42rpx;
color: #9ca3af;
}
.footer-tip {
display: block;
margin-top: 20rpx;
font-size: 24rpx;
color: #6b7280;
text-align: center;
}
</style>