hookehuyr

refactor: 重构文件操作逻辑,统一使用 useFileOperation

- 删除收藏页和产品详情页中重复的文件操作代码(~290行)
- 统一使用 useFileOperation composable
- 简化 onDownload/onView 函数为 viewFile 调用
- 保持功能完全一致

影响文件:
- src/pages/favorites/index.vue
- src/pages/product-detail/index.vue

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
42 <!-- Action Buttons --> 42 <!-- Action Buttons -->
43 <view class="flex flex-col items-end gap-[16rpx] ml-[16rpx] flex-shrink-0"> 43 <view class="flex flex-col items-end gap-[16rpx] ml-[16rpx] flex-shrink-0">
44 <!-- View Button --> 44 <!-- View Button -->
45 - <view class="flex items-center justify-center px-[20rpx] py-[10rpx] bg-blue-50 rounded-full active:bg-blue-100 transition-colors" @tap.stop="onView(item)"> 45 + <view class="flex items-center justify-center px-[20rpx] py-[10rpx] bg-blue-50 rounded-full active:bg-blue-100 transition-colors" @tap.stop="viewFile({...item, fileName: item.title})">
46 <IconFont name="eye" size="14" color="#2563EB" class="mr-[6rpx]" /> 46 <IconFont name="eye" size="14" color="#2563EB" class="mr-[6rpx]" />
47 <text class="text-[24rpx] text-blue-600 font-medium leading-none">查看</text> 47 <text class="text-[24rpx] text-blue-600 font-medium leading-none">查看</text>
48 </view> 48 </view>
...@@ -70,13 +70,15 @@ ...@@ -70,13 +70,15 @@
70 70
71 <script setup> 71 <script setup>
72 import { ref, computed } from 'vue' 72 import { ref, computed } from 'vue'
73 +import Taro from '@tarojs/taro'
73 import { useGo } from '@/hooks/useGo' 74 import { useGo } from '@/hooks/useGo'
75 +import { useFileOperation } from '@/composables/useFileOperation'
74 import IconFont from '@/components/IconFont.vue' 76 import IconFont from '@/components/IconFont.vue'
75 import TabBar from '@/components/TabBar.vue' 77 import TabBar from '@/components/TabBar.vue'
76 import NavHeader from '@/components/NavHeader.vue' 78 import NavHeader from '@/components/NavHeader.vue'
77 -import Taro from '@tarojs/taro'
78 79
79 const go = useGo() 80 const go = useGo()
81 +const { viewFile } = useFileOperation()
80 const activeTab = ref('all') 82 const activeTab = ref('all')
81 83
82 const tabs = [ 84 const tabs = [
...@@ -141,88 +143,6 @@ const filteredList = computed(() => { ...@@ -141,88 +143,6 @@ const filteredList = computed(() => {
141 return list.value.filter(item => item.type === activeTab.value) 143 return list.value.filter(item => item.type === activeTab.value)
142 }) 144 })
143 145
144 -const onView = (item) => {
145 - // 检查是否有下载地址
146 - if (!item.downloadUrl) {
147 - Taro.showToast({
148 - title: '该文件暂无查看地址',
149 - icon: 'none',
150 - duration: 2000
151 - })
152 - return
153 - }
154 -
155 - // 显示加载提示
156 - Taro.showLoading({
157 - title: '打开中...',
158 - mask: true
159 - })
160 -
161 - // 下载并打开文件
162 - downloadAndOpenFile(item)
163 -}
164 -
165 -// 打开文件的通用函数
166 -const openFile = async (filePath, item) => {
167 - try {
168 - await Taro.openDocument({
169 - filePath: filePath,
170 - showMenu: true, // 显示右上角菜单,用户可以转发、保存等
171 - success: () => {
172 - console.log('文件打开成功')
173 - // 文件打开后,延迟提示用户如果看不到内容该如何操作
174 - const fileExt = item.title.split('.').pop()?.toLowerCase() || ''
175 - const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls']
176 -
177 - if (unsupportedFormats.includes(fileExt)) {
178 - setTimeout(() => {
179 - Taro.showToast({
180 - title: '如无法预览,请使用右上角菜单分享',
181 - icon: 'none',
182 - duration: 3000
183 - })
184 - }, 1500)
185 - }
186 - },
187 - fail: (err) => {
188 - console.error('打开文件失败:', err)
189 -
190 - // 获取文件扩展名
191 - const fileExt = item.title.split('.').pop()?.toLowerCase() || ''
192 -
193 - // 根据文件类型给出提示
194 - let message = '文件打开失败'
195 - let suggestion = ''
196 -
197 - if (['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'].includes(fileExt)) {
198 - message = '暂不支持预览 Office 文档'
199 - suggestion = '\n\n建议:\n1. 点击右上角"..."菜单\n2. 选择"发送给朋友"\n3. 在电脑或支持的应用中打开'
200 - } else if (['pdf'].includes(fileExt)) {
201 - message = 'PDF 文件打开失败'
202 - suggestion = '\n\n文件可能已损坏,请联系管理员'
203 - } else {
204 - message = `暂不支持预览 ${fileExt.toUpperCase()} 格式文件`
205 - suggestion = '\n\n请在电脑或其他应用中打开'
206 - }
207 -
208 - Taro.showModal({
209 - title: '提示',
210 - content: message + suggestion,
211 - showCancel: false,
212 - confirmText: '我知道了'
213 - })
214 - }
215 - })
216 - } catch (error) {
217 - console.error('打开文件异常:', error)
218 - Taro.showToast({
219 - title: '打开文件失败',
220 - icon: 'none',
221 - duration: 2000
222 - })
223 - }
224 -}
225 -
226 const onDelete = (item) => { 146 const onDelete = (item) => {
227 Taro.showModal({ 147 Taro.showModal({
228 title: '提示', 148 title: '提示',
...@@ -235,71 +155,6 @@ const onDelete = (item) => { ...@@ -235,71 +155,6 @@ const onDelete = (item) => {
235 } 155 }
236 }) 156 })
237 } 157 }
238 -
239 -// 下载并打开文件的内部函数
240 -const downloadAndOpenFile = async (item) => {
241 - try {
242 - // 下载文件
243 - const downloadResult = await Taro.downloadFile({
244 - url: item.downloadUrl
245 - })
246 -
247 - // 检查下载结果
248 - if (downloadResult.statusCode !== 200) {
249 - throw new Error(`打开失败: HTTP ${downloadResult.statusCode}`)
250 - }
251 -
252 - if (!downloadResult.tempFilePath) {
253 - throw new Error('打开失败: 未获取到文件')
254 - }
255 -
256 - // 隐藏加载提示
257 - Taro.hideLoading()
258 -
259 - // 获取文件扩展名
260 - const fileExt = item.title.split('.').pop()?.toLowerCase() || ''
261 -
262 - // 微信小程序对 Office 文档支持有限,提前提示用户
263 - const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls']
264 -
265 - if (unsupportedFormats.includes(fileExt)) {
266 - // 对于 Office 文档,先提示用户,但仍尝试打开
267 - Taro.showModal({
268 - title: '预览提示',
269 - content: `小程序对 ${fileExt.toUpperCase()} 文档的预览支持有限,如果显示为空白,请点击右上角"..."菜单,选择"发送给朋友"后在电脑或其他应用中打开。\n\n是否继续尝试预览?`,
270 - confirmText: '继续',
271 - cancelText: '取消',
272 - success: (modalRes) => {
273 - if (modalRes.confirm) {
274 - openFile(downloadResult.tempFilePath, item)
275 - }
276 - }
277 - })
278 - } else {
279 - // 其他格式直接打开
280 - await openFile(downloadResult.tempFilePath, item)
281 - }
282 - } catch (error) {
283 - // 确保隐藏加载提示
284 - Taro.hideLoading()
285 -
286 - console.error('打开文件出错:', error)
287 -
288 - // 根据错误类型显示不同的提示
289 - let errorMessage = '打开失败,请重试'
290 - if (error.errMsg && error.errMsg.includes('network')) {
291 - errorMessage = '网络连接失败,请检查网络'
292 - } else if (error.errMsg && error.errMsg.includes('TLS')) {
293 - errorMessage = '安全连接失败,请检查网络'
294 - }
295 -
296 - Taro.showToast({
297 - title: errorMessage,
298 - icon: 'none',
299 - duration: 2000
300 - })
301 - }
302 -}
303 </script> 158 </script>
304 159
305 <style lang="less"> 160 <style lang="less">
......
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
101 <span class="text-[#9CA3AF] text-[24rpx]">{{ file.size }}</span> 101 <span class="text-[#9CA3AF] text-[24rpx]">{{ file.size }}</span>
102 </div> 102 </div>
103 </div> 103 </div>
104 - <IconFont name="download" size="20" color="#2563EB" @tap="onDownload(file)" /> 104 + <IconFont name="download" size="20" color="#2563EB" @tap="viewFile(file)" />
105 </div> 105 </div>
106 </div> 106 </div>
107 </div> 107 </div>
...@@ -118,8 +118,11 @@ import { ref } from 'vue' ...@@ -118,8 +118,11 @@ import { ref } from 'vue'
118 import NavHeader from '@/components/NavHeader.vue' 118 import NavHeader from '@/components/NavHeader.vue'
119 import TabBar from '@/components/TabBar.vue' 119 import TabBar from '@/components/TabBar.vue'
120 import IconFont from '@/components/IconFont.vue' 120 import IconFont from '@/components/IconFont.vue'
121 +import { useFileOperation } from '@/composables/useFileOperation'
121 import Taro, { useLoad } from '@tarojs/taro' 122 import Taro, { useLoad } from '@tarojs/taro'
122 123
124 +const { viewFile } = useFileOperation()
125 +
123 // 接收页面参数 126 // 接收页面参数
124 const productId = ref(null) 127 const productId = ref(null)
125 128
...@@ -231,9 +234,6 @@ const files = ref([ ...@@ -231,9 +234,6 @@ const files = ref([
231 // 收藏状态 234 // 收藏状态
232 const isCollected = ref(false) 235 const isCollected = ref(false)
233 236
234 -// 记录是否已经提示过 Office 文档预览限制
235 -const hasShownOfficeTip = ref(false)
236 -
237 // 切换收藏状态 237 // 切换收藏状态
238 const toggleCollect = () => { 238 const toggleCollect = () => {
239 isCollected.value = !isCollected.value 239 isCollected.value = !isCollected.value
...@@ -249,130 +249,4 @@ const toggleCollect = () => { ...@@ -249,130 +249,4 @@ const toggleCollect = () => {
249 }) 249 })
250 } 250 }
251 } 251 }
252 -
253 -// 打开文件的通用函数
254 -const openFile = async (filePath, file) => {
255 - try {
256 - await Taro.openDocument({
257 - filePath: filePath,
258 - showMenu: true,
259 - success: () => {
260 - console.log('文件打开成功')
261 - },
262 - fail: (err) => {
263 - console.error('打开文件失败:', err)
264 -
265 - const fileExt = file.fileName.split('.').pop()?.toLowerCase() || ''
266 - let message = '文件打开失败'
267 - let suggestion = ''
268 -
269 - if (['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'].includes(fileExt)) {
270 - message = '暂不支持预览 Office 文档'
271 - suggestion = '\n\n建议:\n1. 点击右上角"..."菜单\n2. 选择"发送给朋友"\n3. 在电脑或支持的应用中打开'
272 - } else if (['pdf'].includes(fileExt)) {
273 - message = 'PDF 文件打开失败'
274 - suggestion = '\n\n文件可能已损坏,请联系管理员'
275 - } else {
276 - message = `暂不支持预览 ${fileExt.toUpperCase()} 格式文件`
277 - suggestion = '\n\n请在电脑或其他应用中打开'
278 - }
279 -
280 - Taro.showModal({
281 - title: '提示',
282 - content: message + suggestion,
283 - showCancel: false,
284 - confirmText: '我知道了'
285 - })
286 - }
287 - })
288 - } catch (error) {
289 - console.error('打开文件异常:', error)
290 - Taro.showToast({
291 - title: '打开文件失败',
292 - icon: 'none',
293 - duration: 2000
294 - })
295 - }
296 -}
297 -
298 -// 下载并打开文件的函数
299 -const downloadAndOpenFile = async (file) => {
300 - try {
301 - const downloadResult = await Taro.downloadFile({
302 - url: file.downloadUrl
303 - })
304 -
305 - if (downloadResult.statusCode !== 200) {
306 - throw new Error(`打开失败: HTTP ${downloadResult.statusCode}`)
307 - }
308 -
309 - if (!downloadResult.tempFilePath) {
310 - throw new Error('打开失败: 未获取到文件')
311 - }
312 -
313 - Taro.hideLoading()
314 -
315 - const fileExt = file.fileName.split('.').pop()?.toLowerCase() || ''
316 - const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls']
317 -
318 - if (unsupportedFormats.includes(fileExt)) {
319 - // 对于 Office 文档,如果还没提示过,就提示一次
320 - if (!hasShownOfficeTip.value) {
321 - Taro.showModal({
322 - title: '预览提示',
323 - content: `小程序对 ${fileExt.toUpperCase()} 文档的预览支持有限,如果显示为空白,请点击右上角"..."菜单,选择"发送给朋友"后在电脑或其他应用中打开。\n\n是否继续尝试预览?`,
324 - confirmText: '继续',
325 - cancelText: '取消',
326 - success: (modalRes) => {
327 - if (modalRes.confirm) {
328 - hasShownOfficeTip.value = true // 标记已提示过
329 - openFile(downloadResult.tempFilePath, file)
330 - }
331 - }
332 - })
333 - } else {
334 - // 已经提示过,直接打开
335 - await openFile(downloadResult.tempFilePath, file)
336 - }
337 - } else {
338 - // PDF 等其他格式直接打开
339 - await openFile(downloadResult.tempFilePath, file)
340 - }
341 - } catch (error) {
342 - Taro.hideLoading()
343 - console.error('打开文件出错:', error)
344 -
345 - let errorMessage = '打开失败,请重试'
346 - if (error.errMsg && error.errMsg.includes('network')) {
347 - errorMessage = '网络连接失败,请检查网络'
348 - } else if (error.errMsg && error.errMsg.includes('TLS')) {
349 - errorMessage = '安全连接失败,请检查网络'
350 - }
351 -
352 - Taro.showToast({
353 - title: errorMessage,
354 - icon: 'none',
355 - duration: 2000
356 - })
357 - }
358 -}
359 -
360 -// 下载按钮点击处理
361 -const onDownload = (file) => {
362 - if (!file.downloadUrl) {
363 - Taro.showToast({
364 - title: '该文件暂无下载地址',
365 - icon: 'none',
366 - duration: 2000
367 - })
368 - return
369 - }
370 -
371 - Taro.showLoading({
372 - title: '打开中...',
373 - mask: true
374 - })
375 -
376 - downloadAndOpenFile(file)
377 -}
378 </script> 252 </script>
......