hookehuyr

feat(PosterPage): 添加海报图片上传至七牛云功能

实现图片上传至七牛云的功能,包括获取文件哈希、获取上传token、处理已存在文件和新文件上传逻辑
添加上传状态提示和错误处理
1 <!-- 1 <!--
2 * @Date: 2025-12-23 15:50:59 2 * @Date: 2025-12-23 15:50:59
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-12-23 17:02:26 4 + * @LastEditTime: 2025-12-23 17:07:47
5 * @FilePath: /mlaj/src/views/recall/PosterPage.vue 5 * @FilePath: /mlaj/src/views/recall/PosterPage.vue
6 * @Description: 分享海报页面 6 * @Description: 分享海报页面
7 --> 7 -->
...@@ -37,9 +37,14 @@ import { ref } from 'vue' ...@@ -37,9 +37,14 @@ import { ref } from 'vue'
37 import { useRoute, useRouter } from 'vue-router' 37 import { useRoute, useRouter } from 'vue-router'
38 import { useTitle } from '@vueuse/core' 38 import { useTitle } from '@vueuse/core'
39 import RecallPoster from '@/components/ui/RecallPoster.vue' 39 import RecallPoster from '@/components/ui/RecallPoster.vue'
40 +import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common'
41 +import { showToast, showLoadingToast } from 'vant'
42 +import { qiniuFileHash } from '@/utils/qiniuFileHash'
43 +import { useAuth } from '@/contexts/auth'
40 44
41 const $route = useRoute(); 45 const $route = useRoute();
42 const $router = useRouter(); 46 const $router = useRouter();
47 +const { currentUser } = useAuth();
43 useTitle('分享海报'); 48 useTitle('分享海报');
44 49
45 // Assets 50 // Assets
...@@ -51,10 +56,92 @@ const qrCodeUrl = 'https://cdn.ipadbiz.cn/mlaj/recall/poster/%E4%BA%8C%E7%BB%B4% ...@@ -51,10 +56,92 @@ const qrCodeUrl = 'https://cdn.ipadbiz.cn/mlaj/recall/poster/%E4%BA%8C%E7%BB%B4%
51 const posterBg = ref(defaultBg) 56 const posterBg = ref(defaultBg)
52 const title = ref('2017.7.6-7.11 【自然的恩典】“爱我中华”优秀传统文化夏令营-天津场开启~') 57 const title = ref('2017.7.6-7.11 【自然的恩典】“爱我中华”优秀传统文化夏令营-天津场开启~')
53 58
59 +/**
60 + * 获取文件哈希(与七牛云ETag一致)
61 + */
62 +const getFileMD5 = async (file) => {
63 + return await qiniuFileHash(file)
64 +}
65 +
66 +// 上传到七牛云
67 +const uploadToQiniu = async (file, token, fileName) => {
68 + const formData = new FormData()
69 + formData.append('file', file)
70 + formData.append('token', token)
71 + formData.append('key', fileName)
72 +
73 + const config = {
74 + headers: { 'Content-Type': 'multipart/form-data' }
75 + }
76 +
77 + // 根据协议选择上传地址
78 + const qiniuUploadUrl = window.location.protocol === 'https:'
79 + ? 'https://up.qbox.me'
80 + : 'http://upload.qiniu.com'
81 +
82 + return await qiniuUploadAPI(qiniuUploadUrl, formData, config)
83 +}
84 +
54 // Actions 85 // Actions
55 -const afterRead = (file) => { 86 +const afterRead = async (file) => {
56 - // Use Object URL for local preview to avoid uploading 87 + const toast = showLoadingToast({
57 - posterBg.value = URL.createObjectURL(file.file) 88 + message: '上传中...',
89 + forbidClick: true,
90 + duration: 0 // 持续展示
91 + });
92 +
93 + try {
94 + // 获取MD5值
95 + const md5 = await getFileMD5(file.file)
96 +
97 + // 获取七牛token
98 + const tokenResult = await qiniuTokenAPI({
99 + name: file.file.name,
100 + hash: md5
101 + })
102 +
103 + // 文件已存在,直接使用
104 + if (tokenResult.data) {
105 + posterBg.value = tokenResult.data.src;
106 + showToast('图片上传成功');
107 + return;
108 + }
109 +
110 + // 新文件上传
111 + if (tokenResult.token) {
112 + const suffix = /.[^.]+$/.exec(file.file.name) || ''
113 + // 使用 recall/poster 路径
114 + const mobile = currentUser.value?.mobile || 'guest'
115 + const fileName = `mlaj/upload/recall/poster/${mobile}/${md5}${suffix}`
116 +
117 + const { filekey, image_info } = await uploadToQiniu(
118 + file.file,
119 + tokenResult.token,
120 + fileName
121 + )
122 +
123 + if (filekey) {
124 + // 保存文件信息
125 + const { data } = await saveFileAPI({
126 + name: file.file.name,
127 + filekey,
128 + hash: md5,
129 + height: image_info?.height,
130 + width: image_info?.width
131 + })
132 +
133 + if (data) {
134 + posterBg.value = data.src;
135 + showToast('图片上传成功');
136 + }
137 + }
138 + }
139 + } catch (error) {
140 + console.error('图片上传失败:', error)
141 + showToast('图片上传失败,请重试')
142 + } finally {
143 + toast.close();
144 + }
58 } 145 }
59 </script> 146 </script>
60 147
......