5487ece142
- 新增 profile/login.vue 登录页(UnauthLoginPanel) - 新增 profile/setup.vue 完善信息页(头像/昵称/手机号) - profile/index 未登录态显示头像占位+点击登录+菜单列表 - 登录后始终跳转setup页,completeLogin后才持久化token - auth.js 登录后用pendingToken(内存),不检查profile_completed - request.js 401时reLaunch到登录页,不显示toast,防重复跳转 - 约课页移除UnauthLoginPanel,未登录直接显示列表 - session.js 新增needsSetup状态和completeLogin方法
266 lines
5.8 KiB
Vue
266 lines
5.8 KiB
Vue
<template>
|
|
<view class="setup-page">
|
|
<view class="setup-card">
|
|
<view class="avatar-row">
|
|
<button class="avatar-picker" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
|
|
<image v-if="avatarUrl" :src="avatarUrl" mode="aspectFill" class="avatar-img" />
|
|
<view v-else class="avatar-placeholder">
|
|
<text class="avatar-placeholder-text">选择头像</text>
|
|
</view>
|
|
</button>
|
|
</view>
|
|
|
|
<view class="field-row">
|
|
<text class="field-label">昵称</text>
|
|
<input
|
|
type="nickname"
|
|
class="field-input"
|
|
placeholder="请输入昵称"
|
|
:value="nickname"
|
|
@blur="onNicknameBlur"
|
|
/>
|
|
</view>
|
|
|
|
<view class="field-row">
|
|
<text class="field-label">手机号</text>
|
|
<view class="phone-area">
|
|
<text v-if="phone" class="phone-text">{{ phone }}</text>
|
|
<button class="phone-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhone">
|
|
{{ phone ? '更换' : '获取' }}
|
|
</button>
|
|
</view>
|
|
</view>
|
|
|
|
<button
|
|
class="submit-button"
|
|
:class="{ disabled: submitting || !canSubmit }"
|
|
:disabled="submitting || !canSubmit"
|
|
@tap="onSubmit"
|
|
>
|
|
{{ submitting ? '提交中...' : '完成' }}
|
|
</button>
|
|
|
|
<view class="home-link" @tap="goHome">返回首页</view>
|
|
</view>
|
|
|
|
<AgreementLinks ref="agreementLinksRef" :show-entry="false" />
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from 'vue'
|
|
import { memberApi } from '@/modules/comprehensive/api/member'
|
|
import { authStorage } from '@/modules/comprehensive/api/auth'
|
|
import { useComprehensiveSessionStore } from '@/modules/comprehensive/stores/session'
|
|
import AgreementLinks from '@/components/AgreementLinks.vue'
|
|
|
|
const session = useComprehensiveSessionStore()
|
|
const avatarUrl = ref(session.member?.avatar_url || '')
|
|
const nickname = ref(session.member?.nickname || '')
|
|
const phone = ref(session.member?.phone || '')
|
|
const submitting = ref(false)
|
|
|
|
const canSubmit = computed(() => !!avatarUrl.value && !!nickname.value.trim())
|
|
|
|
const onChooseAvatar = async (e) => {
|
|
if (!e.detail.avatarUrl) return
|
|
try {
|
|
const data = await memberApi.uploadAvatar(e.detail.avatarUrl)
|
|
avatarUrl.value = data.avatar_url
|
|
} catch {
|
|
avatarUrl.value = e.detail.avatarUrl
|
|
}
|
|
}
|
|
|
|
const onNicknameBlur = (e) => {
|
|
nickname.value = e.detail.value || ''
|
|
}
|
|
|
|
const onGetPhone = async (e) => {
|
|
if (e.detail.errMsg !== 'getPhoneNumber:ok') return
|
|
try {
|
|
const member = await memberApi.bindMemberPhone(e.detail.code)
|
|
phone.value = member.phone || ''
|
|
} catch {
|
|
uni.showToast({ title: '获取手机号失败', icon: 'none' })
|
|
}
|
|
}
|
|
|
|
const onSubmit = async () => {
|
|
if (!canSubmit.value || submitting.value) return
|
|
submitting.value = true
|
|
try {
|
|
await memberApi.updateMemberProfile({
|
|
nickname: nickname.value.trim(),
|
|
avatar_url: avatarUrl.value
|
|
})
|
|
session.completeLogin()
|
|
uni.showToast({ title: '设置成功', icon: 'success' })
|
|
setTimeout(() => {
|
|
uni.switchTab({ url: '/pages/profile/index' })
|
|
}, 500)
|
|
} catch {
|
|
uni.showToast({ title: '提交失败,请重试', icon: 'none' })
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
|
|
const goHome = () => {
|
|
authStorage.clear()
|
|
session.member = null
|
|
session.needsSetup = false
|
|
uni.switchTab({ url: '/pages/home/index' })
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.setup-page {
|
|
min-height: 100vh;
|
|
padding: 0 $spacing-lg 48rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
background: $bg-color-soft;
|
|
}
|
|
|
|
.setup-card {
|
|
width: 100%;
|
|
max-width: 560rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: $spacing-md;
|
|
}
|
|
|
|
.avatar-row {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-bottom: $spacing-sm;
|
|
}
|
|
|
|
.avatar-picker {
|
|
width: 144rpx;
|
|
height: 144rpx;
|
|
padding: 0;
|
|
border-radius: 50%;
|
|
overflow: hidden;
|
|
background: $bg-color-hover;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
line-height: 1;
|
|
}
|
|
|
|
.avatar-picker::after {
|
|
border: none;
|
|
}
|
|
|
|
.avatar-img {
|
|
width: 144rpx;
|
|
height: 144rpx;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.avatar-placeholder {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.avatar-placeholder-text {
|
|
font-size: 20rpx;
|
|
color: $text-color-light;
|
|
}
|
|
|
|
.field-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: $spacing-md;
|
|
height: 96rpx;
|
|
padding: 0 $spacing-md;
|
|
border-radius: $border-radius-base;
|
|
background: $bg-color-main;
|
|
}
|
|
|
|
.field-label {
|
|
flex-shrink: 0;
|
|
color: $text-color-muted;
|
|
font-size: 28rpx;
|
|
font-weight: 700;
|
|
width: 100rpx;
|
|
text-align: left;
|
|
}
|
|
|
|
.field-input {
|
|
flex: 1;
|
|
text-align: right;
|
|
font-size: 28rpx;
|
|
color: $text-color-main;
|
|
}
|
|
|
|
.phone-area {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: $spacing-sm;
|
|
}
|
|
|
|
.phone-text {
|
|
color: $text-color-main;
|
|
font-size: 28rpx;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.phone-btn {
|
|
font-size: 24rpx;
|
|
font-weight: 700;
|
|
color: $color-primary;
|
|
background: $color-primary-light;
|
|
padding: 8rpx 24rpx;
|
|
border-radius: $border-radius-sm;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.phone-btn::after {
|
|
border: none;
|
|
}
|
|
|
|
.submit-button {
|
|
width: 100%;
|
|
height: 88rpx;
|
|
border-radius: $border-radius-base;
|
|
color: #FFFFFF;
|
|
background-color: $text-color-main;
|
|
font-size: 28rpx;
|
|
font-weight: 700;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-top: $spacing-sm;
|
|
}
|
|
|
|
.submit-button::after {
|
|
border: none;
|
|
}
|
|
|
|
.submit-button.disabled,
|
|
.submit-button[disabled] {
|
|
color: #FFFFFF;
|
|
background-color: $text-color-light;
|
|
}
|
|
|
|
.home-link {
|
|
text-align: center;
|
|
color: $text-color-muted;
|
|
font-size: 26rpx;
|
|
padding: $spacing-sm 0;
|
|
}
|
|
</style>
|