feat(favorites): 新增收藏列表项下载功能
- 在删除按钮旁新增下载按钮 - 实现文件下载预览功能(支持 PDF、Office 文档等) - 智能文件类型识别,针对 Office 文档提供预览限制提示 - 优化下载失败和文件无法预览时的用户提示 - 添加文件下载地址字段到数据模型 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
1 changed file
with
170 additions
and
4 deletions
| ... | @@ -39,11 +39,18 @@ | ... | @@ -39,11 +39,18 @@ |
| 39 | </view> | 39 | </view> |
| 40 | </view> | 40 | </view> |
| 41 | 41 | ||
| 42 | + <!-- Action Buttons --> | ||
| 43 | + <view class="flex items-center gap-[16rpx]"> | ||
| 44 | + <!-- Download Button --> | ||
| 45 | + <view class="p-[10rpx]" @tap.stop="onDownload(item)"> | ||
| 46 | + <IconFont name="Download" size="18" color="#007AFF" /> | ||
| 47 | + </view> | ||
| 42 | <!-- Delete Button --> | 48 | <!-- Delete Button --> |
| 43 | <view class="p-[10rpx]" @tap.stop="onDelete(item)"> | 49 | <view class="p-[10rpx]" @tap.stop="onDelete(item)"> |
| 44 | <IconFont name="Del" size="18" color="#999" /> | 50 | <IconFont name="Del" size="18" color="#999" /> |
| 45 | </view> | 51 | </view> |
| 46 | </view> | 52 | </view> |
| 53 | + </view> | ||
| 47 | 54 | ||
| 48 | <!-- Empty State --> | 55 | <!-- Empty State --> |
| 49 | <view v-if="filteredList.length === 0" | 56 | <view v-if="filteredList.length === 0" |
| ... | @@ -85,7 +92,9 @@ const list = ref([ | ... | @@ -85,7 +92,9 @@ const list = ref([ |
| 85 | type: 'onboarding', | 92 | type: 'onboarding', |
| 86 | icon: 'Order', // Represents a document | 93 | icon: 'Order', // Represents a document |
| 87 | iconColor: '#EF4444', // Red for PDF | 94 | iconColor: '#EF4444', // Red for PDF |
| 88 | - iconBgClass: 'bg-red-50' | 95 | + iconBgClass: 'bg-red-50', |
| 96 | + // 使用真实的可下载 PDF 文件(W3C 测试文件) | ||
| 97 | + downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%E6%95%99%E8%82%B22024%E9%A1%B9%E7%9B%AE%E5%9B%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf' | ||
| 89 | }, | 98 | }, |
| 90 | { | 99 | { |
| 91 | id: 2, | 100 | id: 2, |
| ... | @@ -95,7 +104,9 @@ const list = ref([ | ... | @@ -95,7 +104,9 @@ const list = ref([ |
| 95 | type: 'signing', | 104 | type: 'signing', |
| 96 | icon: 'Order', // Represents a document | 105 | icon: 'Order', // Represents a document |
| 97 | iconColor: '#2563EB', // Blue for Word | 106 | iconColor: '#2563EB', // Blue for Word |
| 98 | - iconBgClass: 'bg-blue-50' | 107 | + iconBgClass: 'bg-blue-50', |
| 108 | + // 使用真实的可下载图片(作为文档示例) | ||
| 109 | + downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/%E8%80%81%E6%9D%A5%E8%B5%9B%E9%9A%90%E7%A7%81%E6%94%BF%E7%AD%96.docx' | ||
| 99 | }, | 110 | }, |
| 100 | { | 111 | { |
| 101 | id: 3, | 112 | id: 3, |
| ... | @@ -105,7 +116,9 @@ const list = ref([ | ... | @@ -105,7 +116,9 @@ const list = ref([ |
| 105 | type: 'product', | 116 | type: 'product', |
| 106 | icon: 'Order', // Represents a document | 117 | icon: 'Order', // Represents a document |
| 107 | iconColor: '#F59E0B', // Orange for PPT | 118 | iconColor: '#F59E0B', // Orange for PPT |
| 108 | - iconBgClass: 'bg-orange-50' | 119 | + iconBgClass: 'bg-orange-50', |
| 120 | + // 使用另一个图片作为示例 | ||
| 121 | + downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/%E8%82%A1%E5%88%A4%E5%90%88%E5%8F%8B%E7%94%A8%E7%9F%A5%E8%AF%86%E8%AF%B4%E6%98%8E20240112110417414.pptx' | ||
| 109 | }, | 122 | }, |
| 110 | { | 123 | { |
| 111 | id: 4, | 124 | id: 4, |
| ... | @@ -115,7 +128,8 @@ const list = ref([ | ... | @@ -115,7 +128,8 @@ const list = ref([ |
| 115 | type: 'other', | 128 | type: 'other', |
| 116 | icon: 'Edit', // Represents text | 129 | icon: 'Edit', // Represents text |
| 117 | iconColor: '#10B981', // Green for TXT | 130 | iconColor: '#10B981', // Green for TXT |
| 118 | - iconBgClass: 'bg-green-50' | 131 | + iconBgClass: 'bg-green-50', |
| 132 | + downloadUrl: '' // 空下载地址,用于测试无地址情况 | ||
| 119 | } | 133 | } |
| 120 | ]) | 134 | ]) |
| 121 | 135 | ||
| ... | @@ -128,6 +142,67 @@ const onView = (item) => { | ... | @@ -128,6 +142,67 @@ const onView = (item) => { |
| 128 | Taro.showToast({ title: `打开: ${item.title}`, icon: 'none' }) | 142 | Taro.showToast({ title: `打开: ${item.title}`, icon: 'none' }) |
| 129 | } | 143 | } |
| 130 | 144 | ||
| 145 | +// 打开文件的通用函数 | ||
| 146 | +const openFile = async (filePath, item) => { | ||
| 147 | + try { | ||
| 148 | + await Taro.openDocument({ | ||
| 149 | + filePath: filePath, | ||
| 150 | + showMenu: true, // 显示右上角菜单,用户可以转发、保存等 | ||
| 151 | + success: () => { | ||
| 152 | + console.log('文件打开成功') | ||
| 153 | + // 文件打开后,延迟提示用户如果看不到内容该如何操作 | ||
| 154 | + const fileExt = item.title.split('.').pop()?.toLowerCase() || '' | ||
| 155 | + const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'] | ||
| 156 | + | ||
| 157 | + if (unsupportedFormats.includes(fileExt)) { | ||
| 158 | + setTimeout(() => { | ||
| 159 | + Taro.showToast({ | ||
| 160 | + title: '如无法预览,请使用右上角菜单分享', | ||
| 161 | + icon: 'none', | ||
| 162 | + duration: 3000 | ||
| 163 | + }) | ||
| 164 | + }, 1500) | ||
| 165 | + } | ||
| 166 | + }, | ||
| 167 | + fail: (err) => { | ||
| 168 | + console.error('打开文件失败:', err) | ||
| 169 | + | ||
| 170 | + // 获取文件扩展名 | ||
| 171 | + const fileExt = item.title.split('.').pop()?.toLowerCase() || '' | ||
| 172 | + | ||
| 173 | + // 根据文件类型给出提示 | ||
| 174 | + let message = '文件打开失败' | ||
| 175 | + let suggestion = '' | ||
| 176 | + | ||
| 177 | + if (['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'].includes(fileExt)) { | ||
| 178 | + message = '暂不支持预览 Office 文档' | ||
| 179 | + suggestion = '\n\n建议:\n1. 点击右上角"..."菜单\n2. 选择"发送给朋友"\n3. 在电脑或支持的应用中打开' | ||
| 180 | + } else if (['pdf'].includes(fileExt)) { | ||
| 181 | + message = 'PDF 文件打开失败' | ||
| 182 | + suggestion = '\n\n文件可能已损坏,请联系管理员' | ||
| 183 | + } else { | ||
| 184 | + message = `暂不支持预览 ${fileExt.toUpperCase()} 格式文件` | ||
| 185 | + suggestion = '\n\n请在电脑或其他应用中打开' | ||
| 186 | + } | ||
| 187 | + | ||
| 188 | + Taro.showModal({ | ||
| 189 | + title: '提示', | ||
| 190 | + content: message + suggestion, | ||
| 191 | + showCancel: false, | ||
| 192 | + confirmText: '我知道了' | ||
| 193 | + }) | ||
| 194 | + } | ||
| 195 | + }) | ||
| 196 | + } catch (error) { | ||
| 197 | + console.error('打开文件异常:', error) | ||
| 198 | + Taro.showToast({ | ||
| 199 | + title: '打开文件失败', | ||
| 200 | + icon: 'none', | ||
| 201 | + duration: 2000 | ||
| 202 | + }) | ||
| 203 | + } | ||
| 204 | +} | ||
| 205 | + | ||
| 131 | const onDelete = (item) => { | 206 | const onDelete = (item) => { |
| 132 | Taro.showModal({ | 207 | Taro.showModal({ |
| 133 | title: '提示', | 208 | title: '提示', |
| ... | @@ -140,6 +215,97 @@ const onDelete = (item) => { | ... | @@ -140,6 +215,97 @@ const onDelete = (item) => { |
| 140 | } | 215 | } |
| 141 | }) | 216 | }) |
| 142 | } | 217 | } |
| 218 | + | ||
| 219 | +const onDownload = (item) => { | ||
| 220 | + // 检查是否有下载地址 | ||
| 221 | + if (!item.downloadUrl) { | ||
| 222 | + Taro.showToast({ | ||
| 223 | + title: '该文件暂无下载地址', | ||
| 224 | + icon: 'none', | ||
| 225 | + duration: 2000 | ||
| 226 | + }) | ||
| 227 | + return | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + // 显示确认弹窗 | ||
| 231 | + Taro.showModal({ | ||
| 232 | + title: '下载确认', | ||
| 233 | + content: `确定要下载"${item.title}"吗?`, | ||
| 234 | + confirmText: '下载', | ||
| 235 | + cancelText: '取消', | ||
| 236 | + success: async (res) => { | ||
| 237 | + if (res.confirm) { | ||
| 238 | + // 显示加载提示 | ||
| 239 | + Taro.showLoading({ | ||
| 240 | + title: '下载中...', | ||
| 241 | + mask: true | ||
| 242 | + }) | ||
| 243 | + | ||
| 244 | + try { | ||
| 245 | + // 下载文件 | ||
| 246 | + const downloadResult = await Taro.downloadFile({ | ||
| 247 | + url: item.downloadUrl | ||
| 248 | + }) | ||
| 249 | + | ||
| 250 | + // 检查下载结果 | ||
| 251 | + if (downloadResult.statusCode !== 200) { | ||
| 252 | + throw new Error(`下载失败: HTTP ${downloadResult.statusCode}`) | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + if (!downloadResult.tempFilePath) { | ||
| 256 | + throw new Error('下载失败: 未获取到文件') | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + // 隐藏加载提示 | ||
| 260 | + Taro.hideLoading() | ||
| 261 | + | ||
| 262 | + // 获取文件扩展名 | ||
| 263 | + const fileExt = item.title.split('.').pop()?.toLowerCase() || '' | ||
| 264 | + | ||
| 265 | + // 微信小程序对 Office 文档支持有限,提前提示用户 | ||
| 266 | + const unsupportedFormats = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'] | ||
| 267 | + | ||
| 268 | + if (unsupportedFormats.includes(fileExt)) { | ||
| 269 | + // 对于 Office 文档,先提示用户,但仍尝试打开 | ||
| 270 | + Taro.showModal({ | ||
| 271 | + title: '预览提示', | ||
| 272 | + content: `小程序对 ${fileExt.toUpperCase()} 文档的预览支持有限,如果显示为空白,请点击右上角"..."菜单,选择"发送给朋友"后在电脑或其他应用中打开。\n\n是否继续尝试预览?`, | ||
| 273 | + confirmText: '继续', | ||
| 274 | + cancelText: '取消', | ||
| 275 | + success: (modalRes) => { | ||
| 276 | + if (modalRes.confirm) { | ||
| 277 | + openFile(downloadResult.tempFilePath, item) | ||
| 278 | + } | ||
| 279 | + } | ||
| 280 | + }) | ||
| 281 | + } else { | ||
| 282 | + // 其他格式直接打开 | ||
| 283 | + await openFile(downloadResult.tempFilePath, item) | ||
| 284 | + } | ||
| 285 | + } catch (error) { | ||
| 286 | + // 确保隐藏加载提示 | ||
| 287 | + Taro.hideLoading() | ||
| 288 | + | ||
| 289 | + console.error('下载过程出错:', error) | ||
| 290 | + | ||
| 291 | + // 根据错误类型显示不同的提示 | ||
| 292 | + let errorMessage = '下载失败,请重试' | ||
| 293 | + if (error.errMsg && error.errMsg.includes('network')) { | ||
| 294 | + errorMessage = '网络连接失败,请检查网络' | ||
| 295 | + } else if (error.errMsg && error.errMsg.includes('TLS')) { | ||
| 296 | + errorMessage = '安全连接失败,请检查网络' | ||
| 297 | + } | ||
| 298 | + | ||
| 299 | + Taro.showToast({ | ||
| 300 | + title: errorMessage, | ||
| 301 | + icon: 'none', | ||
| 302 | + duration: 2000 | ||
| 303 | + }) | ||
| 304 | + } | ||
| 305 | + } | ||
| 306 | + } | ||
| 307 | + }) | ||
| 308 | +} | ||
| 143 | </script> | 309 | </script> |
| 144 | 310 | ||
| 145 | <style lang="less"> | 311 | <style lang="less"> | ... | ... |
-
Please register or login to post a comment