Q3学习计划
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

279 lines
6.7 KiB

  1. <template>
  2. <view class="viewport">
  3. <!-- 导航栏 -->
  4. <view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
  5. <navigator open-type="navigateBack" class="back icon-left" hover-class="none"></navigator>
  6. <view class="title">个人信息</view>
  7. </view>
  8. <!-- 头像 -->
  9. <view class="avatar">
  10. <view class="avatar-content" @tap="onAvatarChange">
  11. <image class="image" :src="profile?.avatar" mode="aspectFill" />
  12. <text class="text">点击修改头像</text>
  13. </view>
  14. </view>
  15. <!-- 表单 -->
  16. <view class="form">
  17. <!-- 表单内容 -->
  18. <view class="form-content">
  19. <view class="form-item">
  20. <text class="label">账号</text>
  21. <text class="account">{{ profile?.account }}</text>
  22. </view>
  23. <view class="form-item">
  24. <text class="label">昵称</text>
  25. <input class="input" type="text" placeholder="请填写昵称" v-model="profile.nickname" />
  26. </view>
  27. <view class="form-item">
  28. <text class="label">性别</text>
  29. <radio-group>
  30. <label class="radio">
  31. <radio value="男" color="#27ba9b" :checked="profile?.gender === '男'" />
  32. </label>
  33. <label class="radio">
  34. <radio value="女" color="#27ba9b" :checked="profile?.gender === '女'" />
  35. </label>
  36. </radio-group>
  37. </view>
  38. <view class="form-item">
  39. <text class="label">生日</text>
  40. <picker
  41. class="picker"
  42. mode="date"
  43. start="1900-01-01"
  44. :end="new Date()"
  45. :value="profile?.birthday"
  46. >
  47. <view v-if="profile?.birthday">{{ profile?.birthday }}</view>
  48. <view class="placeholder" v-else>请选择日期</view>
  49. </picker>
  50. </view>
  51. <!-- 只有微信小程序端内置了省市区数据 -->
  52. <!-- #ifdef MP-WEIXIN -->
  53. <view class="form-item">
  54. <text class="label">城市</text>
  55. <picker class="picker" mode="region" :value="profile?.fullLocation?.split(' ')">
  56. <view v-if="profile?.fullLocation">{{ profile?.fullLocation }}</view>
  57. <view class="placeholder" v-else>请选择城市</view>
  58. </picker>
  59. </view>
  60. <!-- #endif -->
  61. <view class="form-item">
  62. <text class="label">职业</text>
  63. <input class="input" type="text" placeholder="请填写职业" :value="profile?.profession" />
  64. </view>
  65. </view>
  66. <!-- 提交按钮 -->
  67. <button class="form-button" @tap="onSubmit"> </button>
  68. </view>
  69. </view>
  70. </template>
  71. <script setup lang="ts">
  72. import { onLoad } from '@dcloudio/uni-app'
  73. import { ref } from 'vue'
  74. import type { Gender, ProfileDetail } from '@/types/member'
  75. import { getMemberProfileAPI, putMemberProfileAPI } from '@/services/profile'
  76. import { useMemberStore } from '@/stores'
  77. // 获取屏幕边界到安全区域距离
  78. const { safeAreaInsets } = uni.getSystemInfoSync()
  79. const memberStore = useMemberStore()
  80. // 获取个人信息,修改个人信息需提供初始值
  81. const profile = ref({} as ProfileDetail)
  82. const getMemberProfileData = async () => {
  83. const res = await getMemberProfileAPI()
  84. profile.value = res.result
  85. }
  86. // 修改头像
  87. const onAvatarChange = () => {
  88. // 调用拍照/选择图片
  89. uni.chooseMedia({
  90. // 文件个数
  91. count: 1,
  92. // 文件类型
  93. mediaType: ['image'],
  94. success: (res) => {
  95. // 本地路径
  96. const { tempFilePath } = res.tempFiles[0]
  97. // 文件上传
  98. uni.uploadFile({
  99. url: '/member/profile/avatar',
  100. name: 'file', // 后端数据字段名
  101. filePath: tempFilePath, // 新头像
  102. success: (res) => {
  103. // 判断状态码是否上传成功
  104. if (res.statusCode === 200) {
  105. // 提取头像
  106. const { avatar } = JSON.parse(res.data).result
  107. // 当前页面更新头像
  108. profile.value!.avatar = avatar
  109. // Store头像更新
  110. memberStore.profile!.avatar = avatar
  111. uni.showToast({ icon: 'success', title: '更新成功' })
  112. } else {
  113. uni.showToast({ icon: 'error', title: '出现错误' })
  114. }
  115. },
  116. })
  117. },
  118. })
  119. }
  120. const onSubmit = async () => {
  121. //修改个人信息
  122. const res = await putMemberProfileAPI({
  123. nickname: profile.value.nickname,
  124. })
  125. // 更新Store昵称
  126. memberStore.profile!.nickname = res.result.nickname
  127. // 成功提示
  128. uni.showToast({
  129. icon: 'success',
  130. title: '保存成功',
  131. })
  132. setTimeout(() => {
  133. uni.navigateBack()
  134. }, 400)
  135. }
  136. onLoad(() => {
  137. getMemberProfileData()
  138. })
  139. </script>
  140. <style lang="scss">
  141. page {
  142. background-color: #f4f4f4;
  143. }
  144. .viewport {
  145. display: flex;
  146. flex-direction: column;
  147. height: 100%;
  148. background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/order_bg.png);
  149. background-size: auto 420rpx;
  150. background-repeat: no-repeat;
  151. }
  152. // 导航栏
  153. .navbar {
  154. position: relative;
  155. .title {
  156. height: 40px;
  157. display: flex;
  158. justify-content: center;
  159. align-items: center;
  160. font-size: 16px;
  161. font-weight: 500;
  162. color: #fff;
  163. }
  164. .back {
  165. position: absolute;
  166. height: 40px;
  167. width: 40px;
  168. left: 0;
  169. font-size: 20px;
  170. color: #fff;
  171. display: flex;
  172. justify-content: center;
  173. align-items: center;
  174. }
  175. }
  176. // 头像
  177. .avatar {
  178. text-align: center;
  179. width: 100%;
  180. height: 260rpx;
  181. display: flex;
  182. flex-direction: column;
  183. justify-content: center;
  184. align-items: center;
  185. .image {
  186. width: 160rpx;
  187. height: 160rpx;
  188. border-radius: 50%;
  189. background-color: #eee;
  190. }
  191. .text {
  192. display: block;
  193. padding-top: 20rpx;
  194. line-height: 1;
  195. font-size: 26rpx;
  196. color: #fff;
  197. }
  198. }
  199. // 表单
  200. .form {
  201. background-color: #f4f4f4;
  202. &-content {
  203. margin: 20rpx 20rpx 0;
  204. padding: 0 20rpx;
  205. border-radius: 10rpx;
  206. background-color: #fff;
  207. }
  208. &-item {
  209. display: flex;
  210. height: 96rpx;
  211. line-height: 46rpx;
  212. padding: 25rpx 10rpx;
  213. background-color: #fff;
  214. font-size: 28rpx;
  215. border-bottom: 1rpx solid #ddd;
  216. &:last-child {
  217. border: none;
  218. }
  219. .label {
  220. width: 180rpx;
  221. color: #333;
  222. }
  223. .account {
  224. color: #666;
  225. }
  226. .input {
  227. flex: 1;
  228. display: block;
  229. height: 46rpx;
  230. }
  231. .radio {
  232. margin-right: 20rpx;
  233. }
  234. .picker {
  235. flex: 1;
  236. }
  237. .placeholder {
  238. color: #808080;
  239. }
  240. }
  241. &-button {
  242. height: 80rpx;
  243. text-align: center;
  244. line-height: 80rpx;
  245. margin: 30rpx 20rpx;
  246. color: #fff;
  247. border-radius: 80rpx;
  248. font-size: 30rpx;
  249. background-color: #27ba9b;
  250. }
  251. }
  252. </style>