四零语境前端代码仓库
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.

326 lines
7.7 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <view class="profile-container">
  3. <!-- 个人信息表单 -->
  4. <view class="form-container">
  5. <view class="form-title">个人信息</view>
  6. <!-- 昵称 -->
  7. <view class="form-item">
  8. <view class="label">
  9. <text class="required">*</text>
  10. <text>昵称</text>
  11. </view>
  12. <uv-input
  13. v-model="userInfo.name"
  14. placeholder="请输入昵称"
  15. type="nickname"
  16. :customStyle="inputStyle"
  17. border="bottom"
  18. ></uv-input>
  19. </view>
  20. <!-- 电话 -->
  21. <view class="form-item">
  22. <view class="label">
  23. <text class="required">*</text>
  24. <text>电话</text>
  25. </view>
  26. <uv-input
  27. v-model="userInfo.phone"
  28. placeholder="请输入手机号"
  29. :customStyle="inputStyle"
  30. border="bottom"
  31. type="number"
  32. ></uv-input>
  33. </view>
  34. <!-- 头像 -->
  35. <view class="form-item">
  36. <view class="label">
  37. <text class="required">*</text>
  38. <text>头像</text>
  39. </view>
  40. <button class="avatar-container" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
  41. <view
  42. v-if="userInfo.avatar === 'undefined'"
  43. class="avatar-image"
  44. >
  45. <uv-icon name="camera" size="40" color="white"></uv-icon>
  46. </view>
  47. <image
  48. v-else
  49. :src="userInfo.avatar || '/static/默认头像.png'"
  50. class="avatar-image"
  51. mode="aspectFill"
  52. ></image>
  53. <!-- 遮罩层 -->
  54. <view class="avatar-mask" :class="{ 'fade-out': showMask }" v-if="showMask">
  55. <text>点击上传头像</text>
  56. </view>
  57. </button>
  58. </view>
  59. </view>
  60. <!-- 固定底部保存按钮 -->
  61. <view class="save-button-container">
  62. <uv-button
  63. @click="saveProfile"
  64. :customStyle="saveButtonStyle"
  65. shape="circle"
  66. >
  67. 保存
  68. </uv-button>
  69. </view>
  70. </view>
  71. </template>
  72. <script>
  73. export default {
  74. data() {
  75. return {
  76. userInfo: {
  77. avatar: 'undefined',
  78. name: '',
  79. phone: ''
  80. },
  81. showMask: true,
  82. saveButtonStyle: {
  83. backgroundColor: '#06DADC',
  84. borderRadius: '41rpx',
  85. height: '94rpx',
  86. width: '594rpx',
  87. border: 'none',
  88. color: '#fff',
  89. fontSize: '32rpx',
  90. fontWeight: '500'
  91. },
  92. inputStyle: {
  93. backgroundColor: '#fff',
  94. borderRadius: '12rpx',
  95. padding: '0 -20rpx',
  96. fontSize: '28rpx'
  97. }
  98. }
  99. },
  100. methods: {
  101. // // 微信昵称审核回调
  102. // onNicknameReview(e) {
  103. // console.log('微信昵称审核回调', e);
  104. // if (e.detail.pass) {
  105. // // 审核通过,使用微信昵称
  106. // this.userInfo.name = e.detail.value;
  107. // console.log('微信昵称审核通过:', e.detail.value);
  108. // uni.showToast({
  109. // title: '昵称获取成功',
  110. // icon: 'success'
  111. // });
  112. // } else {
  113. // // 审核不通过
  114. // console.log('微信昵称审核不通过');
  115. // uni.showToast({
  116. // title: '昵称包含敏感词,请重新输入',
  117. // icon: 'none'
  118. // });
  119. // }
  120. // },
  121. // 选择头像并上传到OSS
  122. async onChooseAvatar(e) {
  123. console.log('选择头像回调', e);
  124. if (e.detail.avatarUrl) {
  125. try {
  126. // 显示上传中提示
  127. uni.showLoading({ title: '上传头像中...' });
  128. // 构造文件对象
  129. const file = {
  130. path: e.detail.avatarUrl,
  131. tempFilePath: e.detail.avatarUrl
  132. };
  133. // 上传到OSS
  134. const uploadResult = await this.$utils.uploadImage(file);
  135. uni.hideLoading();
  136. if (uploadResult.success) {
  137. // 上传成功,更新头像URL
  138. this.userInfo.avatar = uploadResult.url;
  139. console.log('头像上传成功', uploadResult.url);
  140. uni.showToast({
  141. title: '头像上传成功',
  142. icon: 'success'
  143. });
  144. } else {
  145. }
  146. } catch (error) {
  147. uni.hideLoading();
  148. console.error('头像上传异常:', error);
  149. // 异常情况下使用本地头像
  150. this.userInfo.avatar = e.detail.avatarUrl;
  151. uni.showToast({
  152. title: '头像处理异常,使用本地头像',
  153. icon: 'none'
  154. });
  155. }
  156. } else {
  157. uni.showToast({
  158. title: '头像选择失败',
  159. icon: 'none'
  160. });
  161. }
  162. },
  163. // 保存资料
  164. async saveProfile() {
  165. if (!this.userInfo.name.trim()) {
  166. uni.showToast({
  167. title: '请输入昵称',
  168. icon: 'none'
  169. })
  170. return
  171. }
  172. if (!this.userInfo.phone.trim()) {
  173. uni.showToast({
  174. title: '请输入手机号',
  175. icon: 'none'
  176. })
  177. return
  178. }
  179. // 简单的手机号验证
  180. const phoneReg = /^1[3-9]\d{9}$/
  181. if (!phoneReg.test(this.userInfo.phone)) {
  182. uni.showToast({
  183. title: '请输入正确的手机号',
  184. icon: 'none'
  185. })
  186. return
  187. }
  188. // TODO: 调用API保存用户信息
  189. const res = await this.$api.login.updateUserInfo({
  190. avatar: this.userInfo.avatar,
  191. name: this.userInfo.name,
  192. phone: this.userInfo.phone
  193. })
  194. if (res.code === 200) {
  195. uni.showToast({
  196. title: '保存成功',
  197. icon: 'success'
  198. })
  199. // 延迟返回上一页
  200. setTimeout(() => {
  201. uni.navigateBack()
  202. }, 1500)
  203. }
  204. },
  205. // 获取个人信息
  206. async getProfile() {
  207. const res = await this.$api.login.getUserInfo()
  208. if (res.code === 200) {
  209. this.userInfo = res.result
  210. this.$store.dispatch('updateUserInfo', this.userInfo)
  211. }
  212. }
  213. },
  214. onLoad() {
  215. this.getProfile()
  216. // 3秒后隐藏遮罩层
  217. setTimeout(() => {
  218. this.showMask = false
  219. }, 3000)
  220. }
  221. }
  222. </script>
  223. <style lang="scss" scoped>
  224. .profile-container {
  225. min-height: 100vh;
  226. background-color: #f5f5f5;
  227. padding: 40rpx 32rpx 200rpx;
  228. .form-container {
  229. background: #fff;
  230. border-radius: 20rpx;
  231. padding: 40rpx 32rpx;
  232. .form-title {
  233. font-size: 36rpx;
  234. font-weight: 600;
  235. color: #333;
  236. margin-bottom: 60rpx;
  237. }
  238. .form-item {
  239. margin-bottom: 60rpx;
  240. &:last-child {
  241. margin-bottom: 0;
  242. }
  243. .label {
  244. display: flex;
  245. align-items: center;
  246. margin-bottom: 20rpx;
  247. font-size: 28rpx;
  248. color: #333;
  249. .required {
  250. color: #ff4757;
  251. margin-right: 8rpx;
  252. }
  253. }
  254. }
  255. .avatar-container {
  256. position: relative;
  257. width: 200rpx;
  258. height: 200rpx;
  259. border-radius: 16rpx;
  260. overflow: hidden;
  261. .avatar-image {
  262. width: 100%;
  263. height: 100%;
  264. background: #00000080;
  265. display: flex;
  266. align-items: center;
  267. justify-content: center;
  268. }
  269. .avatar-mask {
  270. position: absolute;
  271. top: 0;
  272. left: 0;
  273. right: 0;
  274. bottom: 0;
  275. background: rgba(0, 0, 0, 0.6);
  276. display: flex;
  277. align-items: center;
  278. justify-content: center;
  279. color: #fff;
  280. font-size: 24rpx;
  281. transition: opacity 1s ease-out;
  282. &.fade-out {
  283. opacity: 0;
  284. }
  285. }
  286. }
  287. }
  288. .save-button-container {
  289. position: fixed;
  290. bottom: 60rpx;
  291. left: 50%;
  292. transform: translateX(-50%);
  293. z-index: 999;
  294. }
  295. }
  296. </style>