Browse Source

feat: 注册零填写-新增接受家庭邀请页join-form(真实姓名+家庭身份必填)

E2E Test Bot 1 day ago
parent
commit
95efc0f4e2
2 changed files with 244 additions and 1 deletions
  1. 7 1
      cfc-frontend/pages.json
  2. 237 0
      cfc-frontend/pages/invite/join-form.vue

+ 7 - 1
cfc-frontend/pages.json

@@ -1444,7 +1444,7 @@
         }
         }
       ]
       ]
     },
     },
-    {
+     {
       "root": "pages/invite",
       "root": "pages/invite",
       "pages": [
       "pages": [
         {
         {
@@ -1453,6 +1453,12 @@
             "navigationBarTitleText": "加入家庭",
             "navigationBarTitleText": "加入家庭",
             "navigationStyle": "custom"
             "navigationStyle": "custom"
           }
           }
+        },
+        {
+          "path": "join-form",
+          "style": {
+            "navigationBarTitleText": "加入家庭"
+          }
         }
         }
       ]
       ]
     },
     },

+ 237 - 0
cfc-frontend/pages/invite/join-form.vue

@@ -0,0 +1,237 @@
+<template>
+	<view class="join-form-container">
+		<view class="header">
+			<text class="title">加入家庭</text>
+			<text class="subtitle">请填写您的真实姓名和家庭身份,提交后等待管理员审批</text>
+		</view>
+
+		<view class="form-section">
+			<!-- 真实姓名(必填) -->
+			<view class="form-item">
+				<text class="label">真实姓名 <text class="required">*</text></text>
+				<input type="text" v-model="form.realName" placeholder="请输入真实姓名" class="input" maxlength="20" />
+			</view>
+
+			<!-- 家庭身份(必填) -->
+			<view class="form-item">
+				<text class="label">家庭身份 <text class="required">*</text></text>
+				<picker mode="selector" :range="familyRoleOptions" :value="familyRoleIndex" @change="onFamilyRoleChange">
+					<view class="picker-input">{{ form.familyRole || '请选择(如:爸爸、妈妈)' }}</view>
+				</picker>
+			</view>
+
+			<!-- 昵称(选填,默认用户+手机尾号) -->
+			<view class="form-item">
+				<text class="label">昵称</text>
+				<input type="text" v-model="form.nickname" placeholder="选填,默认:用户+手机尾号" class="input" maxlength="20" />
+			</view>
+
+			<!-- 头像(选填) -->
+			<view class="form-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="plus-icon">+</text>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<view class="actions">
+			<button class="btn-primary" @click="submit" :loading="submitting" :disabled="submitting">
+				<text v-if="submitting">提交中...</text>
+				<text v-else>提交加入申请</text>
+			</button>
+		</view>
+	</view>
+</template>
+
+<script>
+import { updateUserInfo, requestJoinByCode } from '@/utils/api.js'
+
+export default {
+	data() {
+		return {
+			familyInviteCode: '',
+			familyRoleOptions: ['爸爸', '妈妈', '爷爷', '奶奶', '姥爷', '姥姥', '其他'],
+			form: {
+				realName: '',
+				familyRole: '',
+				nickname: '',
+				avatar: ''
+			},
+			submitting: false
+		}
+	},
+	computed: {
+		familyRoleIndex: {
+			get() {
+				return this.familyRoleOptions.indexOf(this.form.familyRole)
+			},
+			set(val) {
+				this.form.familyRole = this.familyRoleOptions[val] || ''
+			}
+		}
+	},
+	onLoad(options) {
+		// 接收家庭邀请码:join.vue 传 familyInviteCode;登录页衔接传 pendingFamilyInviteCode 对应值
+		var code = (options && options.familyInviteCode) || uni.getStorageSync('pendingFamilyInviteCode') || ''
+		this.familyInviteCode = code
+		if (code) {
+			uni.setStorageSync('pendingFamilyInviteCode', code)
+		}
+		// 已有资料预填(如有昵称/头像/真实姓名则带出)
+		this.prefill()
+	},
+	methods: {
+		prefill() {
+			var userInfo = uni.getStorageSync('userInfo') || {}
+			if (userInfo.realName) this.form.realName = userInfo.realName
+			if (userInfo.nickname) this.form.nickname = userInfo.nickname
+			if (userInfo.avatar) this.form.avatar = userInfo.avatar
+		},
+		chooseAvatar() {
+			var self = this
+			uni.chooseImage({
+				count: 1,
+				success: function(res) {
+					self.form.avatar = res.tempFilePaths[0]
+				}
+			})
+		},
+		onFamilyRoleChange(e) {
+			this.form.familyRole = this.familyRoleOptions[e.detail.value] || ''
+		},
+		async submit() {
+			if (this.submitting) return
+			if (!this.form.realName || !this.form.realName.trim()) {
+				uni.showToast({ title: '请输入真实姓名', icon: 'none' })
+				return
+			}
+			if (!this.form.familyRole) {
+				uni.showToast({ title: '请选择家庭身份', icon: 'none' })
+				return
+			}
+			if (!this.familyInviteCode) {
+				uni.showToast({ title: '缺少家庭邀请码', icon: 'none' })
+				return
+			}
+			this.submitting = true
+			try {
+				// 先保存基本信息(真实姓名/昵称/头像),失败不阻塞加入申请
+				try {
+					await updateUserInfo({
+						realName: this.form.realName.trim(),
+						nickname: this.form.nickname ? this.form.nickname.trim() : '',
+						avatar: this.form.avatar
+					})
+				} catch (e) {
+					console.log('[join-form] 保存基本信息失败,继续提交加入申请', e)
+				}
+				// 提交加入家庭申请
+				await requestJoinByCode(this.familyInviteCode, this.form.familyRole)
+				uni.removeStorageSync('pendingFamilyInviteCode')
+				uni.showToast({ title: '加入家庭申请已提交,等待管理员审批', icon: 'success' })
+				setTimeout(function() {
+					uni.reLaunch({ url: '/pages/index-home/index' })
+				}, 1500)
+			} catch (e) {
+				uni.showToast({ title: (e && e.message) || '提交失败', icon: 'none' })
+			} finally {
+				this.submitting = false
+			}
+		}
+	}
+}
+</script>
+
+<style scoped>
+.join-form-container {
+	min-height: 100vh;
+	background: #f5f5f5;
+	padding: 30rpx;
+	box-sizing: border-box;
+}
+.header {
+	margin-bottom: 40rpx;
+	text-align: center;
+}
+.title {
+	font-size: 40rpx;
+	font-weight: bold;
+	color: #333;
+	display: block;
+	margin-bottom: 10rpx;
+}
+.subtitle {
+	font-size: 26rpx;
+	color: #999;
+	line-height: 1.6;
+	display: block;
+}
+.form-section {
+	background: #fff;
+	border-radius: 16rpx;
+	padding: 30rpx;
+	margin-bottom: 30rpx;
+}
+.form-item {
+	margin-bottom: 30rpx;
+}
+.form-item:last-child {
+	margin-bottom: 0;
+}
+.label {
+	display: block;
+	font-size: 28rpx;
+	color: #333;
+	margin-bottom: 16rpx;
+}
+.required {
+	color: #f44336;
+}
+.input, .picker-input {
+	padding: 20rpx;
+	background: #f5f5f5;
+	border-radius: 12rpx;
+	font-size: 28rpx;
+	box-sizing: border-box;
+	width: 100%;
+}
+.avatar-upload {
+	width: 150rpx;
+	height: 150rpx;
+}
+.avatar-preview {
+	width: 150rpx;
+	height: 150rpx;
+	border-radius: 50%;
+}
+.avatar-placeholder {
+	width: 150rpx;
+	height: 150rpx;
+	background: #f5f5f5;
+	border-radius: 50%;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+.plus-icon {
+	font-size: 60rpx;
+	color: #ccc;
+}
+.actions {
+	padding-bottom: 40rpx;
+}
+.btn-primary {
+	background: #4CAF50;
+	color: #fff;
+	border-radius: 40rpx;
+	font-size: 30rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	padding: 24rpx 0;
+}
+</style>