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

256 lines
6.0 KiB

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