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

429 lines
9.8 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. <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. }
  88. },
  89. onLoad() {
  90. // 获取微信用户信息
  91. this.getWechatUserInfo();
  92. },
  93. methods: {
  94. // 获取微信用户信息
  95. async getWechatUserInfo() {
  96. const { result } = await this.$api.user.queryUser()
  97. this.userInfo.nickName = result.nickName
  98. this.userInfo.headImage = result.headImage
  99. this.userInfo.phone = result.phone
  100. },
  101. // 提交表单
  102. // 选择头像并上传到OSS
  103. async onChooseAvatar(e) {
  104. console.log('选择头像回调', e);
  105. if (e.detail.avatarUrl) {
  106. try {
  107. // 显示上传中提示
  108. uni.showLoading({ title: '上传头像中...' });
  109. // 构造文件对象
  110. const file = {
  111. path: e.detail.avatarUrl,
  112. tempFilePath: e.detail.avatarUrl
  113. };
  114. // 上传到OSS
  115. const uploadResult = await this.$utils.uploadImage(file);
  116. uni.hideLoading();
  117. if (uploadResult.success) {
  118. // 上传成功,更新头像URL
  119. this.userInfo.headImage = uploadResult.url;
  120. console.log('头像上传成功', uploadResult.url);
  121. uni.showToast({
  122. title: '头像上传成功',
  123. icon: 'success'
  124. });
  125. } else {
  126. // 上传失败,使用本地头像
  127. // this.userInfo.headImage = e.detail.avatarUrl;
  128. uni.showToast({
  129. title: '头像上传失败!请稍后重试!',
  130. icon: 'none'
  131. });
  132. }
  133. } catch (error) {
  134. uni.hideLoading();
  135. console.error('头像上传异常:', error);
  136. // 异常情况下使用本地头像
  137. this.userInfo.headImage = e.detail.avatarUrl;
  138. uni.showToast({
  139. title: '头像处理异常,使用本地头像',
  140. icon: 'none'
  141. });
  142. }
  143. } else {
  144. uni.showToast({
  145. title: '头像选择失败',
  146. icon: 'none'
  147. });
  148. }
  149. },
  150. // 昵称输入失焦
  151. onNicknameBlur() {
  152. if (!this.userInfo.nickname.trim()) {
  153. uni.showToast({
  154. title: '请输入昵称',
  155. icon: 'none'
  156. });
  157. }
  158. },
  159. // 获取手机号
  160. async getPhoneNumber(e) {
  161. console.log('获取手机号回调', e);
  162. if (e.detail.errMsg === 'getPhoneNumber:ok') {
  163. // 获取成功,可以通过e.detail.code发送到后端换取手机号
  164. console.log('获取手机号成功', e.detail);
  165. const res = await this.$api.login.bindPhone({
  166. phoneCode: e.detail.code
  167. })
  168. const str = JSON.parse(res.result);
  169. this.userInfo.phone = str.phone_info.phoneNumber
  170. uni.showToast({
  171. title: '手机号获取成功',
  172. icon: 'success'
  173. });
  174. // 这里需要将e.detail.code发送到后端解密获取真实手机号
  175. // 暂时模拟设置手机号
  176. // this.userInfo.phone = '138****8888';
  177. } else {
  178. // 如果失败了就申请开启权限
  179. uni.showToast({
  180. title: '手机号获取失败',
  181. icon: 'error'
  182. })
  183. }
  184. },
  185. // 提交用户信息
  186. async submitUserInfo() {
  187. if (!this.userInfo.nickName.trim()) {
  188. uni.showToast({
  189. title: '请输入昵称',
  190. icon: 'none'
  191. });
  192. return;
  193. }
  194. if (!this.userInfo.name.trim()) {
  195. uni.showToast({
  196. title: '请输入真实姓名',
  197. icon: 'none'
  198. });
  199. return;
  200. }
  201. if (!this.userInfo.area.trim()) {
  202. uni.showToast({
  203. title: '请输入所在宿集区',
  204. icon: 'none'
  205. });
  206. return;
  207. }
  208. if (!this.userInfo.phone.trim()) {
  209. uni.showToast({
  210. title: '请输入手机号',
  211. icon: 'none'
  212. });
  213. return;
  214. }
  215. if (!/^1[3-9]\d{9}$/.test(this.userInfo.phone)) {
  216. uni.showToast({
  217. title: '请输入正确的手机号',
  218. icon: 'none'
  219. });
  220. return;
  221. }
  222. console.log('提交用户信息', this.userInfo);
  223. // 提交用户信息
  224. await this.$api.user.updateUser({
  225. nickName: this.userInfo.nickName,
  226. phone: this.userInfo.phone,
  227. headImage: this.userInfo.headImage,
  228. address: ''
  229. })
  230. // 这里可以调用API保存用户信息
  231. uni.showToast({
  232. title: '信息保存成功',
  233. icon: 'success'
  234. });
  235. // 跳转到首页或其他页面
  236. setTimeout(() => {
  237. uni.switchTab({
  238. url: '/pages/index/index'
  239. });
  240. }, 1000);
  241. }
  242. }
  243. }
  244. </script>
  245. <style lang="scss" scoped>
  246. .user-info-container {
  247. min-height: 100vh;
  248. background-color: #f5f5f5;
  249. }
  250. .custom-navbar {
  251. position: fixed;
  252. top: 0;
  253. left: 0;
  254. right: 0;
  255. z-index: 1000;
  256. background-color: #1488DB;
  257. .navbar-content {
  258. height: 88rpx;
  259. display: flex;
  260. align-items: center;
  261. justify-content: center;
  262. padding-top: var(--status-bar-height, 44rpx);
  263. .navbar-title {
  264. font-size: 36rpx;
  265. font-weight: 500;
  266. color: #ffffff;
  267. }
  268. }
  269. }
  270. .content {
  271. padding-top: calc(88rpx + var(--status-bar-height, 44rpx));
  272. padding: calc(88rpx + var(--status-bar-height, 44rpx)) 40rpx 40rpx;
  273. }
  274. .avatar-section {
  275. display: flex;
  276. justify-content: center;
  277. margin-bottom: 80rpx;
  278. .app-info {
  279. display: flex;
  280. flex-direction: column;
  281. align-items: center;
  282. .app-logo {
  283. width: 160rpx;
  284. height: 160rpx;
  285. border-radius: 20rpx;
  286. border: 4rpx dashed #cccccc;
  287. margin-bottom: 20rpx;
  288. }
  289. .app-name {
  290. font-size: 32rpx;
  291. font-weight: 500;
  292. color: #333333;
  293. }
  294. }
  295. }
  296. .form-section {
  297. background-color: #ffffff;
  298. border-radius: 20rpx;
  299. padding: 40rpx;
  300. margin-bottom: 60rpx;
  301. }
  302. .form-item {
  303. display: flex;
  304. align-items: center;
  305. margin-bottom: 40rpx;
  306. &:last-child {
  307. margin-bottom: 0;
  308. }
  309. .form-label {
  310. width: 120rpx;
  311. font-size: 32rpx;
  312. color: #333333;
  313. font-weight: 500;
  314. }
  315. .form-input {
  316. flex: 3;
  317. height: 80rpx;
  318. padding: 0 20rpx;
  319. // border: 2rpx solid #e0e0e0;
  320. border-radius: 10rpx;
  321. font-size: 30rpx;
  322. color: #333333;
  323. &.phone-input {
  324. margin-right: 20rpx;
  325. }
  326. }
  327. .avatar-upload {
  328. .avatar-image {
  329. width: 120rpx;
  330. height: 120rpx;
  331. border-radius: 10rpx;
  332. border: 2rpx dashed #cccccc;
  333. }
  334. }
  335. .phone-input-container {
  336. flex: 1;
  337. display: flex;
  338. align-items: center;
  339. .get-phone-btn {
  340. flex: 2;
  341. // padding: 0rpx 0rpx;
  342. background-color: #1488DB;
  343. border-radius: 40rpx;
  344. border: none;
  345. outline: none;
  346. &::after {
  347. border: none;
  348. }
  349. .btn-text {
  350. font-size: 26rpx;
  351. color: #ffffff;
  352. }
  353. }
  354. }
  355. }
  356. .submit-section {
  357. padding: 0 40rpx;
  358. .submit-btn {
  359. width: 100%;
  360. height: 88rpx;
  361. background-color: #1488DB;
  362. border-radius: 44rpx;
  363. display: flex;
  364. align-items: center;
  365. justify-content: center;
  366. .submit-text {
  367. font-size: 32rpx;
  368. font-weight: 500;
  369. color: #ffffff;
  370. }
  371. }
  372. }
  373. // 添加按钮样式
  374. .avatar-button {
  375. padding: 0;
  376. margin: 0;
  377. background: transparent;
  378. border: none;
  379. outline: none;
  380. &::after {
  381. border: none;
  382. }
  383. }
  384. </style>