|
|
@@ -0,0 +1,405 @@
|
|
|
+<template>
|
|
|
+ <view class="user-edit-container">
|
|
|
+ <view class="header">
|
|
|
+ <text class="title">完善个人信息</text>
|
|
|
+ <text class="subtitle">以下信息可帮助您更好地使用服务</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-section">
|
|
|
+ <!-- 头像 -->
|
|
|
+ <view class="form-item avatar-item">
|
|
|
+ <text class="label">头像</text>
|
|
|
+ <view class="avatar-upload" @click="chooseAvatar">
|
|
|
+ <image v-if="form.avatar" :src="form.avatar" class="avatar-preview"></image>
|
|
|
+ <view v-else class="avatar-placeholder">
|
|
|
+ <text class="iconfont icon-plus">+</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 昵称 -->
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="label">昵称 <text class="required">*</text></text>
|
|
|
+ <input type="text" v-model="form.nickname" placeholder="请输入昵称" class="input" maxlength="20" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 真实姓名 -->
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="label">真实姓名</text>
|
|
|
+ <input type="text" v-model="form.realName" placeholder="选填,用于家庭管理" class="input" maxlength="20" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 角色选择 -->
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="label">我是</text>
|
|
|
+ <radio-group class="role-select" @change="onRoleChange">
|
|
|
+ <label class="role-option">
|
|
|
+ <radio value="parent" :checked="form.role === 'parent'" />
|
|
|
+ <text>家长</text>
|
|
|
+ </label>
|
|
|
+ <label class="role-option">
|
|
|
+ <radio value="child" :checked="form.role === 'child'" />
|
|
|
+ <text>孩子</text>
|
|
|
+ </label>
|
|
|
+ </radio-group>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 性别选择 -->
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="label">性别</text>
|
|
|
+ <radio-group class="role-select" @change="onGenderChange">
|
|
|
+ <label class="role-option">
|
|
|
+ <radio value="male" :checked="form.gender === 'male'" />
|
|
|
+ <text>男</text>
|
|
|
+ </label>
|
|
|
+ <label class="role-option">
|
|
|
+ <radio value="female" :checked="form.gender === 'female'" />
|
|
|
+ <text>女</text>
|
|
|
+ </label>
|
|
|
+ </radio-group>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 身份证号 -->
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="label">身份证号</text>
|
|
|
+ <input type="idcard" v-model="form.idCard" placeholder="选填,填写后自动生成生日" class="input" maxlength="18" @input="onIdCardInput" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 生日(可选,如果填了身份证则自动填充) -->
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="label">生日</text>
|
|
|
+ <picker mode="date" :value="form.birthday" @change="onBirthdayChange">
|
|
|
+ <view class="picker-input">{{ form.birthday || '请选择生日' }}</view>
|
|
|
+ </picker>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="family-section">
|
|
|
+ <view class="section-title">家庭绑定</view>
|
|
|
+
|
|
|
+ <!-- 邀请码输入 -->
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="label">家庭邀请码</text>
|
|
|
+ <input type="text" v-model="form.inviteCode" placeholder="选填,如有邀请码请输入" class="input" maxlength="8" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="family-actions">
|
|
|
+ <button v-if="form.inviteCode" class="btn btn-primary" @click="joinFamilyAction" :loading="loading">
|
|
|
+ 加入家庭
|
|
|
+ </button>
|
|
|
+ <button v-else class="btn btn-primary" @click="createFamilyAction" :loading="loading">
|
|
|
+ 创建新家庭
|
|
|
+ </button>
|
|
|
+ <button class="btn btn-skip" @click="skip">
|
|
|
+ 跳过
|
|
|
+ </button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { updateUserInfo, joinFamily, createFamily } from '../../utils/api.js'
|
|
|
+
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ form: {
|
|
|
+ avatar: '',
|
|
|
+ nickname: '',
|
|
|
+ realName: '',
|
|
|
+ role: 'parent',
|
|
|
+ gender: '',
|
|
|
+ idCard: '',
|
|
|
+ birthday: '',
|
|
|
+ inviteCode: ''
|
|
|
+ },
|
|
|
+ loading: false,
|
|
|
+ token: '',
|
|
|
+ userId: null
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onLoad(options) {
|
|
|
+ // 从登录页面传递过来的参数
|
|
|
+ this.token = options.token || ''
|
|
|
+ this.userId = options.userId || null
|
|
|
+ // 从本地存储获取用户信息
|
|
|
+ const userInfo = uni.getStorageSync('userInfo')
|
|
|
+ if (userInfo) {
|
|
|
+ this.form.nickname = userInfo.nickname || ''
|
|
|
+ this.form.avatar = userInfo.avatar || ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 选择头像
|
|
|
+ chooseAvatar() {
|
|
|
+ uni.chooseImage({
|
|
|
+ count: 1,
|
|
|
+ sizeType: ['compressed'],
|
|
|
+ sourceType: ['album', 'camera'],
|
|
|
+ success: (res) => {
|
|
|
+ this.form.avatar = res.tempFilePaths[0]
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 角色选择
|
|
|
+ onRoleChange(e) {
|
|
|
+ this.form.role = e.detail.value
|
|
|
+ },
|
|
|
+
|
|
|
+ // 性别选择
|
|
|
+ onGenderChange(e) {
|
|
|
+ this.form.gender = e.detail.value
|
|
|
+ },
|
|
|
+
|
|
|
+ // 生日选择
|
|
|
+ onBirthdayChange(e) {
|
|
|
+ this.form.birthday = e.detail.value
|
|
|
+ },
|
|
|
+
|
|
|
+ // 身份证输入 - 自动提取生日
|
|
|
+ onIdCardInput(e) {
|
|
|
+ const idCard = e.detail.value
|
|
|
+ if (idCard.length === 15) {
|
|
|
+ // 15位身份证:1985年07月08日 -> 19850708
|
|
|
+ const birthYear = '19' + idCard.substring(6, 8)
|
|
|
+ const birthMonth = idCard.substring(8, 10)
|
|
|
+ const birthDay = idCard.substring(10, 12)
|
|
|
+ this.form.birthday = `${birthYear}-${birthMonth}-${birthDay}`
|
|
|
+ } else if (idCard.length === 18) {
|
|
|
+ // 18位身份证:1985年07月08日 -> 19850708
|
|
|
+ const birthYear = idCard.substring(6, 10)
|
|
|
+ const birthMonth = idCard.substring(10, 12)
|
|
|
+ const birthDay = idCard.substring(12, 14)
|
|
|
+ this.form.birthday = `${birthYear}-${birthMonth}-${birthDay}`
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加入现有家庭
|
|
|
+ async joinFamilyAction() {
|
|
|
+ if (!this.form.inviteCode) {
|
|
|
+ uni.showToast({ title: '请输入邀请码', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ await this.saveUserInfo('join')
|
|
|
+ },
|
|
|
+
|
|
|
+ // 创建新家庭
|
|
|
+ async createFamilyAction() {
|
|
|
+ await this.saveUserInfo('create')
|
|
|
+ },
|
|
|
+
|
|
|
+ // 跳过
|
|
|
+ async skip() {
|
|
|
+ await this.saveUserInfo('skip')
|
|
|
+ },
|
|
|
+
|
|
|
+ // 保存用户信息
|
|
|
+ async saveUserInfo(action) {
|
|
|
+ if (!this.form.nickname) {
|
|
|
+ uni.showToast({ title: '请输入昵称', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.loading = true
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 1. 更新用户信息(包括新字段:role, gender, birthday, idCard)
|
|
|
+ await updateUserInfo({
|
|
|
+ nickname: this.form.nickname,
|
|
|
+ avatar: this.form.avatar,
|
|
|
+ realName: this.form.realName,
|
|
|
+ role: this.form.role,
|
|
|
+ gender: this.form.gender,
|
|
|
+ birthday: this.form.birthday,
|
|
|
+ idCard: this.form.idCard
|
|
|
+ })
|
|
|
+
|
|
|
+ // 2. 根据 action 处理家庭绑定
|
|
|
+ if (action === 'join' && this.form.inviteCode) {
|
|
|
+ await joinFamily(this.form.inviteCode)
|
|
|
+ } else if (action === 'create') {
|
|
|
+ await createFamily('我的家庭')
|
|
|
+ }
|
|
|
+ // action === 'skip' 时不做处理,使用自动创建的默认家庭
|
|
|
+
|
|
|
+ uni.showToast({ title: '保存成功', icon: 'success' })
|
|
|
+
|
|
|
+ // 更新本地存储的用户信息
|
|
|
+ uni.setStorageSync('userInfo', {
|
|
|
+ nickname: this.form.nickname,
|
|
|
+ avatar: this.form.avatar
|
|
|
+ })
|
|
|
+ uni.setStorageSync('role', this.form.role)
|
|
|
+
|
|
|
+ // 跳转到首页
|
|
|
+ setTimeout(() => {
|
|
|
+ // 根据角色跳转到对应的首页
|
|
|
+ const role = uni.getStorageSync('role') || 'parent'
|
|
|
+ if (role === 'parent') {
|
|
|
+ uni.reLaunch({
|
|
|
+ url: '/pages/index/parent-index'
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ uni.reLaunch({
|
|
|
+ url: '/pages/index/child-index'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }, 1000)
|
|
|
+ } catch (error) {
|
|
|
+ uni.showToast({ title: error.message || '保存失败', icon: 'none' })
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.user-edit-container {
|
|
|
+ min-height: 100vh;
|
|
|
+ background: #f8f8f8;
|
|
|
+ padding: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.header {
|
|
|
+ text-align: center;
|
|
|
+ margin-bottom: 60rpx;
|
|
|
+
|
|
|
+ .title {
|
|
|
+ display: block;
|
|
|
+ font-size: 48rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .subtitle {
|
|
|
+ display: block;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.form-section {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ padding: 40rpx;
|
|
|
+ margin-bottom: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.form-item {
|
|
|
+ margin-bottom: 40rpx;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .label {
|
|
|
+ display: block;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+
|
|
|
+ .required {
|
|
|
+ color: #ff6b6b;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .input {
|
|
|
+ width: 100%;
|
|
|
+ height: 80rpx;
|
|
|
+ background: #f8f8f8;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ padding: 0 30rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .picker-input {
|
|
|
+ width: 100%;
|
|
|
+ height: 80rpx;
|
|
|
+ background: #f8f8f8;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ padding: 0 30rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ line-height: 80rpx;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.role-select {
|
|
|
+ display: flex;
|
|
|
+ gap: 40rpx;
|
|
|
+ padding: 10rpx 0;
|
|
|
+}
|
|
|
+
|
|
|
+.role-option {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+
|
|
|
+ .avatar-upload {
|
|
|
+ width: 120rpx;
|
|
|
+ height: 120rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ overflow: hidden;
|
|
|
+ background: #f8f8f8;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ .avatar-preview {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .avatar-placeholder {
|
|
|
+ font-size: 48rpx;
|
|
|
+ color: #ccc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.family-section {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ padding: 40rpx;
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.family-actions {
|
|
|
+ margin-top: 40rpx;
|
|
|
+
|
|
|
+ .btn {
|
|
|
+ width: 100%;
|
|
|
+ height: 88rpx;
|
|
|
+ border-radius: 44rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn-primary {
|
|
|
+ background: #ff6b6b;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn-skip {
|
|
|
+ background: #f8f8f8;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|