feat(register): 添加注册页面及表单功能
实现注册页面UI及表单验证功能,包括头像上传、手机验证码发送、生日和学校选择等
Showing
4 changed files
with
686 additions
and
1 deletions
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-06-28 10:33:00 | 2 | * @Date: 2025-06-28 10:33:00 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-02 15:05:28 | 4 | + * @LastEditTime: 2025-07-02 16:01:00 |
| 5 | * @FilePath: /jgdl/src/app.config.js | 5 | * @FilePath: /jgdl/src/app.config.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -13,6 +13,7 @@ export default { | ... | @@ -13,6 +13,7 @@ export default { |
| 13 | 'pages/messages/index', | 13 | 'pages/messages/index', |
| 14 | 'pages/profile/index', | 14 | 'pages/profile/index', |
| 15 | 'pages/editProfile/index', | 15 | 'pages/editProfile/index', |
| 16 | + 'pages/register/index', | ||
| 16 | 'pages/auth/index', | 17 | 'pages/auth/index', |
| 17 | ], | 18 | ], |
| 18 | subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 | 19 | subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 | ... | ... |
src/pages/register/index.config.js
0 → 100755
| 1 | +/* | ||
| 2 | + * @Date: 2025-07-02 16:00:16 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-07-02 16:00:37 | ||
| 5 | + * @FilePath: /jgdl/src/pages/register/index.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +export default { | ||
| 9 | + navigationBarTitleText: '完善信息', | ||
| 10 | + usingComponents: { | ||
| 11 | + }, | ||
| 12 | +} |
src/pages/register/index.less
0 → 100644
| 1 | +/* 注册页面样式 */ | ||
| 2 | +.register-page { | ||
| 3 | + min-height: 100vh; | ||
| 4 | + background-color: #f5f5f5; | ||
| 5 | + padding-bottom: 200rpx; | ||
| 6 | +} | ||
| 7 | + | ||
| 8 | +/* 头像区域 */ | ||
| 9 | +.avatar-section { | ||
| 10 | + display: flex; | ||
| 11 | + flex-direction: column; | ||
| 12 | + align-items: center; | ||
| 13 | + padding: 60rpx 0; | ||
| 14 | + background-color: #fff; | ||
| 15 | + margin-bottom: 20rpx; | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +.avatar-container { | ||
| 19 | + position: relative; | ||
| 20 | + margin-bottom: 20rpx; | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +.avatar-image { | ||
| 24 | + width: 160rpx; | ||
| 25 | + height: 160rpx; | ||
| 26 | + border-radius: 80rpx; | ||
| 27 | + border: 4rpx solid #f0f0f0; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +.camera-btn { | ||
| 31 | + position: absolute; | ||
| 32 | + bottom: 0; | ||
| 33 | + right: 0; | ||
| 34 | + width: 48rpx; | ||
| 35 | + height: 48rpx; | ||
| 36 | + background-color: #f97316; | ||
| 37 | + border-radius: 24rpx; | ||
| 38 | + display: flex; | ||
| 39 | + align-items: center; | ||
| 40 | + justify-content: center; | ||
| 41 | + border: 4rpx solid #fff; | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +.camera-icon { | ||
| 45 | + width: 24rpx; | ||
| 46 | + height: 24rpx; | ||
| 47 | + color: #fff; | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +.change-avatar-btn { | ||
| 51 | + padding: 16rpx 32rpx; | ||
| 52 | + background-color: #f97316; | ||
| 53 | + border-radius: 40rpx; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +.change-avatar-text { | ||
| 57 | + color: #fff; | ||
| 58 | + font-size: 28rpx; | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +/* 表单容器 */ | ||
| 62 | +.form-container { | ||
| 63 | + background-color: #fff; | ||
| 64 | + margin: 0 24rpx 40rpx; | ||
| 65 | + border-radius: 16rpx; | ||
| 66 | + overflow: hidden; | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +/* 手机号输入容器 */ | ||
| 70 | +.phone-input-container { | ||
| 71 | + display: flex; | ||
| 72 | + align-items: center; | ||
| 73 | + width: 100%; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +.phone-input { | ||
| 77 | + flex: 1; | ||
| 78 | + margin-right: 20rpx; | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +.code-btn { | ||
| 82 | + flex-shrink: 0; | ||
| 83 | + min-width: 160rpx; | ||
| 84 | + height: 64rpx; | ||
| 85 | + background-color: #f97316; | ||
| 86 | + color: #fff; | ||
| 87 | + border-radius: 8rpx; | ||
| 88 | + font-size: 24rpx; | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +.code-btn:disabled { | ||
| 92 | + background-color: #ccc; | ||
| 93 | + color: #999; | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +/* 生日选择项 */ | ||
| 97 | +.birthday-item { | ||
| 98 | + display: flex; | ||
| 99 | + align-items: center; | ||
| 100 | + justify-content: space-between; | ||
| 101 | + width: 100%; | ||
| 102 | + padding: 20rpx 0; | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +.birthday-value { | ||
| 106 | + color: #333; | ||
| 107 | + font-size: 28rpx; | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +.arrow-icon { | ||
| 111 | + width: 24rpx; | ||
| 112 | + height: 24rpx; | ||
| 113 | + color: #999; | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +/* 学校选择项 */ | ||
| 117 | +.school-item { | ||
| 118 | + display: flex; | ||
| 119 | + align-items: center; | ||
| 120 | + justify-content: space-between; | ||
| 121 | + width: 100%; | ||
| 122 | + padding: 20rpx 0; | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +.school-value { | ||
| 126 | + color: #333; | ||
| 127 | + font-size: 28rpx; | ||
| 128 | +} | ||
| 129 | + | ||
| 130 | +/* 性别单选组件右对齐 */ | ||
| 131 | +// .form-container :deep(.nut-form-item:nth-child(4) .nut-form-item__body) { | ||
| 132 | +// justify-content: flex-end; | ||
| 133 | +// } | ||
| 134 | + | ||
| 135 | +/* 注册按钮区域 */ | ||
| 136 | +.register-section { | ||
| 137 | + position: fixed; | ||
| 138 | + bottom: 0; | ||
| 139 | + left: 0; | ||
| 140 | + right: 0; | ||
| 141 | + padding: 40rpx 24rpx; | ||
| 142 | + background-color: #fff; | ||
| 143 | + border-top: 1rpx solid #eee; | ||
| 144 | + z-index: 100; | ||
| 145 | +} | ||
| 146 | + | ||
| 147 | +/* 表单项样式调整 */ | ||
| 148 | +.form-container :deep(.nut-form-item) { | ||
| 149 | + padding: 24rpx 32rpx; | ||
| 150 | + border-bottom: 1rpx solid #f0f0f0; | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +// .form-container :deep(.nut-form-item:last-child) { | ||
| 154 | +// border-bottom: none; | ||
| 155 | +// } | ||
| 156 | + | ||
| 157 | +.form-container .nut-form-item__label { | ||
| 158 | + font-size: 28rpx; | ||
| 159 | + color: #333; | ||
| 160 | + font-weight: 500; | ||
| 161 | + min-width: 140rpx; | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +.form-container :deep(.nut-input__inner) { | ||
| 165 | + font-size: 28rpx; | ||
| 166 | + color: #333; | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +// .form-container :deep(.nut-input__inner::placeholder) { | ||
| 170 | +// color: #999; | ||
| 171 | +// font-size: 28rpx; | ||
| 172 | +// } | ||
| 173 | + | ||
| 174 | +/* 单选按钮样式 */ | ||
| 175 | +.form-container :deep(.nut-radio-group) { | ||
| 176 | + gap: 40rpx; | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +.form-container :deep(.nut-radio__label) { | ||
| 180 | + font-size: 28rpx; | ||
| 181 | + color: #333; | ||
| 182 | +} | ||
| 183 | + | ||
| 184 | +.form-container :deep(.nut-radio__icon) { | ||
| 185 | + width: 32rpx; | ||
| 186 | + height: 32rpx; | ||
| 187 | +} | ||
| 188 | + | ||
| 189 | +/* 弹窗样式调整 */ | ||
| 190 | +:deep(.nut-popup) { | ||
| 191 | + border-radius: 24rpx 24rpx 0 0; | ||
| 192 | +} | ||
| 193 | + | ||
| 194 | +:deep(.nut-picker__title) { | ||
| 195 | + font-size: 32rpx; | ||
| 196 | + font-weight: 600; | ||
| 197 | + color: #333; | ||
| 198 | +} | ||
| 199 | + | ||
| 200 | +:deep(.nut-date-picker__title) { | ||
| 201 | + font-size: 32rpx; | ||
| 202 | + font-weight: 600; | ||
| 203 | + color: #333; | ||
| 204 | +} | ||
| 205 | + | ||
| 206 | +/* 按钮样式 */ | ||
| 207 | +:deep(.nut-button--large) { | ||
| 208 | + height: 88rpx; | ||
| 209 | + border-radius: 44rpx; | ||
| 210 | + font-size: 32rpx; | ||
| 211 | + font-weight: 600; | ||
| 212 | +} | ||
| 213 | + | ||
| 214 | +/* Toast 样式 */ | ||
| 215 | +:deep(.nut-toast) { | ||
| 216 | + z-index: 9999; | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +/* 导航栏样式 */ | ||
| 220 | +:deep(.nut-navbar) { | ||
| 221 | + background-color: #f97316; | ||
| 222 | + color: #fff; | ||
| 223 | +} | ||
| 224 | + | ||
| 225 | +:deep(.nut-navbar__title) { | ||
| 226 | + color: #fff; | ||
| 227 | + font-size: 36rpx; | ||
| 228 | + font-weight: 600; | ||
| 229 | +} | ||
| 230 | + | ||
| 231 | +:deep(.nut-navbar__left) { | ||
| 232 | + color: #fff; | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +/* 响应式适配 */ | ||
| 236 | +@media screen and (max-width: 375px) { | ||
| 237 | + .avatar-image { | ||
| 238 | + width: 140rpx; | ||
| 239 | + height: 140rpx; | ||
| 240 | + border-radius: 70rpx; | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + .camera-btn { | ||
| 244 | + width: 40rpx; | ||
| 245 | + height: 40rpx; | ||
| 246 | + border-radius: 20rpx; | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + .camera-icon { | ||
| 250 | + width: 20rpx; | ||
| 251 | + height: 20rpx; | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | + .form-container :deep(.nut-form-item) { | ||
| 255 | + padding: 20rpx 24rpx; | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + .form-container :deep.nut-form-item__label { | ||
| 259 | + font-size: 26rpx; | ||
| 260 | + min-width: 120rpx; | ||
| 261 | + } | ||
| 262 | + | ||
| 263 | + .form-container :deep(.nut-input__inner) { | ||
| 264 | + font-size: 26rpx; | ||
| 265 | + } | ||
| 266 | +} | ||
| 267 | + | ||
| 268 | +/* 加载状态 */ | ||
| 269 | +.register-section :deep(.nut-button--loading) { | ||
| 270 | + opacity: 0.7; | ||
| 271 | +} | ||
| 272 | + | ||
| 273 | +/* 禁用状态 */ | ||
| 274 | +.register-section :deep(.nut-button--disabled) { | ||
| 275 | + background-color: #ccc !important; | ||
| 276 | + color: #999 !important; | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | +/* 表单验证错误样式 */ | ||
| 280 | +// .form-container :deep(.nut-form-item--error .nut-form-item__label) { | ||
| 281 | +// color: #ff4757; | ||
| 282 | +// } | ||
| 283 | + | ||
| 284 | +// .form-container :deep(.nut-form-item--error .nut-input__inner) { | ||
| 285 | +// border-color: #ff4757; | ||
| 286 | +// } | ||
| 287 | + | ||
| 288 | +/* 占位符颜色调整 */ | ||
| 289 | +.birthday-value, | ||
| 290 | +.school-value { | ||
| 291 | + color: #999; | ||
| 292 | +} | ||
| 293 | + | ||
| 294 | +// .birthday-item:has(.birthday-value:not(:empty)), | ||
| 295 | +// .school-item:has(.school-value:not(:empty)) { | ||
| 296 | +// .birthday-value, | ||
| 297 | +// .school-value { | ||
| 298 | +// color: #333; | ||
| 299 | +// } | ||
| 300 | +// } |
src/pages/register/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <view class="register-page"> | ||
| 3 | + <!-- 头像区域 --> | ||
| 4 | + <view class="avatar-section"> | ||
| 5 | + <view class="avatar-container"> | ||
| 6 | + <image | ||
| 7 | + :src="formData.avatar || defaultAvatar" | ||
| 8 | + class="avatar-image" | ||
| 9 | + mode="aspectFill" | ||
| 10 | + @click="previewAvatar" | ||
| 11 | + /> | ||
| 12 | + </view> | ||
| 13 | + <view class="change-avatar-btn" @click="changeAvatar"> | ||
| 14 | + <text class="change-avatar-text">上传头像</text> | ||
| 15 | + </view> | ||
| 16 | + </view> | ||
| 17 | + | ||
| 18 | + <!-- 表单内容 --> | ||
| 19 | + <nut-form ref="formRef" :model-value="formData"> | ||
| 20 | + <view class="form-container"> | ||
| 21 | + <!-- 昵称 --> | ||
| 22 | + <nut-form-item label="昵称" prop="nickname" required :rules="[{ required: true, message: '请输入昵称' }]"> | ||
| 23 | + <nut-input | ||
| 24 | + v-model="formData.nickname" | ||
| 25 | + placeholder="请输入昵称" | ||
| 26 | + input-align="right" | ||
| 27 | + clearable | ||
| 28 | + /> | ||
| 29 | + </nut-form-item> | ||
| 30 | + | ||
| 31 | + <!-- 手机号 --> | ||
| 32 | + <nut-form-item label="手机号" prop="phone" required :rules="phoneRules"> | ||
| 33 | + <view class="phone-input-container"> | ||
| 34 | + <nut-input | ||
| 35 | + v-model="formData.phone" | ||
| 36 | + placeholder="请输入手机号" | ||
| 37 | + type="tel" | ||
| 38 | + maxlength="11" | ||
| 39 | + input-align="right" | ||
| 40 | + clearable | ||
| 41 | + class="phone-input" | ||
| 42 | + /> | ||
| 43 | + <nut-button | ||
| 44 | + size="small" | ||
| 45 | + :disabled="codeCountdown > 0 || !isPhoneValid" | ||
| 46 | + @click="sendCode" | ||
| 47 | + class="code-btn" | ||
| 48 | + > | ||
| 49 | + {{ codeCountdown > 0 ? `${codeCountdown}s` : '获取验证码' }} | ||
| 50 | + </nut-button> | ||
| 51 | + </view> | ||
| 52 | + </nut-form-item> | ||
| 53 | + | ||
| 54 | + <!-- 验证码 --> | ||
| 55 | + <nut-form-item label="验证码" prop="verifyCode" required :rules="[{ required: true, message: '请输入验证码' }]"> | ||
| 56 | + <nut-input | ||
| 57 | + v-model="formData.verifyCode" | ||
| 58 | + placeholder="请输入验证码" | ||
| 59 | + type="number" | ||
| 60 | + maxlength="6" | ||
| 61 | + input-align="right" | ||
| 62 | + clearable | ||
| 63 | + /> | ||
| 64 | + </nut-form-item> | ||
| 65 | + | ||
| 66 | + <!-- 性别 --> | ||
| 67 | + <nut-form-item label="性别" prop="gender" body-align="right" required :rules="[{ required: true, message: '请选择性别' }]"> | ||
| 68 | + <nut-radio-group v-model="formData.gender" direction="horizontal"> | ||
| 69 | + <nut-radio label="男">男</nut-radio> | ||
| 70 | + <nut-radio label="女">女</nut-radio> | ||
| 71 | + </nut-radio-group> | ||
| 72 | + </nut-form-item> | ||
| 73 | + | ||
| 74 | + <!-- 生日 --> | ||
| 75 | + <nut-form-item label="生日" prop="birthday" label-position="top"> | ||
| 76 | + <view class="birthday-item" @click="showDatePicker"> | ||
| 77 | + <text class="birthday-value">{{ formData.birthday || '请选择生日' }}</text> | ||
| 78 | + <Right class="arrow-icon" /> | ||
| 79 | + </view> | ||
| 80 | + </nut-form-item> | ||
| 81 | + | ||
| 82 | + <!-- 所在学校 --> | ||
| 83 | + <nut-form-item label="所在学校" prop="school" required :rules="[{ required: true, message: '请选择学校' }]" label-position="top"> | ||
| 84 | + <view class="school-item" @click="showSchoolPicker"> | ||
| 85 | + <text class="school-value">{{ formData.school || '请选择学校' }}</text> | ||
| 86 | + <Right class="arrow-icon" /> | ||
| 87 | + </view> | ||
| 88 | + </nut-form-item> | ||
| 89 | + </view> | ||
| 90 | + </nut-form> | ||
| 91 | + | ||
| 92 | + <!-- 注册按钮 --> | ||
| 93 | + <view class="register-section"> | ||
| 94 | + <nut-button | ||
| 95 | + color="#f97316" | ||
| 96 | + size="large" | ||
| 97 | + block | ||
| 98 | + @click="handleRegister" | ||
| 99 | + :loading="isRegistering" | ||
| 100 | + > | ||
| 101 | + {{ isRegistering ? '保存中...' : '保存' }} | ||
| 102 | + </nut-button> | ||
| 103 | + </view> | ||
| 104 | + | ||
| 105 | + <!-- 日期选择器 --> | ||
| 106 | + <nut-popup v-model:visible="datePickerVisible" position="bottom"> | ||
| 107 | + <nut-date-picker | ||
| 108 | + v-model="dateValue" | ||
| 109 | + title="选择生日" | ||
| 110 | + @confirm="onDateConfirm" | ||
| 111 | + @cancel="datePickerVisible = false" | ||
| 112 | + /> | ||
| 113 | + </nut-popup> | ||
| 114 | + | ||
| 115 | + <!-- 学校选择器 --> | ||
| 116 | + <nut-popup v-model:visible="schoolPickerVisible" position="bottom"> | ||
| 117 | + <nut-picker | ||
| 118 | + v-model="schoolValue" | ||
| 119 | + :columns="schoolOptions" | ||
| 120 | + title="选择学校" | ||
| 121 | + @confirm="onSchoolConfirm" | ||
| 122 | + @cancel="schoolPickerVisible = false" | ||
| 123 | + /> | ||
| 124 | + </nut-popup> | ||
| 125 | + | ||
| 126 | + <!-- 头像预览 --> | ||
| 127 | + <nut-image-preview | ||
| 128 | + v-model:show="avatarPreviewVisible" | ||
| 129 | + :images="[formData.avatar || defaultAvatar]" | ||
| 130 | + /> | ||
| 131 | + | ||
| 132 | + <!-- 成功提示 --> | ||
| 133 | + <nut-toast | ||
| 134 | + v-model:visible="toastVisible" | ||
| 135 | + :msg="toastMessage" | ||
| 136 | + :type="toastType" | ||
| 137 | + /> | ||
| 138 | + </view> | ||
| 139 | +</template> | ||
| 140 | + | ||
| 141 | +<script setup> | ||
| 142 | +import { ref, reactive, computed, onMounted } from 'vue' | ||
| 143 | +import Taro from '@tarojs/taro' | ||
| 144 | +import { RectLeft, Camera, Right } from '@nutui/icons-vue-taro' | ||
| 145 | +import './index.less' | ||
| 146 | + | ||
| 147 | +// 主题配置 | ||
| 148 | +const themeVars = { | ||
| 149 | + navbarBackground: '#f97316', | ||
| 150 | + navbarColor: '#fff' | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +// 默认头像 | ||
| 154 | +const defaultAvatar = 'https://randomuser.me/api/portraits/men/32.jpg' | ||
| 155 | + | ||
| 156 | +// 表单数据 | ||
| 157 | +const formData = reactive({ | ||
| 158 | + avatar: '', | ||
| 159 | + nickname: '', | ||
| 160 | + phone: '', | ||
| 161 | + verifyCode: '', | ||
| 162 | + gender: '', | ||
| 163 | + birthday: '', | ||
| 164 | + school: '' | ||
| 165 | +}) | ||
| 166 | + | ||
| 167 | +// 弹框控制 | ||
| 168 | +const datePickerVisible = ref(false) | ||
| 169 | +const schoolPickerVisible = ref(false) | ||
| 170 | +const avatarPreviewVisible = ref(false) | ||
| 171 | +const toastVisible = ref(false) | ||
| 172 | +const toastMessage = ref('') | ||
| 173 | +const toastType = ref('success') | ||
| 174 | + | ||
| 175 | +// 验证码相关 | ||
| 176 | +const codeCountdown = ref(0) | ||
| 177 | +const isRegistering = ref(false) | ||
| 178 | + | ||
| 179 | +// 日期选择 | ||
| 180 | +const dateValue = ref(new Date()) | ||
| 181 | + | ||
| 182 | +// 学校选择 | ||
| 183 | +const schoolValue = ref([]) | ||
| 184 | +const schoolOptions = ref([ | ||
| 185 | + [ | ||
| 186 | + { text: '上海理工大学', value: '上海理工大学' }, | ||
| 187 | + { text: '上海大学', value: '上海大学' }, | ||
| 188 | + { text: '华东理工大学', value: '华东理工大学' }, | ||
| 189 | + { text: '上海交通大学', value: '上海交通大学' }, | ||
| 190 | + { text: '复旦大学', value: '复旦大学' }, | ||
| 191 | + { text: '同济大学', value: '同济大学' }, | ||
| 192 | + { text: '华东师范大学', value: '华东师范大学' }, | ||
| 193 | + { text: '上海财经大学', value: '上海财经大学' } | ||
| 194 | + ] | ||
| 195 | +]) | ||
| 196 | + | ||
| 197 | +// 手机号验证规则 | ||
| 198 | +const phoneRules = [ | ||
| 199 | + { required: true, message: '请输入手机号' }, | ||
| 200 | + { | ||
| 201 | + pattern: /^1[3-9]\d{9}$/, | ||
| 202 | + message: '请输入正确的手机号格式' | ||
| 203 | + } | ||
| 204 | +] | ||
| 205 | + | ||
| 206 | +// 计算属性:手机号是否有效 | ||
| 207 | +const isPhoneValid = computed(() => { | ||
| 208 | + return /^1[3-9]\d{9}$/.test(formData.phone) | ||
| 209 | +}) | ||
| 210 | + | ||
| 211 | +/** | ||
| 212 | + * 返回上一页 | ||
| 213 | + */ | ||
| 214 | +const goBack = () => { | ||
| 215 | + Taro.navigateBack() | ||
| 216 | +} | ||
| 217 | + | ||
| 218 | +/** | ||
| 219 | + * 更换头像 | ||
| 220 | + */ | ||
| 221 | +const changeAvatar = () => { | ||
| 222 | + Taro.chooseImage({ | ||
| 223 | + count: 1, | ||
| 224 | + sizeType: ['compressed'], | ||
| 225 | + sourceType: ['album', 'camera'], | ||
| 226 | + success: (res) => { | ||
| 227 | + formData.avatar = res.tempFilePaths[0] | ||
| 228 | + } | ||
| 229 | + }) | ||
| 230 | +} | ||
| 231 | + | ||
| 232 | +/** | ||
| 233 | + * 预览头像 | ||
| 234 | + */ | ||
| 235 | +const previewAvatar = () => { | ||
| 236 | + avatarPreviewVisible.value = true | ||
| 237 | +} | ||
| 238 | + | ||
| 239 | +/** | ||
| 240 | + * 发送验证码 | ||
| 241 | + */ | ||
| 242 | +const sendCode = () => { | ||
| 243 | + if (!isPhoneValid.value) { | ||
| 244 | + showToast('请输入正确的手机号', 'error') | ||
| 245 | + return | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + // 模拟发送验证码 | ||
| 249 | + codeCountdown.value = 60 | ||
| 250 | + const timer = setInterval(() => { | ||
| 251 | + codeCountdown.value-- | ||
| 252 | + if (codeCountdown.value <= 0) { | ||
| 253 | + clearInterval(timer) | ||
| 254 | + } | ||
| 255 | + }, 1000) | ||
| 256 | + | ||
| 257 | + showToast('验证码已发送', 'success') | ||
| 258 | +} | ||
| 259 | + | ||
| 260 | +/** | ||
| 261 | + * 显示日期选择器 | ||
| 262 | + */ | ||
| 263 | +const showDatePicker = () => { | ||
| 264 | + datePickerVisible.value = true | ||
| 265 | +} | ||
| 266 | + | ||
| 267 | +/** | ||
| 268 | + * 确认日期选择 | ||
| 269 | + */ | ||
| 270 | +const onDateConfirm = ({ selectedValue }) => { | ||
| 271 | + formData.birthday = `${selectedValue[0]}-${String(selectedValue[1]).padStart(2, '0')}-${String(selectedValue[2]).padStart(2, '0')}` | ||
| 272 | + datePickerVisible.value = false | ||
| 273 | +} | ||
| 274 | + | ||
| 275 | +/** | ||
| 276 | + * 显示学校选择器 | ||
| 277 | + */ | ||
| 278 | +const showSchoolPicker = () => { | ||
| 279 | + schoolPickerVisible.value = true | ||
| 280 | +} | ||
| 281 | + | ||
| 282 | +/** | ||
| 283 | + * 确认学校选择 | ||
| 284 | + */ | ||
| 285 | +const onSchoolConfirm = ({ selectedOptions }) => { | ||
| 286 | + formData.school = selectedOptions[0].text | ||
| 287 | + schoolPickerVisible.value = false | ||
| 288 | +} | ||
| 289 | + | ||
| 290 | +/** | ||
| 291 | + * 显示提示信息 | ||
| 292 | + */ | ||
| 293 | +const showToast = (message, type = 'success') => { | ||
| 294 | + toastMessage.value = message | ||
| 295 | + toastType.value = type | ||
| 296 | + toastVisible.value = true | ||
| 297 | +} | ||
| 298 | + | ||
| 299 | +/** | ||
| 300 | + * 表单验证 | ||
| 301 | + */ | ||
| 302 | +const validateForm = () => { | ||
| 303 | + if (!formData.nickname.trim()) { | ||
| 304 | + showToast('请输入昵称', 'error') | ||
| 305 | + return false | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + if (!isPhoneValid.value) { | ||
| 309 | + showToast('请输入正确的手机号', 'error') | ||
| 310 | + return false | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + if (!formData.verifyCode.trim()) { | ||
| 314 | + showToast('请输入验证码', 'error') | ||
| 315 | + return false | ||
| 316 | + } | ||
| 317 | + | ||
| 318 | + if (!formData.gender) { | ||
| 319 | + showToast('请选择性别', 'error') | ||
| 320 | + return false | ||
| 321 | + } | ||
| 322 | + | ||
| 323 | + if (!formData.school) { | ||
| 324 | + showToast('请选择学校', 'error') | ||
| 325 | + return false | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + return true | ||
| 329 | +} | ||
| 330 | + | ||
| 331 | +/** | ||
| 332 | + * 处理注册 | ||
| 333 | + */ | ||
| 334 | +const handleRegister = async () => { | ||
| 335 | + if (!validateForm()) { | ||
| 336 | + return | ||
| 337 | + } | ||
| 338 | + | ||
| 339 | + isRegistering.value = true | ||
| 340 | + | ||
| 341 | + try { | ||
| 342 | + // 模拟注册API调用 | ||
| 343 | + await new Promise(resolve => setTimeout(resolve, 2000)) | ||
| 344 | + | ||
| 345 | + // TODO: 这里应该调用实际的注册API | ||
| 346 | + // const result = await registerAPI(formData) | ||
| 347 | + | ||
| 348 | + showToast('注册成功', 'success') | ||
| 349 | + | ||
| 350 | + setTimeout(() => { | ||
| 351 | + // 注册成功后跳转到登录页面或首页 | ||
| 352 | + Taro.navigateBack() | ||
| 353 | + }, 1500) | ||
| 354 | + | ||
| 355 | + } catch (error) { | ||
| 356 | + showToast('注册失败,请重试', 'error') | ||
| 357 | + } finally { | ||
| 358 | + isRegistering.value = false | ||
| 359 | + } | ||
| 360 | +} | ||
| 361 | + | ||
| 362 | +// 初始化 | ||
| 363 | +onMounted(() => { | ||
| 364 | + // 可以在这里进行初始化操作 | ||
| 365 | +}) | ||
| 366 | +</script> | ||
| 367 | + | ||
| 368 | +<script> | ||
| 369 | +export default { | ||
| 370 | + name: 'RegisterPage' | ||
| 371 | +} | ||
| 372 | +</script> |
-
Please register or login to post a comment