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 @@
<!-- Action Buttons -->
<view class="flex flex-col items-end gap-[16rpx] ml-[16rpx] flex-shrink-0">
<!-- View Button -->
<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)">
<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})">
<IconFont name="eye" size="14" color="#2563EB" class="mr-[6rpx]" />
<text class="text-[24rpx] text-blue-600 font-medium leading-none">查看</text>
</view>
......@@ -70,13 +70,15 @@
<script setup>
import { ref, computed } from 'vue'
import Taro from '@tarojs/taro'
import { useGo } from '@/hooks/useGo'
import { useFileOperation } from '@/composables/useFileOperation'
import IconFont from '@/components/IconFont.vue'
import TabBar from '@/components/TabBar.vue'
import NavHeader from '@/components/NavHeader.vue'
import Taro from '@tarojs/taro'
const go = useGo()
const { viewFile } = useFileOperation()
const activeTab = ref('all')
const tabs = [
......@@ -141,88 +143,6 @@ const filteredList = computed(() => {
return list.value.filter(item => item.type === activeTab.value)
})
const onView = (item) => {
// 检查是否有下载地址
if (!item.downloadUrl) {
Taro.showToast({
title: '该文件暂无查看地址',
icon: 'none',
duration: 2000
})
return
}
// 显示加载提示
Taro.showLoading({
title: '打开中...',
mask: true
})
// 下载并打开文件
downloadAndOpenFile(item)
}
// 打开文件的通用函数
const openFile = async (filePath, item) => {
try {
await Taro.openDocument({
filePath: filePath,
showMenu: true, // 显示右上角菜单,用户可以转发、保存等
success: () => {
console.log('文件打开成功')
// 文件打开后,延迟提示用户如果看不到内容该如何操作
const fileExt = item.title.split('.').pop()?.toLowerCase() || ''
const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls']
if (unsupportedFormats.includes(fileExt)) {
setTimeout(() => {
Taro.showToast({
title: '如无法预览,请使用右上角菜单分享',
icon: 'none',
duration: 3000
})
}, 1500)
}
},
fail: (err) => {
console.error('打开文件失败:', err)
// 获取文件扩展名
const fileExt = item.title.split('.').pop()?.toLowerCase() || ''
// 根据文件类型给出提示
let message = '文件打开失败'
let suggestion = ''
if (['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'].includes(fileExt)) {
message = '暂不支持预览 Office 文档'
suggestion = '\n\n建议:\n1. 点击右上角"..."菜单\n2. 选择"发送给朋友"\n3. 在电脑或支持的应用中打开'
} else if (['pdf'].includes(fileExt)) {
message = 'PDF 文件打开失败'
suggestion = '\n\n文件可能已损坏,请联系管理员'
} else {
message = `暂不支持预览 ${fileExt.toUpperCase()} 格式文件`
suggestion = '\n\n请在电脑或其他应用中打开'
}
Taro.showModal({
title: '提示',
content: message + suggestion,
showCancel: false,
confirmText: '我知道了'
})
}
})
} catch (error) {
console.error('打开文件异常:', error)
Taro.showToast({
title: '打开文件失败',
icon: 'none',
duration: 2000
})
}
}
const onDelete = (item) => {
Taro.showModal({
title: '提示',
......@@ -235,71 +155,6 @@ const onDelete = (item) => {
}
})
}
// 下载并打开文件的内部函数
const downloadAndOpenFile = async (item) => {
try {
// 下载文件
const downloadResult = await Taro.downloadFile({
url: item.downloadUrl
})
// 检查下载结果
if (downloadResult.statusCode !== 200) {
throw new Error(`打开失败: HTTP ${downloadResult.statusCode}`)
}
if (!downloadResult.tempFilePath) {
throw new Error('打开失败: 未获取到文件')
}
// 隐藏加载提示
Taro.hideLoading()
// 获取文件扩展名
const fileExt = item.title.split('.').pop()?.toLowerCase() || ''
// 微信小程序对 Office 文档支持有限,提前提示用户
const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls']
if (unsupportedFormats.includes(fileExt)) {
// 对于 Office 文档,先提示用户,但仍尝试打开
Taro.showModal({
title: '预览提示',
content: `小程序对 ${fileExt.toUpperCase()} 文档的预览支持有限,如果显示为空白,请点击右上角"..."菜单,选择"发送给朋友"后在电脑或其他应用中打开。\n\n是否继续尝试预览?`,
confirmText: '继续',
cancelText: '取消',
success: (modalRes) => {
if (modalRes.confirm) {
openFile(downloadResult.tempFilePath, item)
}
}
})
} else {
// 其他格式直接打开
await openFile(downloadResult.tempFilePath, item)
}
} catch (error) {
// 确保隐藏加载提示
Taro.hideLoading()
console.error('打开文件出错:', error)
// 根据错误类型显示不同的提示
let errorMessage = '打开失败,请重试'
if (error.errMsg && error.errMsg.includes('network')) {
errorMessage = '网络连接失败,请检查网络'
} else if (error.errMsg && error.errMsg.includes('TLS')) {
errorMessage = '安全连接失败,请检查网络'
}
Taro.showToast({
title: errorMessage,
icon: 'none',
duration: 2000
})
}
}
</script>
<style lang="less">
......
......@@ -101,7 +101,7 @@
<span class="text-[#9CA3AF] text-[24rpx]">{{ file.size }}</span>
</div>
</div>
<IconFont name="download" size="20" color="#2563EB" @tap="onDownload(file)" />
<IconFont name="download" size="20" color="#2563EB" @tap="viewFile(file)" />
</div>
</div>
</div>
......@@ -118,8 +118,11 @@ import { ref } from 'vue'
import NavHeader from '@/components/NavHeader.vue'
import TabBar from '@/components/TabBar.vue'
import IconFont from '@/components/IconFont.vue'
import { useFileOperation } from '@/composables/useFileOperation'
import Taro, { useLoad } from '@tarojs/taro'
const { viewFile } = useFileOperation()
// 接收页面参数
const productId = ref(null)
......@@ -231,9 +234,6 @@ const files = ref([
// 收藏状态
const isCollected = ref(false)
// 记录是否已经提示过 Office 文档预览限制
const hasShownOfficeTip = ref(false)
// 切换收藏状态
const toggleCollect = () => {
isCollected.value = !isCollected.value
......@@ -249,130 +249,4 @@ const toggleCollect = () => {
})
}
}
// 打开文件的通用函数
const openFile = async (filePath, file) => {
try {
await Taro.openDocument({
filePath: filePath,
showMenu: true,
success: () => {
console.log('文件打开成功')
},
fail: (err) => {
console.error('打开文件失败:', err)
const fileExt = file.fileName.split('.').pop()?.toLowerCase() || ''
let message = '文件打开失败'
let suggestion = ''
if (['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'].includes(fileExt)) {
message = '暂不支持预览 Office 文档'
suggestion = '\n\n建议:\n1. 点击右上角"..."菜单\n2. 选择"发送给朋友"\n3. 在电脑或支持的应用中打开'
} else if (['pdf'].includes(fileExt)) {
message = 'PDF 文件打开失败'
suggestion = '\n\n文件可能已损坏,请联系管理员'
} else {
message = `暂不支持预览 ${fileExt.toUpperCase()} 格式文件`
suggestion = '\n\n请在电脑或其他应用中打开'
}
Taro.showModal({
title: '提示',
content: message + suggestion,
showCancel: false,
confirmText: '我知道了'
})
}
})
} catch (error) {
console.error('打开文件异常:', error)
Taro.showToast({
title: '打开文件失败',
icon: 'none',
duration: 2000
})
}
}
// 下载并打开文件的函数
const downloadAndOpenFile = async (file) => {
try {
const downloadResult = await Taro.downloadFile({
url: file.downloadUrl
})
if (downloadResult.statusCode !== 200) {
throw new Error(`打开失败: HTTP ${downloadResult.statusCode}`)
}
if (!downloadResult.tempFilePath) {
throw new Error('打开失败: 未获取到文件')
}
Taro.hideLoading()
const fileExt = file.fileName.split('.').pop()?.toLowerCase() || ''
const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls']
if (unsupportedFormats.includes(fileExt)) {
// 对于 Office 文档,如果还没提示过,就提示一次
if (!hasShownOfficeTip.value) {
Taro.showModal({
title: '预览提示',
content: `小程序对 ${fileExt.toUpperCase()} 文档的预览支持有限,如果显示为空白,请点击右上角"..."菜单,选择"发送给朋友"后在电脑或其他应用中打开。\n\n是否继续尝试预览?`,
confirmText: '继续',
cancelText: '取消',
success: (modalRes) => {
if (modalRes.confirm) {
hasShownOfficeTip.value = true // 标记已提示过
openFile(downloadResult.tempFilePath, file)
}
}
})
} else {
// 已经提示过,直接打开
await openFile(downloadResult.tempFilePath, file)
}
} else {
// PDF 等其他格式直接打开
await openFile(downloadResult.tempFilePath, file)
}
} catch (error) {
Taro.hideLoading()
console.error('打开文件出错:', error)
let errorMessage = '打开失败,请重试'
if (error.errMsg && error.errMsg.includes('network')) {
errorMessage = '网络连接失败,请检查网络'
} else if (error.errMsg && error.errMsg.includes('TLS')) {
errorMessage = '安全连接失败,请检查网络'
}
Taro.showToast({
title: errorMessage,
icon: 'none',
duration: 2000
})
}
}
// 下载按钮点击处理
const onDownload = (file) => {
if (!file.downloadUrl) {
Taro.showToast({
title: '该文件暂无下载地址',
icon: 'none',
duration: 2000
})
return
}
Taro.showLoading({
title: '打开中...',
mask: true
})
downloadAndOpenFile(file)
}
</script>
......