bdb8d425eb
Made-with: Cursor
461 lines
9.6 KiB
Vue
461 lines
9.6 KiB
Vue
<template>
|
||
<view class="page">
|
||
<view class="nav-area" :style="{ paddingTop: navBarHeight + 'px' }">
|
||
<view class="nav-row">
|
||
<text class="step-indicator">{{ step }} / {{ totalSteps }}</text>
|
||
</view>
|
||
<view class="progress-bar">
|
||
<view class="progress-fill" :style="{ width: progressWidth }"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="content">
|
||
<view v-if="step === 1" class="step">
|
||
<text class="step-title">你每天抽多少支烟?</text>
|
||
<text class="step-desc">这将帮助我们为你制定个性化的戒烟计划</text>
|
||
<view class="input-group">
|
||
<view class="input-row">
|
||
<view class="input-btn" @tap="decreaseCigs">-</view>
|
||
<text class="input-value">{{ formData.baseline_cigs_per_day }}</text>
|
||
<view class="input-btn" @tap="increaseCigs">+</view>
|
||
</view>
|
||
<text class="input-unit">支/天</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="step === 2" class="step">
|
||
<text class="step-title">你的烟龄是多久?</text>
|
||
<text class="step-desc">了解你的吸烟历史有助于更好地帮助你</text>
|
||
<view class="options">
|
||
<view
|
||
v-for="option in smokingYearsOptions"
|
||
:key="option.value"
|
||
class="option"
|
||
:class="{ 'option-active': formData.smoking_years === option.value }"
|
||
@tap="formData.smoking_years = option.value"
|
||
>
|
||
{{ option.label }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="step === 3" class="step">
|
||
<text class="step-title">你为什么想戒烟?</text>
|
||
<text class="step-desc">选择对你最重要的原因(可多选)</text>
|
||
<view class="options options-wrap">
|
||
<view
|
||
v-for="option in quitMotivationOptions"
|
||
:key="option"
|
||
class="option option-tag"
|
||
:class="{ 'option-active': formData.quit_motivations.includes(option) }"
|
||
@tap="toggleMotivation(option)"
|
||
>
|
||
{{ option }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="step === 4" class="step">
|
||
<text class="step-title">你通常什么时候起床和睡觉?</text>
|
||
<text class="step-desc">我们会在你的休息时间避免打扰你</text>
|
||
<view class="time-row">
|
||
<view class="time-item">
|
||
<text class="time-label">起床时间</text>
|
||
<picker mode="time" :value="formData.wake_up_time" @change="onWakeTimeChange">
|
||
<view class="time-picker">{{ formData.wake_up_time }}</view>
|
||
</picker>
|
||
</view>
|
||
<view class="time-item">
|
||
<text class="time-label">睡觉时间</text>
|
||
<picker mode="time" :value="formData.sleep_time" @change="onSleepTimeChange">
|
||
<view class="time-picker">{{ formData.sleep_time }}</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="step === 5" class="step">
|
||
<text class="step-title">每包烟多少钱?</text>
|
||
<text class="step-desc">我们会帮你计算省下的钱</text>
|
||
<view class="input-group">
|
||
<view class="price-input">
|
||
<text class="price-prefix">¥</text>
|
||
<input
|
||
type="digit"
|
||
v-model="priceYuan"
|
||
class="price-field"
|
||
placeholder="0"
|
||
placeholder-style="color: #6B7280"
|
||
/>
|
||
</view>
|
||
<text class="input-unit">元/包</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="footer">
|
||
<view v-if="step > 1" class="btn-secondary" @tap="prevStep">上一步</view>
|
||
<view class="btn-primary" :class="{ 'btn-full': step === 1 }" @tap="nextStep">
|
||
{{ step === 5 ? '开始戒烟之旅 🚀' : '下一步' }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { onShareAppMessage } from '@dcloudio/uni-app'
|
||
import { useProfileStore } from '@/stores/profile'
|
||
import { useLogin } from '@/hooks/useLogin'
|
||
|
||
const profileStore = useProfileStore()
|
||
const { waitForLogin } = useLogin()
|
||
|
||
const navBarHeight = ref(0)
|
||
const step = ref(1)
|
||
const totalSteps = 5
|
||
|
||
const formData = ref({
|
||
baseline_cigs_per_day: 10,
|
||
smoking_years: 5,
|
||
quit_motivations: [],
|
||
smoke_motivations: [],
|
||
wake_up_time: '07:30',
|
||
sleep_time: '23:00',
|
||
pack_price_cent: 2500
|
||
})
|
||
|
||
const priceYuan = ref('25')
|
||
|
||
const progressWidth = computed(() => `${(step.value / totalSteps) * 100}%`)
|
||
|
||
const smokingYearsOptions = [
|
||
{ label: '少于1年', value: 1 },
|
||
{ label: '1-3年', value: 2 },
|
||
{ label: '3-5年', value: 4 },
|
||
{ label: '5-10年', value: 7 },
|
||
{ label: '10年以上', value: 15 }
|
||
]
|
||
|
||
const quitMotivationOptions = [
|
||
'身体健康',
|
||
'家人孩子',
|
||
'省钱',
|
||
'形象气质',
|
||
'工作需要',
|
||
'伴侣要求'
|
||
]
|
||
|
||
function increaseCigs() {
|
||
formData.value.baseline_cigs_per_day++
|
||
}
|
||
|
||
function decreaseCigs() {
|
||
if (formData.value.baseline_cigs_per_day > 1) {
|
||
formData.value.baseline_cigs_per_day--
|
||
}
|
||
}
|
||
|
||
function toggleMotivation(option) {
|
||
const index = formData.value.quit_motivations.indexOf(option)
|
||
if (index > -1) {
|
||
formData.value.quit_motivations.splice(index, 1)
|
||
} else {
|
||
formData.value.quit_motivations.push(option)
|
||
}
|
||
}
|
||
|
||
function onWakeTimeChange(e) {
|
||
formData.value.wake_up_time = e.detail.value
|
||
}
|
||
|
||
function onSleepTimeChange(e) {
|
||
formData.value.sleep_time = e.detail.value
|
||
}
|
||
|
||
function prevStep() {
|
||
if (step.value > 1) {
|
||
step.value--
|
||
}
|
||
}
|
||
|
||
async function nextStep() {
|
||
if (step.value < totalSteps) {
|
||
step.value++
|
||
return
|
||
}
|
||
|
||
formData.value.pack_price_cent = Math.round(parseFloat(priceYuan.value || '0') * 100)
|
||
|
||
try {
|
||
uni.showLoading({ title: '保存中...' })
|
||
await profileStore.saveProfile(formData.value)
|
||
uni.hideLoading()
|
||
uni.switchTab({ url: '/pages/index/index' })
|
||
} catch (e) {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '保存失败', icon: 'none' })
|
||
}
|
||
}
|
||
|
||
onMounted(async () => {
|
||
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
|
||
}
|
||
await waitForLogin()
|
||
})
|
||
|
||
onShareAppMessage(() => {
|
||
return {
|
||
title: '戒烟助手 - 帮你定制戒烟计划',
|
||
path: 'pages/index/index'
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page {
|
||
min-height: 100vh;
|
||
background: linear-gradient(to bottom, #D1FAE5 0%, #F0FDF4 45%, #FFFFFF 100%);
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.nav-area {
|
||
padding-left: 32rpx;
|
||
padding-right: 32rpx;
|
||
background: linear-gradient(to bottom, #D1FAE5, #E9FDF2);
|
||
}
|
||
|
||
.nav-row {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 12rpx 0;
|
||
}
|
||
|
||
.step-indicator {
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
color: #059669;
|
||
background-color: rgba(255, 255, 255, 0.7);
|
||
padding: 6rpx 24rpx;
|
||
border-radius: 999rpx;
|
||
}
|
||
|
||
.progress-bar {
|
||
height: 6rpx;
|
||
background-color: rgba(255, 255, 255, 0.5);
|
||
border-radius: 999rpx;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #10B981, #34D399);
|
||
border-radius: 999rpx;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.content {
|
||
flex: 1;
|
||
padding: 0 48rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
}
|
||
|
||
.step {
|
||
animation: fadeIn 0.3s ease;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(20rpx);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.step-title {
|
||
font-size: 44rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
display: block;
|
||
margin-bottom: 16rpx;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.step-desc {
|
||
font-size: 28rpx;
|
||
color: #6B7280;
|
||
display: block;
|
||
margin-bottom: 56rpx;
|
||
}
|
||
|
||
.input-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.input-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 48rpx;
|
||
}
|
||
|
||
.input-btn {
|
||
width: 96rpx;
|
||
height: 96rpx;
|
||
border-radius: 50%;
|
||
background-color: #FFFFFF;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 48rpx;
|
||
color: #10B981;
|
||
border: 2rpx solid #ECFDF3;
|
||
box-shadow: 0 8rpx 20rpx rgba(16, 185, 129, 0.12);
|
||
}
|
||
|
||
.input-value {
|
||
font-size: 96rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
min-width: 160rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.input-unit {
|
||
font-size: 28rpx;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.options {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.options-wrap {
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.option {
|
||
padding: 28rpx 36rpx;
|
||
background-color: #FFFFFF;
|
||
border-radius: 16rpx;
|
||
font-size: 30rpx;
|
||
color: #111827;
|
||
border: 2rpx solid #ECFDF3;
|
||
box-shadow: 0 4rpx 12rpx rgba(16, 185, 129, 0.06);
|
||
}
|
||
|
||
.option-tag {
|
||
padding: 20rpx 28rpx;
|
||
border-radius: 32rpx;
|
||
}
|
||
|
||
.option-active {
|
||
background-color: #ECFDF5;
|
||
border-color: #10B981;
|
||
color: #059669;
|
||
}
|
||
|
||
.time-row {
|
||
display: flex;
|
||
gap: 32rpx;
|
||
}
|
||
|
||
.time-item { flex: 1; }
|
||
|
||
.time-label {
|
||
font-size: 26rpx;
|
||
color: #6B7280;
|
||
display: block;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.time-picker {
|
||
background-color: #FFFFFF;
|
||
padding: 32rpx;
|
||
border-radius: 16rpx;
|
||
font-size: 40rpx;
|
||
color: #111827;
|
||
text-align: center;
|
||
border: 2rpx solid #ECFDF3;
|
||
box-shadow: 0 4rpx 12rpx rgba(16, 185, 129, 0.06);
|
||
}
|
||
|
||
.price-input {
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: #FFFFFF;
|
||
padding: 24rpx 32rpx;
|
||
border-radius: 16rpx;
|
||
gap: 8rpx;
|
||
border: 2rpx solid #ECFDF3;
|
||
box-shadow: 0 4rpx 12rpx rgba(16, 185, 129, 0.06);
|
||
}
|
||
|
||
.price-prefix {
|
||
font-size: 48rpx;
|
||
color: #9CA3AF;
|
||
}
|
||
|
||
.price-field {
|
||
font-size: 64rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
width: 200rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.footer {
|
||
display: flex;
|
||
gap: 24rpx;
|
||
padding: 32rpx 48rpx;
|
||
padding-bottom: 64rpx;
|
||
}
|
||
|
||
.btn-primary {
|
||
flex: 1;
|
||
height: 96rpx;
|
||
background-color: #10B981;
|
||
border-radius: 48rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
color: #FFFFFF;
|
||
box-shadow: 0 12rpx 28rpx rgba(16, 185, 129, 0.25);
|
||
}
|
||
|
||
.btn-full { flex: 1; }
|
||
|
||
.btn-secondary {
|
||
height: 96rpx;
|
||
padding: 0 48rpx;
|
||
background-color: #FFFFFF;
|
||
border-radius: 48rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 32rpx;
|
||
color: #111827;
|
||
border: 2rpx solid #E5E7EB;
|
||
}
|
||
</style>
|