hookehuyr

feat(头像设置): 实现七牛云头像上传功能并添加MD5校验

- 新增七牛云上传相关API调用和MD5文件校验功能
- 优化头像上传流程,先检查文件是否已存在
- 使用vant组件显示上传状态提示
- 根据协议自动选择七牛云上传地址
- 添加文件信息保存逻辑
1 <!-- 1 <!--
2 * @Date: 2025-03-24 13:04:21 2 * @Date: 2025-03-24 13:04:21
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-03-26 13:27:45 4 + * @LastEditTime: 2025-06-06 11:22:30
5 * @FilePath: /mlaj/src/views/profile/settings/AvatarSettingPage.vue 5 * @FilePath: /mlaj/src/views/profile/settings/AvatarSettingPage.vue
6 * @Description: 修改头像页面 6 * @Description: 修改头像页面
7 --> 7 -->
...@@ -55,10 +55,14 @@ import { ref, onMounted } from "vue"; ...@@ -55,10 +55,14 @@ import { ref, onMounted } from "vue";
55 import AppLayout from "@/components/layout/AppLayout.vue"; 55 import AppLayout from "@/components/layout/AppLayout.vue";
56 import FrostedGlass from "@/components/ui/FrostedGlass.vue"; 56 import FrostedGlass from "@/components/ui/FrostedGlass.vue";
57 import { getUserInfoAPI, updateUserInfoAPI } from "@/api/users"; 57 import { getUserInfoAPI, updateUserInfoAPI } from "@/api/users";
58 - 58 +import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common';
59 +import { showToast, showLoadingToast } from 'vant';
60 +import BMF from 'browser-md5-file';
59 import { useTitle } from '@vueuse/core'; 61 import { useTitle } from '@vueuse/core';
62 +import { useAuth } from '@/contexts/auth';
60 63
61 const $route = useRoute(); 64 const $route = useRoute();
65 +const { currentUser } = useAuth();
62 useTitle($route.meta.title); 66 useTitle($route.meta.title);
63 67
64 68
...@@ -77,20 +81,99 @@ onMounted(async () => { ...@@ -77,20 +81,99 @@ onMounted(async () => {
77 } 81 }
78 }); 82 });
79 83
84 +// 获取文件MD5
85 +const getFileMD5 = (file) => {
86 + return new Promise((resolve, reject) => {
87 + const bmf = new BMF()
88 + bmf.md5(file, (err, md5) => {
89 + if (err) {
90 + reject(err)
91 + return
92 + }
93 + resolve(md5)
94 + })
95 + })
96 +}
97 +
98 +// 上传到七牛云
99 +const uploadToQiniu = async (file, token, fileName) => {
100 + const formData = new FormData()
101 + formData.append('file', file)
102 + formData.append('token', token)
103 + formData.append('key', fileName)
104 +
105 + const config = {
106 + headers: { 'Content-Type': 'multipart/form-data' }
107 + }
108 +
109 + // 根据协议选择上传地址
110 + const qiniuUploadUrl = window.location.protocol === 'https:'
111 + ? 'https://up.qbox.me'
112 + : 'http://upload.qiniu.com'
113 +
114 + return await qiniuUploadAPI(qiniuUploadUrl, formData, config)
115 +}
116 +
80 // 处理头像上传 117 // 处理头像上传
81 const handleAvatarChange = async (file) => { 118 const handleAvatarChange = async (file) => {
119 + // const toast = showLoadingToast({
120 + // message: '上传中...',
121 + // forbidClick: true,
122 + // });
123 +
82 try { 124 try {
83 - const formData = new FormData(); 125 + // 获取MD5值
84 - formData.append("avatar", file); 126 + const md5 = await getFileMD5(file.file)
85 127
86 - const response = await updateUserInfoAPI(formData); 128 + // 获取七牛token
87 - if (response.data) { 129 + const tokenResult = await qiniuTokenAPI({
88 - userAvatar.value = response.data.url; 130 + name: file.file.name,
89 - alert("头像上传成功"); 131 + hash: md5
132 + })
133 +
134 + // 文件已存在,直接使用
135 + if (tokenResult.data) {
136 + userAvatar.value = tokenResult.data.src;
137 + // 更新用户信息
138 + await updateUserInfoAPI({ avatar: tokenResult.data.src });
139 + showToast('头像上传成功');
140 + return;
141 + }
142 +
143 + // 新文件上传
144 + if (tokenResult.token) {
145 + const suffix = /.[^.]+$/.exec(file.file.name) || ''
146 + const fileName = `mlaj/upload/avatar/${currentUser.value.mobile}/${md5}${suffix}`
147 +
148 + const { filekey, image_info } = await uploadToQiniu(
149 + file.file,
150 + tokenResult.token,
151 + fileName
152 + )
153 +
154 + if (filekey) {
155 + // 保存文件信息
156 + const { data } = await saveFileAPI({
157 + name: file.file.name,
158 + filekey,
159 + hash: md5,
160 + height: image_info?.height,
161 + width: image_info?.width
162 + })
163 +
164 + if (data) {
165 + userAvatar.value = data.src;
166 + // 更新用户信息
167 + await updateUserInfoAPI({ avatar: data.src });
168 + showToast('头像上传成功');
169 + }
170 + }
90 } 171 }
91 } catch (error) { 172 } catch (error) {
92 - console.error("头像上传失败:", error); 173 + console.error('头像上传失败:', error)
93 - alert("头像上传失败,请重试"); 174 + showToast('头像上传失败,请重试')
175 + } finally {
176 + // toast.close();
94 } 177 }
95 }; 178 };
96 </script> 179 </script>
......