feat(profile): 添加编辑资料页面功能
- 新增编辑资料页面,包含头像、昵称、手机号等表单字段 - 实现头像上传、日期选择、学校选择等功能 - 添加手机号绑定弹框和验证码发送逻辑 - 更新路由配置和组件类型声明
Showing
7 changed files
with
685 additions
and
5 deletions
| ... | @@ -11,6 +11,7 @@ declare module 'vue' { | ... | @@ -11,6 +11,7 @@ declare module 'vue' { |
| 11 | NutButton: typeof import('@nutui/nutui-taro')['Button'] | 11 | NutButton: typeof import('@nutui/nutui-taro')['Button'] |
| 12 | NutCol: typeof import('@nutui/nutui-taro')['Col'] | 12 | NutCol: typeof import('@nutui/nutui-taro')['Col'] |
| 13 | NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider'] | 13 | NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider'] |
| 14 | + NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker'] | ||
| 14 | NutForm: typeof import('@nutui/nutui-taro')['Form'] | 15 | NutForm: typeof import('@nutui/nutui-taro')['Form'] |
| 15 | NutFormItem: typeof import('@nutui/nutui-taro')['FormItem'] | 16 | NutFormItem: typeof import('@nutui/nutui-taro')['FormItem'] |
| 16 | NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview'] | 17 | NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview'] |
| ... | @@ -20,6 +21,8 @@ declare module 'vue' { | ... | @@ -20,6 +21,8 @@ declare module 'vue' { |
| 20 | NutNavbar: typeof import('@nutui/nutui-taro')['Navbar'] | 21 | NutNavbar: typeof import('@nutui/nutui-taro')['Navbar'] |
| 21 | NutPicker: typeof import('@nutui/nutui-taro')['Picker'] | 22 | NutPicker: typeof import('@nutui/nutui-taro')['Picker'] |
| 22 | NutPopup: typeof import('@nutui/nutui-taro')['Popup'] | 23 | NutPopup: typeof import('@nutui/nutui-taro')['Popup'] |
| 24 | + NutRadio: typeof import('@nutui/nutui-taro')['Radio'] | ||
| 25 | + NutRadioGroup: typeof import('@nutui/nutui-taro')['RadioGroup'] | ||
| 23 | NutRow: typeof import('@nutui/nutui-taro')['Row'] | 26 | NutRow: typeof import('@nutui/nutui-taro')['Row'] |
| 24 | NutSearchbar: typeof import('@nutui/nutui-taro')['Searchbar'] | 27 | NutSearchbar: typeof import('@nutui/nutui-taro')['Searchbar'] |
| 25 | NutSticky: typeof import('@nutui/nutui-taro')['Sticky'] | 28 | NutSticky: typeof import('@nutui/nutui-taro')['Sticky'] |
| ... | @@ -28,6 +31,7 @@ declare module 'vue' { | ... | @@ -28,6 +31,7 @@ declare module 'vue' { |
| 28 | NutTabPane: typeof import('@nutui/nutui-taro')['TabPane'] | 31 | NutTabPane: typeof import('@nutui/nutui-taro')['TabPane'] |
| 29 | NutTabs: typeof import('@nutui/nutui-taro')['Tabs'] | 32 | NutTabs: typeof import('@nutui/nutui-taro')['Tabs'] |
| 30 | NutTextarea: typeof import('@nutui/nutui-taro')['Textarea'] | 33 | NutTextarea: typeof import('@nutui/nutui-taro')['Textarea'] |
| 34 | + NutToast: typeof import('@nutui/nutui-taro')['Toast'] | ||
| 31 | Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] | 35 | Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] |
| 32 | PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] | 36 | PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] |
| 33 | RouterLink: typeof import('vue-router')['RouterLink'] | 37 | RouterLink: typeof import('vue-router')['RouterLink'] | ... | ... |
| 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 14:02:10 | 4 | + * @LastEditTime: 2025-07-02 15:05:28 |
| 5 | * @FilePath: /jgdl/src/app.config.js | 5 | * @FilePath: /jgdl/src/app.config.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -12,6 +12,7 @@ export default { | ... | @@ -12,6 +12,7 @@ export default { |
| 12 | 'pages/sell/index', | 12 | 'pages/sell/index', |
| 13 | 'pages/messages/index', | 13 | 'pages/messages/index', |
| 14 | 'pages/profile/index', | 14 | 'pages/profile/index', |
| 15 | + 'pages/editProfile/index', | ||
| 15 | 'pages/auth/index', | 16 | 'pages/auth/index', |
| 16 | ], | 17 | ], |
| 17 | subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 | 18 | subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-09-19 14:11:06 | 2 | * @Date: 2022-09-19 14:11:06 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-01 10:56:38 | 4 | + * @LastEditTime: 2025-07-02 14:50:57 |
| 5 | - * @FilePath: /myApp/src/pages/demo/index.vue | 5 | + * @FilePath: /jgdl/src/pages/demo/index.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| ... | @@ -10,7 +10,7 @@ | ... | @@ -10,7 +10,7 @@ |
| 10 | </template> | 10 | </template> |
| 11 | 11 | ||
| 12 | <script setup> | 12 | <script setup> |
| 13 | -import '@tarojs/taro/html.css' | 13 | +// import '@tarojs/taro/html.css' |
| 14 | import { ref } from "vue"; | 14 | import { ref } from "vue"; |
| 15 | import "./index.less"; | 15 | import "./index.less"; |
| 16 | 16 | ... | ... |
src/pages/editProfile/index.config.js
0 → 100755
| 1 | +/* | ||
| 2 | + * @Date: 2025-07-02 14:50:25 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-07-02 15:22:00 | ||
| 5 | + * @FilePath: /jgdl/src/pages/editProfile/index.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +export default { | ||
| 9 | + navigationBarTitleText: '编辑资料', | ||
| 10 | + usingComponents: { | ||
| 11 | + }, | ||
| 12 | +} |
src/pages/editProfile/index.less
0 → 100644
| 1 | +.edit-profile-page { | ||
| 2 | + background-color: #f5f5f5; | ||
| 3 | + min-height: 100vh; | ||
| 4 | +} | ||
| 5 | + | ||
| 6 | +// 头像区域 | ||
| 7 | +.avatar-section { | ||
| 8 | + display: flex; | ||
| 9 | + flex-direction: column; | ||
| 10 | + align-items: center; | ||
| 11 | + padding: 48rpx 0; | ||
| 12 | + background-color: #fff; | ||
| 13 | + margin-bottom: 20rpx; | ||
| 14 | + | ||
| 15 | + .avatar-container { | ||
| 16 | + position: relative; | ||
| 17 | + margin-bottom: 32rpx; | ||
| 18 | + | ||
| 19 | + .avatar-image { | ||
| 20 | + width: 192rpx; | ||
| 21 | + height: 192rpx; | ||
| 22 | + border-radius: 50%; | ||
| 23 | + object-fit: cover; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + .camera-btn { | ||
| 27 | + position: absolute; | ||
| 28 | + bottom: 0; | ||
| 29 | + right: 0; | ||
| 30 | + width: 48rpx; | ||
| 31 | + height: 48rpx; | ||
| 32 | + background-color: #fff; | ||
| 33 | + border-radius: 50%; | ||
| 34 | + display: flex; | ||
| 35 | + align-items: center; | ||
| 36 | + justify-content: center; | ||
| 37 | + box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); | ||
| 38 | + | ||
| 39 | + .camera-icon { | ||
| 40 | + width: 32rpx; | ||
| 41 | + height: 32rpx; | ||
| 42 | + color: #666; | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + .change-avatar-btn { | ||
| 48 | + background-color: #f5f5f5; | ||
| 49 | + padding: 16rpx 40rpx; | ||
| 50 | + border-radius: 48rpx; | ||
| 51 | + | ||
| 52 | + .change-avatar-text { | ||
| 53 | + color: #666; | ||
| 54 | + font-size: 28rpx; | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +// 表单容器 | ||
| 60 | +.form-container { | ||
| 61 | + background-color: #fff; | ||
| 62 | + margin-bottom: 40rpx; | ||
| 63 | + | ||
| 64 | + :deep(.nut-form-item) { | ||
| 65 | + padding: 32rpx 32rpx; | ||
| 66 | + border-bottom: 1rpx solid #f0f0f0; | ||
| 67 | + | ||
| 68 | + &:last-child { | ||
| 69 | + border-bottom: none; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + .nut-form-item__label { | ||
| 73 | + color: #333; | ||
| 74 | + font-size: 32rpx; | ||
| 75 | + font-weight: normal; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + .nut-input { | ||
| 79 | + text-align: right; | ||
| 80 | + | ||
| 81 | + .nut-input__inner { | ||
| 82 | + color: #666; | ||
| 83 | + font-size: 32rpx; | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + // 手机号项 | ||
| 89 | + .phone-item { | ||
| 90 | + display: flex; | ||
| 91 | + align-items: center; | ||
| 92 | + justify-content: flex-end; | ||
| 93 | + width: 100%; | ||
| 94 | + | ||
| 95 | + .phone-value { | ||
| 96 | + color: #666; | ||
| 97 | + font-size: 32rpx; | ||
| 98 | + margin-right: 16rpx; | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + .arrow-icon { | ||
| 102 | + width: 32rpx; | ||
| 103 | + height: 32rpx; | ||
| 104 | + color: #ccc; | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + // 生日项 | ||
| 109 | + .birthday-item { | ||
| 110 | + display: flex; | ||
| 111 | + align-items: center; | ||
| 112 | + justify-content: flex-end; | ||
| 113 | + width: 100%; | ||
| 114 | + | ||
| 115 | + .birthday-value { | ||
| 116 | + color: #666; | ||
| 117 | + font-size: 32rpx; | ||
| 118 | + margin-right: 16rpx; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + .arrow-icon { | ||
| 122 | + width: 32rpx; | ||
| 123 | + height: 32rpx; | ||
| 124 | + color: #ccc; | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + // 学校项 | ||
| 129 | + .school-item { | ||
| 130 | + display: flex; | ||
| 131 | + align-items: center; | ||
| 132 | + justify-content: flex-end; | ||
| 133 | + width: 100%; | ||
| 134 | + | ||
| 135 | + .school-value { | ||
| 136 | + color: #666; | ||
| 137 | + font-size: 32rpx; | ||
| 138 | + margin-right: 16rpx; | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + .arrow-icon { | ||
| 142 | + width: 32rpx; | ||
| 143 | + height: 32rpx; | ||
| 144 | + color: #ccc; | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + // 性别单选组 | ||
| 149 | + :deep(.nut-radio-group) { | ||
| 150 | + display: flex; | ||
| 151 | + justify-content: flex-end; | ||
| 152 | + gap: 64rpx; | ||
| 153 | + | ||
| 154 | + .nut-radio { | ||
| 155 | + .nut-radio__label { | ||
| 156 | + color: #666; | ||
| 157 | + font-size: 32rpx; | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + &.nut-radio--checked { | ||
| 161 | + .nut-radio__icon { | ||
| 162 | + color: #f97316; | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + .nut-radio__label { | ||
| 166 | + color: #333; | ||
| 167 | + } | ||
| 168 | + } | ||
| 169 | + } | ||
| 170 | + } | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | +// 保存按钮区域 | ||
| 174 | +.save-section { | ||
| 175 | + padding: 0 32rpx 80rpx; | ||
| 176 | + | ||
| 177 | + :deep(.nut-button) { | ||
| 178 | + height: 96rpx; | ||
| 179 | + border-radius: 48rpx; | ||
| 180 | + font-size: 32rpx; | ||
| 181 | + font-weight: 500; | ||
| 182 | + } | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +// 手机号绑定弹框 | ||
| 186 | +.phone-dialog { | ||
| 187 | + padding: 48rpx 32rpx 32rpx; | ||
| 188 | + border-radius: 16rpx; | ||
| 189 | + | ||
| 190 | + .dialog-title { | ||
| 191 | + text-align: center; | ||
| 192 | + font-size: 36rpx; | ||
| 193 | + font-weight: 500; | ||
| 194 | + color: #333; | ||
| 195 | + margin-bottom: 48rpx; | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + .dialog-content { | ||
| 199 | + margin-bottom: 48rpx; | ||
| 200 | + | ||
| 201 | + .phone-input { | ||
| 202 | + margin-bottom: 32rpx; | ||
| 203 | + | ||
| 204 | + :deep(.nut-input__inner) { | ||
| 205 | + border: 1rpx solid #e0e0e0; | ||
| 206 | + border-radius: 8rpx; | ||
| 207 | + padding: 24rpx 16rpx; | ||
| 208 | + font-size: 32rpx; | ||
| 209 | + } | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + .code-row { | ||
| 213 | + display: flex; | ||
| 214 | + gap: 16rpx; | ||
| 215 | + align-items: center; | ||
| 216 | + | ||
| 217 | + .code-input { | ||
| 218 | + flex: 1; | ||
| 219 | + | ||
| 220 | + :deep(.nut-input__inner) { | ||
| 221 | + border: 1rpx solid #e0e0e0; | ||
| 222 | + border-radius: 8rpx; | ||
| 223 | + padding: 24rpx 16rpx; | ||
| 224 | + font-size: 32rpx; | ||
| 225 | + } | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + .code-btn { | ||
| 229 | + :deep(.nut-button__warp) { | ||
| 230 | + padding: 24rpx 32rpx; | ||
| 231 | + font-size: 28rpx; | ||
| 232 | + white-space: nowrap; | ||
| 233 | + } | ||
| 234 | + } | ||
| 235 | + } | ||
| 236 | + } | ||
| 237 | + | ||
| 238 | + .dialog-actions { | ||
| 239 | + display: flex; | ||
| 240 | + gap: 24rpx; | ||
| 241 | + | ||
| 242 | + .cancel-btn, | ||
| 243 | + .confirm-btn { | ||
| 244 | + flex: 1; | ||
| 245 | + | ||
| 246 | + :deep(.nut-button__warp) { | ||
| 247 | + height: 80rpx; | ||
| 248 | + border-radius: 8rpx; | ||
| 249 | + font-size: 32rpx; | ||
| 250 | + } | ||
| 251 | + } | ||
| 252 | + | ||
| 253 | + .cancel-btn { | ||
| 254 | + :deep(.nut-button__warp) { | ||
| 255 | + background-color: #f5f5f5; | ||
| 256 | + color: #666; | ||
| 257 | + border: none; | ||
| 258 | + } | ||
| 259 | + } | ||
| 260 | + } | ||
| 261 | +} | ||
| 262 | + | ||
| 263 | +// 响应式适配 | ||
| 264 | +@media (max-width: 750rpx) { | ||
| 265 | + .avatar-section { | ||
| 266 | + padding: 40rpx 0; | ||
| 267 | + | ||
| 268 | + .avatar-container { | ||
| 269 | + .avatar-image { | ||
| 270 | + width: 160rpx; | ||
| 271 | + height: 160rpx; | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + .camera-btn { | ||
| 275 | + width: 40rpx; | ||
| 276 | + height: 40rpx; | ||
| 277 | + | ||
| 278 | + .camera-icon { | ||
| 279 | + width: 24rpx; | ||
| 280 | + height: 24rpx; | ||
| 281 | + } | ||
| 282 | + } | ||
| 283 | + } | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + .form-container { | ||
| 287 | + :deep(.nut-form-item) { | ||
| 288 | + padding: 24rpx 24rpx; | ||
| 289 | + | ||
| 290 | + .nut-form-item__label { | ||
| 291 | + font-size: 28rpx; | ||
| 292 | + } | ||
| 293 | + | ||
| 294 | + .nut-input { | ||
| 295 | + .nut-input__inner { | ||
| 296 | + font-size: 28rpx; | ||
| 297 | + } | ||
| 298 | + } | ||
| 299 | + } | ||
| 300 | + | ||
| 301 | + .phone-item, | ||
| 302 | + .birthday-item, | ||
| 303 | + .school-item { | ||
| 304 | + .phone-value, | ||
| 305 | + .birthday-value, | ||
| 306 | + .school-value { | ||
| 307 | + font-size: 28rpx; | ||
| 308 | + } | ||
| 309 | + } | ||
| 310 | + } | ||
| 311 | + | ||
| 312 | + .save-section { | ||
| 313 | + padding: 0 24rpx 60rpx; | ||
| 314 | + | ||
| 315 | + :deep(.nut-button) { | ||
| 316 | + height: 80rpx; | ||
| 317 | + font-size: 28rpx; | ||
| 318 | + } | ||
| 319 | + } | ||
| 320 | +} |
src/pages/editProfile/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <view class="edit-profile-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"> | ||
| 23 | + <nut-input | ||
| 24 | + v-model="formData.nickname" | ||
| 25 | + placeholder="请输入昵称" | ||
| 26 | + input-align="right" | ||
| 27 | + /> | ||
| 28 | + </nut-form-item> | ||
| 29 | + | ||
| 30 | + <!-- 手机号 --> | ||
| 31 | + <nut-form-item label="手机号" prop="phone"> | ||
| 32 | + <view class="phone-item" @click="showPhoneDialog"> | ||
| 33 | + <text class="phone-value">{{ formData.phone || '未绑定' }}</text> | ||
| 34 | + <Right class="arrow-icon" /> | ||
| 35 | + </view> | ||
| 36 | + </nut-form-item> | ||
| 37 | + | ||
| 38 | + <!-- 性别 --> | ||
| 39 | + <nut-form-item label="性别" prop="gender" body-align="right"> | ||
| 40 | + <nut-radio-group v-model="formData.gender" direction="horizontal"> | ||
| 41 | + <nut-radio label="男">男</nut-radio> | ||
| 42 | + <nut-radio label="女">女</nut-radio> | ||
| 43 | + </nut-radio-group> | ||
| 44 | + </nut-form-item> | ||
| 45 | + | ||
| 46 | + <!-- 生日 --> | ||
| 47 | + <nut-form-item label="生日" prop="birthday"> | ||
| 48 | + <view class="birthday-item" @click="showDatePicker"> | ||
| 49 | + <text class="birthday-value">{{ formData.birthday || '未设置' }}</text> | ||
| 50 | + <Right class="arrow-icon" /> | ||
| 51 | + </view> | ||
| 52 | + </nut-form-item> | ||
| 53 | + | ||
| 54 | + <!-- 所在学校 --> | ||
| 55 | + <nut-form-item label="所在学校" prop="school"> | ||
| 56 | + <view class="school-item" @click="showSchoolPicker"> | ||
| 57 | + <text class="school-value">{{ formData.school || '请选择学校' }}</text> | ||
| 58 | + <Right class="arrow-icon" /> | ||
| 59 | + </view> | ||
| 60 | + </nut-form-item> | ||
| 61 | + | ||
| 62 | + <!-- 微信号 --> | ||
| 63 | + <nut-form-item label="微信号" prop="wechat"> | ||
| 64 | + <nut-input | ||
| 65 | + v-model="formData.wechat" | ||
| 66 | + placeholder="请输入微信号" | ||
| 67 | + input-align="right" | ||
| 68 | + > | ||
| 69 | + </nut-input> | ||
| 70 | + </nut-form-item> | ||
| 71 | + </view> | ||
| 72 | + </nut-form> | ||
| 73 | + | ||
| 74 | + <!-- 保存按钮 --> | ||
| 75 | + <view class="save-section mt-32"> | ||
| 76 | + <nut-button | ||
| 77 | + color="#f97316" | ||
| 78 | + size="large" | ||
| 79 | + block | ||
| 80 | + @click="handleSave" | ||
| 81 | + > | ||
| 82 | + 保存 | ||
| 83 | + </nut-button> | ||
| 84 | + </view> | ||
| 85 | + | ||
| 86 | + <!-- 手机号绑定弹框 --> | ||
| 87 | + <nut-popup v-model:visible="phoneDialogVisible" position="center" :style="{ width: '80%' }"> | ||
| 88 | + <view class="phone-dialog"> | ||
| 89 | + <view class="dialog-title">重新绑定手机号</view> | ||
| 90 | + <view class="dialog-content"> | ||
| 91 | + <nut-input | ||
| 92 | + v-model="newPhone" | ||
| 93 | + placeholder="请输入新手机号" | ||
| 94 | + type="tel" | ||
| 95 | + class="phone-input" | ||
| 96 | + /> | ||
| 97 | + <view class="code-row"> | ||
| 98 | + <nut-input | ||
| 99 | + v-model="verifyCode" | ||
| 100 | + placeholder="验证码" | ||
| 101 | + class="code-input" | ||
| 102 | + /> | ||
| 103 | + <nut-button | ||
| 104 | + size="small" | ||
| 105 | + :disabled="codeCountdown > 0" | ||
| 106 | + @click="sendCode" | ||
| 107 | + class="code-btn" | ||
| 108 | + > | ||
| 109 | + {{ codeCountdown > 0 ? `${codeCountdown}s` : '发送验证码' }} | ||
| 110 | + </nut-button> | ||
| 111 | + </view> | ||
| 112 | + </view> | ||
| 113 | + <view class="dialog-actions"> | ||
| 114 | + <nut-button | ||
| 115 | + size="small" | ||
| 116 | + @click="phoneDialogVisible = false" | ||
| 117 | + class="cancel-btn" | ||
| 118 | + > | ||
| 119 | + 取消 | ||
| 120 | + </nut-button> | ||
| 121 | + <nut-button | ||
| 122 | + size="small" | ||
| 123 | + color="#f97316" | ||
| 124 | + @click="confirmPhoneChange" | ||
| 125 | + class="confirm-btn" | ||
| 126 | + > | ||
| 127 | + 确认 | ||
| 128 | + </nut-button> | ||
| 129 | + </view> | ||
| 130 | + </view> | ||
| 131 | + </nut-popup> | ||
| 132 | + | ||
| 133 | + <!-- 日期选择器 --> | ||
| 134 | + <nut-popup v-model:visible="datePickerVisible" position="bottom"> | ||
| 135 | + <nut-date-picker | ||
| 136 | + v-model="dateValue" | ||
| 137 | + title="选择生日" | ||
| 138 | + @confirm="onDateConfirm" | ||
| 139 | + @cancel="datePickerVisible = false" | ||
| 140 | + /> | ||
| 141 | + </nut-popup> | ||
| 142 | + | ||
| 143 | + <!-- 学校选择器 --> | ||
| 144 | + <nut-popup v-model:visible="schoolPickerVisible" position="bottom"> | ||
| 145 | + <nut-picker | ||
| 146 | + v-model="schoolValue" | ||
| 147 | + :columns="schoolOptions" | ||
| 148 | + title="选择学校" | ||
| 149 | + @confirm="onSchoolConfirm" | ||
| 150 | + @cancel="schoolPickerVisible = false" | ||
| 151 | + /> | ||
| 152 | + </nut-popup> | ||
| 153 | + | ||
| 154 | + <!-- 头像预览 --> | ||
| 155 | + <nut-image-preview | ||
| 156 | + v-model:show="avatarPreviewVisible" | ||
| 157 | + :images="[formData.avatar || defaultAvatar]" | ||
| 158 | + /> | ||
| 159 | + | ||
| 160 | + <!-- 成功提示 --> | ||
| 161 | + <nut-toast | ||
| 162 | + v-model:visible="toastVisible" | ||
| 163 | + msg="保存成功" | ||
| 164 | + type="success" | ||
| 165 | + /> | ||
| 166 | + </view> | ||
| 167 | +</template> | ||
| 168 | + | ||
| 169 | +<script setup> | ||
| 170 | +import { ref, reactive, onMounted } from 'vue' | ||
| 171 | +import Taro from '@tarojs/taro' | ||
| 172 | +import { RectLeft, Right } from '@nutui/icons-vue-taro' | ||
| 173 | +import './index.less' | ||
| 174 | + | ||
| 175 | +// 主题配置 | ||
| 176 | +const themeVars = ref({ | ||
| 177 | + navbarBackground: '#fb923c', | ||
| 178 | + navbarColor: '#ffffff', | ||
| 179 | +}) | ||
| 180 | + | ||
| 181 | +// 默认头像 | ||
| 182 | +const defaultAvatar = 'https://randomuser.me/api/portraits/men/32.jpg' | ||
| 183 | + | ||
| 184 | +// 表单数据 | ||
| 185 | +const formData = reactive({ | ||
| 186 | + avatar: '', | ||
| 187 | + nickname: '张先生', | ||
| 188 | + phone: '139 2233 8888', | ||
| 189 | + gender: '男', | ||
| 190 | + birthday: '', | ||
| 191 | + school: '上海理工大学', | ||
| 192 | + wechat: '' | ||
| 193 | +}) | ||
| 194 | + | ||
| 195 | +// 弹框控制 | ||
| 196 | +const phoneDialogVisible = ref(false) | ||
| 197 | +const datePickerVisible = ref(false) | ||
| 198 | +const schoolPickerVisible = ref(false) | ||
| 199 | +const avatarPreviewVisible = ref(false) | ||
| 200 | +const toastVisible = ref(false) | ||
| 201 | + | ||
| 202 | +// 手机号相关 | ||
| 203 | +const newPhone = ref('') | ||
| 204 | +const verifyCode = ref('') | ||
| 205 | +const codeCountdown = ref(0) | ||
| 206 | + | ||
| 207 | +// 日期选择 | ||
| 208 | +const dateValue = ref(new Date()) | ||
| 209 | + | ||
| 210 | +// 学校选择 | ||
| 211 | +const schoolValue = ref([]) | ||
| 212 | +const schoolOptions = ref([ | ||
| 213 | + [ | ||
| 214 | + { text: '上海理工大学', value: '上海理工大学' }, | ||
| 215 | + { text: '上海大学', value: '上海大学' }, | ||
| 216 | + { text: '华东理工大学', value: '华东理工大学' }, | ||
| 217 | + { text: '上海交通大学', value: '上海交通大学' }, | ||
| 218 | + { text: '复旦大学', value: '复旦大学' }, | ||
| 219 | + { text: '同济大学', value: '同济大学' }, | ||
| 220 | + { text: '华东师范大学', value: '华东师范大学' }, | ||
| 221 | + { text: '上海财经大学', value: '上海财经大学' } | ||
| 222 | + ] | ||
| 223 | +]) | ||
| 224 | + | ||
| 225 | +// 返回上一页 | ||
| 226 | +const goBack = () => { | ||
| 227 | + Taro.navigateBack() | ||
| 228 | +} | ||
| 229 | + | ||
| 230 | +// 更换头像 | ||
| 231 | +const changeAvatar = () => { | ||
| 232 | + Taro.chooseImage({ | ||
| 233 | + count: 1, | ||
| 234 | + sizeType: ['compressed'], | ||
| 235 | + sourceType: ['album', 'camera'], | ||
| 236 | + success: (res) => { | ||
| 237 | + formData.avatar = res.tempFilePaths[0] | ||
| 238 | + } | ||
| 239 | + }) | ||
| 240 | +} | ||
| 241 | + | ||
| 242 | +// 预览头像 | ||
| 243 | +const previewAvatar = () => { | ||
| 244 | + avatarPreviewVisible.value = true | ||
| 245 | +} | ||
| 246 | + | ||
| 247 | +// 显示手机号弹框 | ||
| 248 | +const showPhoneDialog = () => { | ||
| 249 | + phoneDialogVisible.value = true | ||
| 250 | + newPhone.value = '' | ||
| 251 | + verifyCode.value = '' | ||
| 252 | +} | ||
| 253 | + | ||
| 254 | +// 发送验证码 | ||
| 255 | +const sendCode = () => { | ||
| 256 | + if (!newPhone.value) { | ||
| 257 | + Taro.showToast({ | ||
| 258 | + title: '请输入手机号', | ||
| 259 | + icon: 'none' | ||
| 260 | + }) | ||
| 261 | + return | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + // 模拟发送验证码 | ||
| 265 | + codeCountdown.value = 60 | ||
| 266 | + const timer = setInterval(() => { | ||
| 267 | + codeCountdown.value-- | ||
| 268 | + if (codeCountdown.value <= 0) { | ||
| 269 | + clearInterval(timer) | ||
| 270 | + } | ||
| 271 | + }, 1000) | ||
| 272 | + | ||
| 273 | + Taro.showToast({ | ||
| 274 | + title: '验证码已发送', | ||
| 275 | + icon: 'success' | ||
| 276 | + }) | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | +// 确认手机号更改 | ||
| 280 | +const confirmPhoneChange = () => { | ||
| 281 | + if (!newPhone.value || !verifyCode.value) { | ||
| 282 | + Taro.showToast({ | ||
| 283 | + title: '请填写完整信息', | ||
| 284 | + icon: 'none' | ||
| 285 | + }) | ||
| 286 | + return | ||
| 287 | + } | ||
| 288 | + | ||
| 289 | + formData.phone = newPhone.value | ||
| 290 | + phoneDialogVisible.value = false | ||
| 291 | + Taro.showToast({ | ||
| 292 | + title: '手机号绑定成功', | ||
| 293 | + icon: 'success' | ||
| 294 | + }) | ||
| 295 | +} | ||
| 296 | + | ||
| 297 | +// 显示日期选择器 | ||
| 298 | +const showDatePicker = () => { | ||
| 299 | + datePickerVisible.value = true | ||
| 300 | +} | ||
| 301 | + | ||
| 302 | +// 确认日期选择 | ||
| 303 | +const onDateConfirm = ({ selectedValue }) => { | ||
| 304 | + const date = new Date(selectedValue[0], selectedValue[1] - 1, selectedValue[2]) | ||
| 305 | + formData.birthday = `${selectedValue[0]}-${String(selectedValue[1]).padStart(2, '0')}-${String(selectedValue[2]).padStart(2, '0')}` | ||
| 306 | + datePickerVisible.value = false | ||
| 307 | +} | ||
| 308 | + | ||
| 309 | +// 显示学校选择器 | ||
| 310 | +const showSchoolPicker = () => { | ||
| 311 | + schoolPickerVisible.value = true | ||
| 312 | +} | ||
| 313 | + | ||
| 314 | +// 确认学校选择 | ||
| 315 | +const onSchoolConfirm = ({ selectedOptions }) => { | ||
| 316 | + formData.school = selectedOptions[0].text | ||
| 317 | + schoolPickerVisible.value = false | ||
| 318 | +} | ||
| 319 | + | ||
| 320 | +// 保存 | ||
| 321 | +const handleSave = () => { | ||
| 322 | + // 这里可以添加表单验证 | ||
| 323 | + console.log('保存数据:', formData) | ||
| 324 | + | ||
| 325 | + toastVisible.value = true | ||
| 326 | + | ||
| 327 | + setTimeout(() => { | ||
| 328 | + toastVisible.value = false | ||
| 329 | + goBack() | ||
| 330 | + }, 1500) | ||
| 331 | +} | ||
| 332 | + | ||
| 333 | +// 初始化 | ||
| 334 | +onMounted(() => { | ||
| 335 | + // 可以在这里加载用户数据 | ||
| 336 | +}) | ||
| 337 | +</script> | ||
| 338 | + | ||
| 339 | +<script> | ||
| 340 | +export default { | ||
| 341 | + name: 'EditProfilePage' | ||
| 342 | +} | ||
| 343 | +</script> |
| ... | @@ -103,7 +103,7 @@ const userStats = ref({ | ... | @@ -103,7 +103,7 @@ const userStats = ref({ |
| 103 | */ | 103 | */ |
| 104 | const onEditProfile = () => { | 104 | const onEditProfile = () => { |
| 105 | Taro.navigateTo({ | 105 | Taro.navigateTo({ |
| 106 | - url: '/pages/edit-profile/index' | 106 | + url: '/pages/editProfile/index' |
| 107 | }) | 107 | }) |
| 108 | } | 108 | } |
| 109 | 109 | ... | ... |
-
Please register or login to post a comment