图书馆小程序
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.

267 lines
6.4 KiB

5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
4 months ago
4 months ago
4 months ago
3 weeks ago
4 months ago
3 weeks ago
4 weeks ago
4 months ago
4 months ago
5 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 weeks ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 weeks ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
5 months ago
4 months ago
5 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
  1. <template>
  2. <view class="user-info-page">
  3. <view v-if="pageReady">
  4. <view class="user-info-section">
  5. <!-- 点击获取头像 -->
  6. <button open-type="chooseAvatar" @chooseavatar="onChooseAvatar" class="avatar-btn">
  7. <image v-show="avatarUrl" :src="avatarUrl" class="avatar-img"></image>
  8. <image v-show="!avatarUrl" src="@/static/images/logo.jpg" class="avatar-img"></image>
  9. <text class="tip-text">点击更换头像</text>
  10. </button>
  11. </view>
  12. <!-- 昵称 -->
  13. <view class="form-item">
  14. <text class="label">昵称</text>
  15. <input
  16. class="input"
  17. type="nickname"
  18. v-model="nickname"
  19. placeholder="请输入昵称"
  20. />
  21. <uni-icons type="right" size="18" color="#ccc"></uni-icons>
  22. </view>
  23. <!-- 保存按钮 -->
  24. <view class="save-box">
  25. <button class="save-btn" @click="saveUserInfo">保存修改</button>
  26. </view>
  27. </view>
  28. </view>
  29. </template>
  30. <script>
  31. import config from '@/utils/config'
  32. import { FetchBindRead } from '@/api/user';
  33. const USER_KEY = 'user-info';
  34. export default {
  35. data() {
  36. return {
  37. pageReady: false,
  38. nickname: '',
  39. avatarUrl: '', // 仅存放 base64,用于显示
  40. imgId: '', // 仅用于提交后端
  41. saving: false // 防重复提交
  42. };
  43. },
  44. onReady() {
  45. //等页面完全渲染完成,再渲染chooseAvatar按钮
  46. this.pageReady = true;
  47. this.loadUserInfo();
  48. },
  49. methods:{
  50. // 加载用户信息
  51. async loadUserInfo() {
  52. const userInfo = uni.getStorageSync(USER_KEY) || {};
  53. this.nickname = userInfo.nickname || '';
  54. const avatarId = userInfo.avatarId || '';
  55. console.log(userInfo)
  56. if (avatarId) {
  57. try {
  58. uni.showLoading({ title: '加载头像中...' });
  59. this.imgId = avatarId;
  60. const url = `${config.baseUrl}/files/${avatarId}`;
  61. console.log(url)
  62. this.avatarUrl = url
  63. } catch (err) {
  64. console.error('头像加载失败', err);
  65. this.avatarUrl = '';
  66. } finally {
  67. uni.hideLoading();
  68. }
  69. } else {
  70. this.avatarUrl = '';
  71. this.imgId = '';
  72. }
  73. },
  74. // 选择头像 + 上传 + 转base64预览
  75. async onChooseAvatar(e) {
  76. const tempFilePath = e.detail.avatarUrl;
  77. uni.showLoading({ title: '上传中...' });
  78. try {
  79. // 上传头像
  80. const uploadRes = await new Promise((resolve, reject) => {
  81. uni.uploadFile({
  82. // url: config.baseUrl + '/api/fileRelevant/uploadWxAvatarImg',
  83. url: config.baseUrl + '/api/fileRelevant/uploadHttpsImg',
  84. filePath: tempFilePath,
  85. name: 'file',
  86. success: resolve,
  87. fail: reject
  88. });
  89. });
  90. const resData = JSON.parse(uploadRes.data);
  91. const imgId = resData.data;
  92. this.imgId = imgId;
  93. // 转base64显示
  94. // const url = `${config.baseUrl}/api/fileRelevant/getImg?imgType=5&imgId=${imgId}`;
  95. const url = `${config.baseUrl}/files/${imgId}`;
  96. // this.avatarUrl = await this.urlToBase64(url);
  97. this.avatarUrl = url
  98. uni.showToast({ title: '头像上传成功', icon: 'success' });
  99. } catch (err) {
  100. console.error(err);
  101. uni.showToast({ title: '头像上传失败', icon: 'none' });
  102. } finally {
  103. uni.hideLoading();
  104. }
  105. },
  106. // 最简稳定版:图片链接转base64
  107. urlToBase64(url) {
  108. return new Promise((resolve, reject) => {
  109. uni.request({
  110. url,
  111. method: 'GET',
  112. responseType: 'arraybuffer',
  113. success: (res) => {
  114. try {
  115. const base64 = uni.arrayBufferToBase64(res.data);
  116. resolve(`data:image/jpeg;base64,${base64}`);
  117. } catch (e) {
  118. reject(e);
  119. }
  120. },
  121. fail: reject
  122. });
  123. });
  124. },
  125. // 保存(防重复提交 + 同步缓存)
  126. async saveUserInfo() {
  127. if (this.saving) return;
  128. if (!this.nickname.trim()) {
  129. uni.showToast({ title: '请输入昵称', icon: 'none' });
  130. return;
  131. }
  132. this.saving = true;
  133. uni.showLoading({ title: '保存中...' });
  134. try {
  135. const openId = uni.getStorageSync('wx_login_code') || '';
  136. const params = {
  137. avatar: this.imgId,
  138. libcode: config.LIB_CODE,
  139. nickname: this.nickname,
  140. openid: openId
  141. };
  142. const res = await FetchBindRead(params);
  143. console.log('FetchBindRead',res)
  144. if(res.code !== 200){
  145. uni.showToast({title: res.message || '保存失败',icon:'none'})
  146. return
  147. }
  148. // 保存到缓存
  149. uni.setStorageSync(USER_KEY, {
  150. nickname: this.nickname,
  151. avatarId: this.imgId
  152. });
  153. uni.showToast({ title: '保存成功', icon: 'success' });
  154. setTimeout(() => {
  155. uni.navigateBack();
  156. }, 1500);
  157. } catch (error) {
  158. console.error(error);
  159. uni.showToast({ title: '保存失败', icon: 'none' });
  160. } finally {
  161. uni.hideLoading();
  162. this.saving = false;
  163. }
  164. }
  165. }
  166. };
  167. </script>
  168. <style lang="scss" scoped>
  169. .user-info-page {
  170. background-color: #f5f5f5;
  171. min-height: 100vh;
  172. padding: 20rpx;
  173. box-sizing: border-box;
  174. }
  175. .user-info-section {
  176. background: #fff;
  177. padding: 40rpx 0;
  178. border-radius: 12rpx;
  179. margin-bottom: 20rpx;
  180. display: flex;
  181. flex-direction: column;
  182. align-items: center;
  183. }
  184. .avatar-btn {
  185. background: transparent;
  186. pointer-events: auto !important;
  187. display: flex;
  188. flex-direction: column;
  189. align-items: center;
  190. &::after {
  191. border: none !important;
  192. }
  193. }
  194. .avatar-img {
  195. width: 120rpx;
  196. height: 120rpx;
  197. border-radius: 50%;
  198. object-fit: cover;
  199. margin-bottom: 10rpx;
  200. }
  201. .tip-text {
  202. font-size: 26rpx;
  203. color: #999;
  204. }
  205. .form-item {
  206. background-color: #fff;
  207. display: flex;
  208. justify-content: space-between;
  209. align-items: center;
  210. padding: 0 30rpx;
  211. height: 88rpx;
  212. border-radius: 12rpx;
  213. margin-bottom: 20rpx;
  214. }
  215. .label {
  216. font-size: 30rpx;
  217. color: #333;
  218. width: 120rpx;
  219. }
  220. .input {
  221. flex: 1;
  222. font-size: 30rpx;
  223. color: #333;
  224. text-align: right;
  225. }
  226. .save-box {
  227. margin-top: 60rpx;
  228. padding: 0 20rpx;
  229. }
  230. .save-btn {
  231. height: 88rpx;
  232. line-height: 88rpx;
  233. background-color: #007aff;
  234. color: #fff;
  235. border-radius: 44rpx;
  236. font-size: 32rpx;
  237. }
  238. </style>