useFileOperation.js
6.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/**
* 统一的文件操作 Composable
*
* @description 提供文件下载、打开、预览等统一操作逻辑
* 处理 PDF、Office 文档等多种文件格式的预览和下载
*
* @author Claude Code
* @date 2026-01-31
*/
import { showToast, showLoading, hideLoading, showModal, openDocument, downloadFile } from '@tarojs/taro'
/**
* 文件操作 Hook
*
* @returns {Object} 文件操作方法集合
*/
export function useFileOperation() {
/**
* 打开文件的通用函数
*
* @description 使用 Taro.openDocument 打开文件,支持菜单转发和保存
* @async
* @param {string} filePath - 本地文件路径
* @param {Object} item - 文件信息对象
* @param {string} item.fileName - 文件名(用于判断文件类型)
* @returns {Promise<void>}
*
* @example
* const { openFile } = useFileOperation()
* await openFile(tempFilePath, { fileName: 'document.pdf' })
*/
const openFile = async (filePath, item) => {
try {
await openDocument({
filePath: filePath,
showMenu: true, // 显示右上角菜单,用户可以转发、保存等
success: () => {
console.log('文件打开成功')
// 文件打开后,延迟提示用户如果看不到内容该如何操作
const fileExt = item.fileName.split('.').pop()?.toLowerCase() || ''
const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls']
if (unsupportedFormats.includes(fileExt)) {
setTimeout(() => {
showToast({
title: '如无法预览,请使用右上角菜单分享',
icon: 'none',
duration: 3000
})
}, 1500)
}
},
fail: (err) => {
console.error('打开文件失败:', err)
// 获取文件扩展名
const fileExt = item.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请在电脑或其他应用中打开'
}
showModal({
title: '提示',
content: message + suggestion,
showCancel: false,
confirmText: '我知道了'
})
}
})
} catch (error) {
console.error('打开文件异常:', error)
showToast({
title: '打开文件失败',
icon: 'none',
duration: 2000
})
}
}
/**
* 下载并打开文件的内部函数
*
* @description 先下载文件到本地临时路径,再调用 openFile 打开
* @async
* @param {Object} item - 文件信息对象
* @param {string} item.downloadUrl - 文件下载地址
* @param {string} item.fileName - 文件名
* @returns {Promise<void>}
*
* @example
* const { downloadAndOpenFile } = useFileOperation()
* await downloadAndOpenFile({
* downloadUrl: 'https://example.com/file.pdf',
* fileName: 'document.pdf'
* })
*/
const downloadAndOpenFile = async (item) => {
try {
// 下载文件
const downloadResult = await downloadFile({
url: item.downloadUrl
})
// 检查下载结果
if (downloadResult.statusCode !== 200) {
throw new Error(`打开失败: HTTP ${downloadResult.statusCode}`)
}
if (!downloadResult.tempFilePath) {
throw new Error('打开失败: 未获取到文件')
}
// 隐藏加载提示
hideLoading()
// 获取文件扩展名
const fileExt = item.fileName.split('.').pop()?.toLowerCase() || ''
// 微信小程序对 Office 文档支持有限,提前提示用户
const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls']
if (unsupportedFormats.includes(fileExt)) {
// 对于 Office 文档,先提示用户,但仍尝试打开
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) {
// 确保隐藏加载提示
hideLoading()
console.error('打开文件出错:', error)
// 根据错误类型显示不同的提示
let errorMessage = '打开失败,请重试'
if (error.errMsg && error.errMsg.includes('network')) {
errorMessage = '网络连接失败,请检查网络'
} else if (error.errMsg && error.errMsg.includes('TLS')) {
errorMessage = '安全连接失败,请检查网络'
}
showToast({
title: errorMessage,
icon: 'none',
duration: 2000
})
}
}
/**
* 查看文件(入口函数)
*
* @description 检查文件是否有下载地址,然后下载并打开文件
* @async
* @param {Object} item - 文件信息对象
* @param {string} [item.downloadUrl] - 文件下载地址
* @param {string} item.fileName - 文件名
* @returns {Promise<void>}
*
* @example
* const { viewFile } = useFileOperation()
* await viewFile({
* downloadUrl: 'https://example.com/file.pdf',
* fileName: 'document.pdf'
* })
*/
const viewFile = async (item) => {
// 检查是否有下载地址
if (!item.downloadUrl) {
showToast({
title: '该文件暂无查看地址',
icon: 'none',
duration: 2000
})
return
}
// 显示加载提示
showLoading({
title: '打开中...',
mask: true
})
// 下载并打开文件
await downloadAndOpenFile(item)
}
return {
openFile,
downloadAndOpenFile,
viewFile
}
}