326 lines
7.3 KiB
Vue
326 lines
7.3 KiB
Vue
<template>
|
||||
|
|
<view class="profile-form-page" :class="`theme-${themeMode}`">
|
|||
|
|
<view class="form-card">
|
|||
|
|
<view class="form-header">
|
|||
|
|
<text class="form-title">修改资料</text>
|
|||
|
|
<text class="form-desc">用户名、邮箱和手机号为账号信息,暂不支持修改</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<u-form :model="form" :border-bottom="false" label-position="top" label-width="120">
|
|||
|
|
<u-form-item label="用户名" :border-bottom="false">
|
|||
|
|
<view class="field-card locked">
|
|||
|
|
<text class="field-text locked-text">{{ form.username || '-' }}</text>
|
|||
|
|
</view>
|
|||
|
|
</u-form-item>
|
|||
|
|
<u-form-item label="邮箱" :border-bottom="false">
|
|||
|
|
<view class="field-card locked">
|
|||
|
|
<text class="field-text locked-text">{{ form.email || '-' }}</text>
|
|||
|
|
</view>
|
|||
|
|
</u-form-item>
|
|||
|
|
<u-form-item label="手机号" :border-bottom="false">
|
|||
|
|
<view class="field-card locked">
|
|||
|
|
<text class="field-text locked-text">{{ form.phone || '暂未绑定手机号' }}</text>
|
|||
|
|
</view>
|
|||
|
|
</u-form-item>
|
|||
|
|
<u-form-item label="昵称" prop="nickname" required :border-bottom="false">
|
|||
|
|
<view class="field-card">
|
|||
|
|
<u-input v-model="form.nickname" clearable placeholder="请输入昵称" placeholder-style="color: #a0a7b5;" />
|
|||
|
|
</view>
|
|||
|
|
</u-form-item>
|
|||
|
|
<u-form-item label="性别" prop="gender" :border-bottom="false">
|
|||
|
|
<view class="gender-options">
|
|||
|
|
<view
|
|||
|
|
v-for="item in genderOptions"
|
|||
|
|
:key="item.value"
|
|||
|
|
class="gender-option"
|
|||
|
|
:class="{ active: form.gender === item.value }"
|
|||
|
|
@tap="form.gender = item.value"
|
|||
|
|
>
|
|||
|
|
<view class="gender-dot"></view>
|
|||
|
|
<text>{{ item.label }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</u-form-item>
|
|||
|
|
<u-form-item label="生日" prop="birthday" :border-bottom="false">
|
|||
|
|
<picker mode="date" :value="form.birthday || today" start="1900-01-01" :end="today" @change="handleBirthdayChange">
|
|||
|
|
<view class="field-card picker-card">
|
|||
|
|
<text class="field-text" :class="{ placeholder: !form.birthday }">{{ form.birthday || '请选择生日' }}</text>
|
|||
|
|
<u-icon name="calendar" color="#a0a7b5" size="24" />
|
|||
|
|
</view>
|
|||
|
|
</picker>
|
|||
|
|
</u-form-item>
|
|||
|
|
<u-form-item label="个人简介" prop="bio" :border-bottom="false">
|
|||
|
|
<view class="textarea-card">
|
|||
|
|
<textarea v-model="form.bio" maxlength="120" placeholder="请输入个人简介" placeholder-style="color: #a0a7b5;" />
|
|||
|
|
</view>
|
|||
|
|
</u-form-item>
|
|||
|
|
</u-form>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view class="bottom-actions">
|
|||
|
|
<u-button type="primary" shape="circle" size="large" :loading="submitting" @click="submitForm">保存资料</u-button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import { getUserInfo, updateUserInfo } from '@/api/member.js'
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
submitting: false,
|
|||
|
|
today: '',
|
|||
|
|
form: {
|
|||
|
|
username: '',
|
|||
|
|
email: '',
|
|||
|
|
phone: '',
|
|||
|
|
nickname: '',
|
|||
|
|
gender: 0,
|
|||
|
|
birthday: '',
|
|||
|
|
bio: ''
|
|||
|
|
},
|
|||
|
|
genderOptions: [
|
|||
|
|
{ label: '男', value: 1 },
|
|||
|
|
{ label: '女', value: 2 },
|
|||
|
|
{ label: '其他', value: 0 }
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
onLoad() {
|
|||
|
|
this.today = this.getToday()
|
|||
|
|
this.loadProfile()
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
getToday() {
|
|||
|
|
const now = new Date()
|
|||
|
|
const year = now.getFullYear()
|
|||
|
|
const month = String(now.getMonth() + 1).padStart(2, '0')
|
|||
|
|
const day = String(now.getDate()).padStart(2, '0')
|
|||
|
|
return `${year}-${month}-${day}`
|
|||
|
|
},
|
|||
|
|
async loadProfile() {
|
|||
|
|
try {
|
|||
|
|
const profile = await getUserInfo()
|
|||
|
|
this.form = {
|
|||
|
|
username: profile.username || '',
|
|||
|
|
email: profile.email || '',
|
|||
|
|
phone: profile.phone || '',
|
|||
|
|
nickname: profile.nickname || profile.username || '',
|
|||
|
|
gender: Number(profile.gender || 0),
|
|||
|
|
birthday: profile.birthday || '',
|
|||
|
|
bio: profile.bio || ''
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[scanapp] 加载资料失败', error)
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
handleBirthdayChange(e) {
|
|||
|
|
this.form.birthday = e.detail.value
|
|||
|
|
},
|
|||
|
|
validateForm() {
|
|||
|
|
if (!this.form.nickname.trim()) {
|
|||
|
|
uni.showToast({ title: '请输入昵称', icon: 'none' })
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
},
|
|||
|
|
async submitForm() {
|
|||
|
|
if (this.submitting || !this.validateForm()) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
this.submitting = true
|
|||
|
|
try {
|
|||
|
|
await updateUserInfo({
|
|||
|
|
nickname: this.form.nickname.trim(),
|
|||
|
|
gender: this.form.gender,
|
|||
|
|
birthday: this.form.birthday,
|
|||
|
|
bio: this.form.bio
|
|||
|
|
})
|
|||
|
|
const profile = await getUserInfo()
|
|||
|
|
uni.setStorageSync('scanapp_user', profile)
|
|||
|
|
uni.showToast({ title: '资料已保存', icon: 'none' })
|
|||
|
|
setTimeout(() => {
|
|||
|
|
uni.navigateBack()
|
|||
|
|
}, 400)
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[scanapp] 保存资料失败', error)
|
|||
|
|
} finally {
|
|||
|
|
this.submitting = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.profile-form-page {
|
|||
|
|
min-height: 100vh;
|
|||
|
|
padding: 28rpx 24rpx 48rpx;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
background: #f4f6fb;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-card {
|
|||
|
|
padding: 34rpx 30rpx;
|
|||
|
|
border: 1rpx solid rgba(255, 255, 255, 0.75);
|
|||
|
|
border-radius: 28rpx;
|
|||
|
|
background: #ffffff;
|
|||
|
|
box-shadow: 0 16rpx 40rpx rgba(23, 35, 61, 0.06);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-header {
|
|||
|
|
margin-bottom: 24rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-title,
|
|||
|
|
.form-desc,
|
|||
|
|
.field-text {
|
|||
|
|
display: block;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-title {
|
|||
|
|
color: #17233d;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
font-weight: 700;
|
|||
|
|
line-height: 1.35;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-desc {
|
|||
|
|
margin-top: 10rpx;
|
|||
|
|
color: #8a94a6;
|
|||
|
|
font-size: 22rpx;
|
|||
|
|
line-height: 1.45;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.field-card {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 88rpx;
|
|||
|
|
padding: 0 24rpx;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
border: 1rpx solid #edf1f7;
|
|||
|
|
border-radius: 22rpx;
|
|||
|
|
background: #f8fbff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.field-card.locked {
|
|||
|
|
border-color: #edf0f5;
|
|||
|
|
background: #f4f6fb;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.field-card.picker-card {
|
|||
|
|
padding-right: 20rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.field-text {
|
|||
|
|
color: #17233d;
|
|||
|
|
font-size: 22rpx;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.field-text.locked-text,
|
|||
|
|
.field-text.placeholder {
|
|||
|
|
color: #a0a7b5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.gender-options {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(3, 1fr);
|
|||
|
|
column-gap: 16rpx;
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.gender-option {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
height: 76rpx;
|
|||
|
|
border: 1rpx solid #edf1f7;
|
|||
|
|
border-radius: 20rpx;
|
|||
|
|
background: #f8fbff;
|
|||
|
|
color: #6b778c;
|
|||
|
|
font-size: 22rpx;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.gender-dot {
|
|||
|
|
width: 14rpx;
|
|||
|
|
height: 14rpx;
|
|||
|
|
margin-right: 10rpx;
|
|||
|
|
border: 3rpx solid #c5ccda;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.gender-option.active {
|
|||
|
|
border-color: #1677ff;
|
|||
|
|
background: rgba(22, 119, 255, 0.1);
|
|||
|
|
color: #1677ff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.gender-option.active .gender-dot {
|
|||
|
|
border-color: #1677ff;
|
|||
|
|
background: #1677ff;
|
|||
|
|
box-shadow: 0 0 0 4rpx rgba(22, 119, 255, 0.16);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.textarea-card {
|
|||
|
|
width: 100%;
|
|||
|
|
min-height: 156rpx;
|
|||
|
|
padding: 20rpx 24rpx;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
border: 1rpx solid #edf1f7;
|
|||
|
|
border-radius: 22rpx;
|
|||
|
|
background: #f8fbff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.textarea-card textarea {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 116rpx;
|
|||
|
|
color: #17233d;
|
|||
|
|
font-size: 22rpx;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.bottom-actions {
|
|||
|
|
margin-top: 36rpx;
|
|||
|
|
}
|
|||
|
|
.theme-dark.profile-form-page {
|
|||
|
|
background: #151923;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.theme-dark .form-card,
|
|||
|
|
.theme-dark .field-card,
|
|||
|
|
.theme-dark .gender-option,
|
|||
|
|
.theme-dark .textarea-card {
|
|||
|
|
border-color: #364152;
|
|||
|
|
background: #222936;
|
|||
|
|
box-shadow: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.theme-dark .field-card.locked {
|
|||
|
|
background: #1c2431;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.theme-dark .form-title,
|
|||
|
|
.theme-dark .field-text,
|
|||
|
|
.theme-dark .textarea-card textarea {
|
|||
|
|
color: #f2f5fa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.theme-dark .form-desc,
|
|||
|
|
.theme-dark .field-text.locked-text,
|
|||
|
|
.theme-dark .field-text.placeholder,
|
|||
|
|
.theme-dark .gender-option {
|
|||
|
|
color: #aeb9ca;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.theme-dark :deep(textarea),
|
|||
|
|
.theme-dark :deep(.u-input__input) {
|
|||
|
|
color: #f2f5fa !important;
|
|||
|
|
}
|
|||
|
|
</style>
|