木邻有你前端代码仓库
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.

446 lines
10 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
1 month ago
3 months ago
1 month ago
1 month ago
1 month ago
1 month ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
  1. <template>
  2. <view class="user-info-container">
  3. <!-- 内容区域 -->
  4. <view class="content">
  5. <!-- 头像区域 -->
  6. <view class="avatar-section">
  7. <view class="app-info">
  8. <image
  9. class="app-logo"
  10. :src="configParamImage('config_logo')"
  11. mode="aspectFit"
  12. ></image>
  13. <text class="app-name">{{ configParamText('config_app_name') }}</text>
  14. </view>
  15. </view>
  16. <!-- 表单区域 -->
  17. <view class="form-section">
  18. <!-- 头像 -->
  19. <!-- 在模板中修改头像区域 -->
  20. <view class="form-item">
  21. <text class="form-label">头像</text>
  22. <view class="avatar-upload">
  23. <button
  24. class="avatar-button"
  25. open-type="chooseAvatar"
  26. @chooseavatar="onChooseAvatar"
  27. >
  28. <image
  29. class="avatar-image"
  30. :src="userInfo.headImage || '/static/待上传头像.png'"
  31. mode="aspectFill"
  32. ></image>
  33. </button>
  34. </view>
  35. </view>
  36. <!-- 昵称 -->
  37. <view class="form-item">
  38. <text class="form-label">昵称</text>
  39. <input
  40. class="form-input"
  41. v-model="userInfo.nickName"
  42. placeholder="请输入昵称"
  43. type="nickname"
  44. @blur="onNicknameBlur"
  45. />
  46. </view>
  47. <!-- 手机号 -->
  48. <view class="form-item">
  49. <text class="form-label">手机号</text>
  50. <view class="phone-input-container">
  51. <input
  52. class="form-input phone-input"
  53. v-model="userInfo.phone"
  54. placeholder="请输入手机号"
  55. type="number"
  56. maxlength="11"
  57. />
  58. <button
  59. class="get-phone-btn"
  60. open-type="getPhoneNumber"
  61. @getphonenumber="getPhoneNumber"
  62. >
  63. <text class="btn-text">获取手机号</text>
  64. </button>
  65. </view>
  66. </view>
  67. </view>
  68. <!-- 确定按钮 -->
  69. <view class="submit-section">
  70. <view class="submit-btn" @click="submitUserInfo">
  71. <text class="submit-text">确定</text>
  72. </view>
  73. </view>
  74. </view>
  75. </view>
  76. </template>
  77. <script>
  78. export default {
  79. name: 'UserInfo',
  80. data() {
  81. return {
  82. userInfo: {
  83. headImage: '',
  84. nickName: '',
  85. phone: ''
  86. },
  87. isSubmitting: false // 防止重复提交
  88. }
  89. },
  90. onLoad() {
  91. // 获取微信用户信息
  92. this.getWechatUserInfo();
  93. },
  94. methods: {
  95. // 获取微信用户信息
  96. async getWechatUserInfo() {
  97. const { result } = await this.$api.user.queryUser()
  98. this.userInfo.nickName = result.nickName
  99. this.userInfo.headImage = result.headImage
  100. this.userInfo.phone = result.phone
  101. },
  102. // 提交表单
  103. // 选择头像并上传到OSS
  104. async onChooseAvatar(e) {
  105. console.log('选择头像回调', e);
  106. if (e.detail.avatarUrl) {
  107. try {
  108. // 显示上传中提示
  109. uni.showLoading({ title: '上传头像中...' });
  110. // 构造文件对象
  111. const file = {
  112. path: e.detail.avatarUrl,
  113. tempFilePath: e.detail.avatarUrl
  114. };
  115. // 上传到OSS
  116. const uploadResult = await this.$utils.uploadImage(file);
  117. uni.hideLoading();
  118. if (uploadResult.success) {
  119. // 上传成功,更新头像URL
  120. this.userInfo.headImage = uploadResult.url;
  121. console.log('头像上传成功', uploadResult.url);
  122. uni.showToast({
  123. title: '头像上传成功',
  124. icon: 'success'
  125. });
  126. } else {
  127. // 上传失败,使用本地头像
  128. // this.userInfo.headImage = e.detail.avatarUrl;
  129. uni.showToast({
  130. title: '头像上传失败!请稍后重试!',
  131. icon: 'none'
  132. });
  133. }
  134. } catch (error) {
  135. uni.hideLoading();
  136. console.error('头像上传异常:', error);
  137. // 异常情况下使用本地头像
  138. this.userInfo.headImage = e.detail.avatarUrl;
  139. uni.showToast({
  140. title: '头像处理异常,使用本地头像',
  141. icon: 'none'
  142. });
  143. }
  144. } else {
  145. uni.showToast({
  146. title: '头像选择失败',
  147. icon: 'none'
  148. });
  149. }
  150. },
  151. // 昵称输入失焦
  152. onNicknameBlur() {
  153. if (!this.userInfo.nickName.trim()) {
  154. uni.showToast({
  155. title: '请输入昵称',
  156. icon: 'none'
  157. });
  158. }
  159. },
  160. // 获取手机号
  161. async getPhoneNumber(e) {
  162. console.log('获取手机号回调', e);
  163. if (e.detail.errMsg === 'getPhoneNumber:ok') {
  164. // 获取成功,可以通过e.detail.code发送到后端换取手机号
  165. console.log('获取手机号成功', e.detail);
  166. const res = await this.$api.login.bindPhone({
  167. phoneCode: e.detail.code
  168. })
  169. const str = JSON.parse(res.result);
  170. this.userInfo.phone = str.phone_info.phoneNumber
  171. uni.showToast({
  172. title: '手机号获取成功',
  173. icon: 'success'
  174. });
  175. // 这里需要将e.detail.code发送到后端解密获取真实手机号
  176. // 暂时模拟设置手机号
  177. // this.userInfo.phone = '138****8888';
  178. } else {
  179. // 如果失败了就申请开启权限
  180. uni.showToast({
  181. title: '手机号获取失败',
  182. icon: 'error'
  183. })
  184. }
  185. },
  186. // 提交用户信息
  187. async submitUserInfo() {
  188. // 防止重复提交
  189. if (this.isSubmitting) {
  190. return;
  191. }
  192. if (!this.userInfo.nickName.trim()) {
  193. uni.showToast({
  194. title: '请输入昵称',
  195. icon: 'none'
  196. });
  197. return;
  198. }
  199. if (!this.userInfo.phone.trim()) {
  200. uni.showToast({
  201. title: '请输入手机号',
  202. icon: 'none'
  203. });
  204. return;
  205. }
  206. if (!/^1[3-9]\d{9}$/.test(this.userInfo.phone)) {
  207. uni.showToast({
  208. title: '请输入正确的手机号',
  209. icon: 'none'
  210. });
  211. return;
  212. }
  213. console.log('提交用户信息', this.userInfo);
  214. try {
  215. // 设置提交状态,防止重复点击
  216. this.isSubmitting = true;
  217. // 提交用户信息
  218. const res = await this.$api.user.updateUser({
  219. nickName: this.userInfo.nickName,
  220. phone: this.userInfo.phone,
  221. headImage: this.userInfo.headImage,
  222. address: ''
  223. });
  224. if (res.code === 200) {
  225. uni.showToast({
  226. title: '信息保存成功',
  227. icon: 'success'
  228. });
  229. // 跳转到首页或其他页面
  230. setTimeout(() => {
  231. uni.switchTab({
  232. url: '/pages/index/index'
  233. });
  234. }, 1000);
  235. } else {
  236. // 处理API返回的错误
  237. uni.showToast({
  238. title: res.message || '保存失败,请重试',
  239. icon: 'none'
  240. });
  241. }
  242. } catch (error) {
  243. console.error('提交用户信息失败:', error);
  244. uni.showToast({
  245. title: '网络错误,请检查网络连接',
  246. icon: 'none'
  247. });
  248. } finally {
  249. // 重置提交状态
  250. this.isSubmitting = false;
  251. }
  252. }
  253. }
  254. }
  255. </script>
  256. <style lang="scss" scoped>
  257. .user-info-container {
  258. min-height: 100vh;
  259. background-color: #f5f5f5;
  260. }
  261. .custom-navbar {
  262. position: fixed;
  263. top: 0;
  264. left: 0;
  265. right: 0;
  266. z-index: 1000;
  267. background-color: #1488DB;
  268. .navbar-content {
  269. height: 88rpx;
  270. display: flex;
  271. align-items: center;
  272. justify-content: center;
  273. padding-top: var(--status-bar-height, 44rpx);
  274. .navbar-title {
  275. font-size: 36rpx;
  276. font-weight: 500;
  277. color: #ffffff;
  278. }
  279. }
  280. }
  281. .content {
  282. padding-top: calc(88rpx + var(--status-bar-height, 44rpx));
  283. padding: calc(88rpx + var(--status-bar-height, 44rpx)) 40rpx 40rpx;
  284. }
  285. .avatar-section {
  286. display: flex;
  287. justify-content: center;
  288. margin-bottom: 80rpx;
  289. .app-info {
  290. display: flex;
  291. flex-direction: column;
  292. align-items: center;
  293. .app-logo {
  294. width: 160rpx;
  295. height: 160rpx;
  296. border-radius: 20rpx;
  297. border: 4rpx dashed #cccccc;
  298. margin-bottom: 20rpx;
  299. }
  300. .app-name {
  301. font-size: 32rpx;
  302. font-weight: 500;
  303. color: #333333;
  304. }
  305. }
  306. }
  307. .form-section {
  308. background-color: #ffffff;
  309. border-radius: 20rpx;
  310. padding: 40rpx;
  311. margin-bottom: 60rpx;
  312. }
  313. .form-item {
  314. display: flex;
  315. align-items: center;
  316. margin-bottom: 40rpx;
  317. &:last-child {
  318. margin-bottom: 0;
  319. }
  320. .form-label {
  321. width: 120rpx;
  322. font-size: 32rpx;
  323. color: #333333;
  324. font-weight: 500;
  325. }
  326. .form-input {
  327. flex: 3;
  328. height: 80rpx;
  329. padding: 0 20rpx;
  330. // border: 2rpx solid #e0e0e0;
  331. border-radius: 10rpx;
  332. font-size: 30rpx;
  333. color: #333333;
  334. &.phone-input {
  335. margin-right: 20rpx;
  336. }
  337. }
  338. .avatar-upload {
  339. .avatar-image {
  340. width: 120rpx;
  341. height: 120rpx;
  342. border-radius: 10rpx;
  343. border: 2rpx dashed #cccccc;
  344. }
  345. }
  346. .phone-input-container {
  347. flex: 1;
  348. display: flex;
  349. align-items: center;
  350. .get-phone-btn {
  351. flex: 2;
  352. // padding: 0rpx 0rpx;
  353. background-color: #1488DB;
  354. border-radius: 40rpx;
  355. border: none;
  356. outline: none;
  357. height: 80rpx;
  358. line-height: 70rpx;
  359. &::after {
  360. border: none;
  361. }
  362. .btn-text {
  363. font-size: 26rpx;
  364. color: #ffffff;
  365. }
  366. }
  367. }
  368. }
  369. .submit-section {
  370. padding: 0 40rpx;
  371. .submit-btn {
  372. width: 100%;
  373. height: 88rpx;
  374. background-color: #1488DB;
  375. border-radius: 44rpx;
  376. display: flex;
  377. align-items: center;
  378. justify-content: center;
  379. .submit-text {
  380. font-size: 32rpx;
  381. font-weight: 500;
  382. color: #ffffff;
  383. }
  384. }
  385. }
  386. // 添加按钮样式
  387. .avatar-button {
  388. padding: 0;
  389. margin: 0;
  390. background: transparent;
  391. border: none;
  392. outline: none;
  393. &::after {
  394. border: none;
  395. }
  396. }
  397. </style>