feat: Enhance onboarding and profile pages with improved navigation and UI updates
Updated the onboarding page to include a navigation area with a step indicator and adjusted the progress bar styling. In the profile page, simplified the user section, added options for clearing cache and copying feedback email, and improved layout consistency. Enhanced overall visual design with a light green gradient theme.
This commit is contained in:
@@ -1,8 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page">
|
<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-bar">
|
||||||
<view class="progress-fill" :style="{ width: progressWidth }"></view>
|
<view class="progress-fill" :style="{ width: progressWidth }"></view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="content">
|
<view class="content">
|
||||||
<view v-if="step === 1" class="step">
|
<view v-if="step === 1" class="step">
|
||||||
@@ -105,6 +110,7 @@ import { useLogin } from '@/hooks/useLogin'
|
|||||||
const profileStore = useProfileStore()
|
const profileStore = useProfileStore()
|
||||||
const { waitForLogin } = useLogin()
|
const { waitForLogin } = useLogin()
|
||||||
|
|
||||||
|
const navBarHeight = ref(0)
|
||||||
const step = ref(1)
|
const step = ref(1)
|
||||||
const totalSteps = 5
|
const totalSteps = 5
|
||||||
|
|
||||||
@@ -192,6 +198,14 @@ async function nextStep() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
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()
|
await waitForLogin()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -204,20 +218,48 @@ onMounted(async () => {
|
|||||||
flex-direction: column;
|
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 {
|
.progress-bar {
|
||||||
height: 8rpx;
|
height: 6rpx;
|
||||||
background-color: #E5E7EB;
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
border-radius: 999rpx;
|
||||||
|
margin-top: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-fill {
|
.progress-fill {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(90deg, #10B981, #34D399);
|
background: linear-gradient(90deg, #10B981, #34D399);
|
||||||
|
border-radius: 999rpx;
|
||||||
transition: width 0.3s ease;
|
transition: width 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 64rpx 48rpx;
|
padding: 0 48rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step {
|
.step {
|
||||||
@@ -227,11 +269,11 @@ onMounted(async () => {
|
|||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(20rpx);
|
transform: translateY(20rpx);
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateX(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +290,7 @@ onMounted(async () => {
|
|||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #6B7280;
|
color: #6B7280;
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 64rpx;
|
margin-bottom: 56rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
@@ -377,7 +419,6 @@ onMounted(async () => {
|
|||||||
gap: 24rpx;
|
gap: 24rpx;
|
||||||
padding: 32rpx 48rpx;
|
padding: 32rpx 48rpx;
|
||||||
padding-bottom: 64rpx;
|
padding-bottom: 64rpx;
|
||||||
background-color: #FFFFFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
|
|||||||
+54
-129
@@ -1,34 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="user-section">
|
<view class="user-section">
|
||||||
<view class="avatar-wrapper">
|
|
||||||
<image class="avatar" :src="userAvatar" mode="aspectFill"></image>
|
<image class="avatar" :src="userAvatar" mode="aspectFill"></image>
|
||||||
<view class="avatar-edit">📷</view>
|
|
||||||
</view>
|
|
||||||
<text class="user-name">{{ userName }}</text>
|
<text class="user-name">{{ userName }}</text>
|
||||||
<view class="goal-badge">
|
|
||||||
<text>目标:{{ goalDate }} 戒烟</text>
|
|
||||||
<text class="goal-icon">🎯</text>
|
|
||||||
</view>
|
|
||||||
<text class="streak-text">已连续戒烟 {{ streakDays }} 天 🔥</text>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="section">
|
<view class="section">
|
||||||
<text class="section-title">我的进程</text>
|
|
||||||
<view class="menu-list">
|
<view class="menu-list">
|
||||||
<view class="menu-item" @tap="goPage('goal')">
|
<view class="menu-item" @tap="goOnboarding">
|
||||||
<view class="menu-icon menu-icon-green">🎯</view>
|
<view class="menu-icon menu-icon-green">📝</view>
|
||||||
<view class="menu-content">
|
<view class="menu-content">
|
||||||
<text class="menu-label">目标设定</text>
|
<text class="menu-label">重新填写问卷</text>
|
||||||
<text class="menu-desc">调整每日限额与戒烟日期</text>
|
<text class="menu-desc">修改吸烟基线与个人信息</text>
|
||||||
</view>
|
|
||||||
<text class="menu-arrow">›</text>
|
|
||||||
</view>
|
|
||||||
<view class="menu-item" @tap="goPage('ai-plan')">
|
|
||||||
<view class="menu-icon menu-icon-blue">🤖</view>
|
|
||||||
<view class="menu-content">
|
|
||||||
<text class="menu-label">AI 计划调整</text>
|
|
||||||
<text class="menu-desc">个性化辅导风格</text>
|
|
||||||
</view>
|
</view>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -36,40 +19,18 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="section">
|
<view class="section">
|
||||||
<text class="section-title">偏好设置</text>
|
|
||||||
<view class="menu-list">
|
<view class="menu-list">
|
||||||
<view class="menu-item" @tap="goPage('notification')">
|
<view class="menu-item" @tap="clearCache">
|
||||||
<view class="menu-icon menu-icon-orange">🔔</view>
|
<view class="menu-icon menu-icon-gray">🗑️</view>
|
||||||
<view class="menu-content">
|
<view class="menu-content">
|
||||||
<text class="menu-label">通知设置</text>
|
<text class="menu-label">清除缓存</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-item" @tap="goPage('vip')">
|
<view class="menu-item" @tap="copyInfo">
|
||||||
<view class="menu-icon menu-icon-yellow">💎</view>
|
<view class="menu-icon menu-icon-gray">💬</view>
|
||||||
<view class="menu-content">
|
<view class="menu-content">
|
||||||
<text class="menu-label">解锁会员</text>
|
<text class="menu-label">意见反馈</text>
|
||||||
<view class="pro-badge">PRO</view>
|
|
||||||
</view>
|
|
||||||
<text class="menu-arrow">›</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="section">
|
|
||||||
<text class="section-title">通用</text>
|
|
||||||
<view class="menu-list">
|
|
||||||
<view class="menu-item" @tap="goPage('settings')">
|
|
||||||
<view class="menu-icon menu-icon-gray">⚙️</view>
|
|
||||||
<view class="menu-content">
|
|
||||||
<text class="menu-label">基础设置</text>
|
|
||||||
</view>
|
|
||||||
<text class="menu-arrow">›</text>
|
|
||||||
</view>
|
|
||||||
<view class="menu-item" @tap="goPage('privacy')">
|
|
||||||
<view class="menu-icon menu-icon-gray">🔒</view>
|
|
||||||
<view class="menu-content">
|
|
||||||
<text class="menu-label">隐私与数据</text>
|
|
||||||
</view>
|
</view>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -85,20 +46,44 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { useLogin } from '@/hooks/useLogin'
|
import { useLogin } from '@/hooks/useLogin'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { waitForLogin } = useLogin()
|
const { waitForLogin } = useLogin()
|
||||||
|
|
||||||
const userName = computed(() => userStore.user?.nickname || 'Alex Doe')
|
const userName = computed(() => userStore.user?.nickname || '戒烟用户')
|
||||||
const userAvatar = computed(() => userStore.user?.avatar_url || '/static/images/default-avatar.png')
|
const userAvatar = computed(() => userStore.user?.avatar_url || '/static/images/default-avatar.png')
|
||||||
const goalDate = ref('12月1日')
|
|
||||||
const streakDays = ref(12)
|
|
||||||
|
|
||||||
function goPage(page) {
|
function goOnboarding() {
|
||||||
uni.showToast({ title: '功能开发中', icon: 'none' })
|
uni.navigateTo({ url: '/pages/onboarding/index' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCache() {
|
||||||
|
uni.showModal({
|
||||||
|
title: '清除缓存',
|
||||||
|
content: '将清除本地缓存数据,不会影响云端记录',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
try {
|
||||||
|
uni.clearStorageSync()
|
||||||
|
uni.showToast({ title: '缓存已清除', icon: 'success' })
|
||||||
|
} catch (e) {
|
||||||
|
uni.showToast({ title: '清除失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyInfo() {
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: '806669289@qq.com',
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({ title: '反馈邮箱已复制', icon: 'success' })
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
@@ -124,7 +109,7 @@ onMounted(async () => {
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: linear-gradient(to bottom, #D1FAE5 0%, #F0FDF4 45%, #FFFFFF 100%);
|
background: linear-gradient(to bottom, #D1FAE5 0%, #F0FDF4 45%, #FFFFFF 100%);
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
padding-bottom: 120rpx;
|
padding-bottom: 160rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,70 +117,25 @@ onMounted(async () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 48rpx 0;
|
padding: 48rpx 0 40rpx;
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-wrapper {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 24rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
width: 160rpx;
|
width: 140rpx;
|
||||||
height: 160rpx;
|
height: 140rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 6rpx solid #10B981;
|
border: 4rpx solid #A7F3D0;
|
||||||
background-color: #F3F4F6;
|
background-color: #ECFDF5;
|
||||||
}
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
.avatar-edit {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 48rpx;
|
|
||||||
height: 48rpx;
|
|
||||||
background-color: #10B981;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 24rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-name {
|
.user-name {
|
||||||
font-size: 40rpx;
|
font-size: 38rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #111827;
|
color: #111827;
|
||||||
margin-bottom: 16rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.goal-badge {
|
.section { margin-bottom: 24rpx; }
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8rpx;
|
|
||||||
background-color: #EF4444;
|
|
||||||
color: #FFFFFF;
|
|
||||||
padding: 12rpx 24rpx;
|
|
||||||
border-radius: 32rpx;
|
|
||||||
font-size: 24rpx;
|
|
||||||
margin-bottom: 12rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goal-icon { font-size: 24rpx; }
|
|
||||||
|
|
||||||
.streak-text {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #6B7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section { margin-bottom: 32rpx; }
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #6B7280;
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-list {
|
.menu-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -209,7 +149,7 @@ onMounted(async () => {
|
|||||||
gap: 24rpx;
|
gap: 24rpx;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 24rpx;
|
padding: 28rpx 24rpx;
|
||||||
border: 2rpx solid #ECFDF3;
|
border: 2rpx solid #ECFDF3;
|
||||||
box-shadow: 0 8rpx 20rpx rgba(16, 185, 129, 0.08);
|
box-shadow: 0 8rpx 20rpx rgba(16, 185, 129, 0.08);
|
||||||
}
|
}
|
||||||
@@ -225,9 +165,6 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon-green { background-color: #DCFCE7; }
|
.menu-icon-green { background-color: #DCFCE7; }
|
||||||
.menu-icon-blue { background-color: #DBEAFE; }
|
|
||||||
.menu-icon-orange { background-color: #FFEDD5; }
|
|
||||||
.menu-icon-yellow { background-color: #FEF3C7; }
|
|
||||||
.menu-icon-gray { background-color: #F3F4F6; }
|
.menu-icon-gray { background-color: #F3F4F6; }
|
||||||
|
|
||||||
.menu-content {
|
.menu-content {
|
||||||
@@ -247,18 +184,6 @@ onMounted(async () => {
|
|||||||
color: #6B7280;
|
color: #6B7280;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pro-badge {
|
|
||||||
display: inline-block;
|
|
||||||
background-color: #10B981;
|
|
||||||
color: #FFFFFF;
|
|
||||||
font-size: 20rpx;
|
|
||||||
padding: 4rpx 12rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-top: 4rpx;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-arrow {
|
.menu-arrow {
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
color: #9CA3AF;
|
color: #9CA3AF;
|
||||||
@@ -266,8 +191,8 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.logout-btn {
|
.logout-btn {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 24rpx;
|
padding: 28rpx;
|
||||||
margin-top: 32rpx;
|
margin-top: 40rpx;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
border: 2rpx solid #FEE2E2;
|
border: 2rpx solid #FEE2E2;
|
||||||
@@ -281,8 +206,8 @@ onMounted(async () => {
|
|||||||
.version {
|
.version {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24rpx;
|
font-size: 22rpx;
|
||||||
color: #9CA3AF;
|
color: #9CA3AF;
|
||||||
margin-top: 24rpx;
|
margin-top: 32rpx;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user