hookehuyr

feat(StudyDetailPage): 添加文件下载功能

为StudyDetailPage页面添加文件下载功能,用户点击非PDF文件时触发下载操作。通过axios获取文件数据,并使用Blob对象处理下载流程,提升用户体验。
...@@ -54,8 +54,12 @@ ...@@ -54,8 +54,12 @@
54 <font-awesome-icon icon="file-alt" class="text-gray-400 text-lg flex-shrink-0" /> 54 <font-awesome-icon icon="file-alt" class="text-gray-400 text-lg flex-shrink-0" />
55 <h3 class="text-x font-medium text-gray-900 truncate">{{ item.title }}</h3> 55 <h3 class="text-x font-medium text-gray-900 truncate">{{ item.title }}</h3>
56 </div> 56 </div>
57 - <div v-if="item.url.toLowerCase().endsWith('.pdf')" class="text-x text-blue-600 hover:text-blue-800 hover:underline whitespace-nowrap" @click="showPdf(item)">查看文件</div> 57 + <div v-if="item.url.toLowerCase().endsWith('.pdf')"
58 - <a v-else :href="item.url" target="_blank" class="text-x text-blue-600 hover:text-blue-800 hover:underline whitespace-nowrap">查看文件</a> 58 + class="text-x text-blue-600 hover:text-blue-800 hover:underline whitespace-nowrap"
59 + @click="showPdf(item)">查看文件</div>
60 + <div v-else @click="downloadFile(item)"
61 + class="text-x text-blue-600 hover:text-blue-800 hover:underline whitespace-nowrap">
62 + 下载文件</div>
59 </div> 63 </div>
60 </div> 64 </div>
61 </div> 65 </div>
...@@ -135,7 +139,7 @@ ...@@ -135,7 +139,7 @@
135 <span class="font-medium text-gray-900">{{ comment.name }}</span> 139 <span class="font-medium text-gray-900">{{ comment.name }}</span>
136 <div class="flex items-center space-x-1"> 140 <div class="flex items-center space-x-1">
137 <span class="text-sm text-gray-500">{{ comment.like_count 141 <span class="text-sm text-gray-500">{{ comment.like_count
138 - }}</span> 142 + }}</span>
139 &nbsp; 143 &nbsp;
140 <van-icon :name="comment.is_like ? 'like' : 'like-o'" 144 <van-icon :name="comment.is_like ? 'like' : 'like-o'"
141 :class="{ 'text-red-500': comment.is_like, 'text-gray-400': !comment.is_like }" 145 :class="{ 'text-red-500': comment.is_like, 'text-gray-400': !comment.is_like }"
...@@ -145,7 +149,7 @@ ...@@ -145,7 +149,7 @@
145 </div> 149 </div>
146 <p class="text-gray-700 text-sm mb-1">{{ comment.note }}</p> 150 <p class="text-gray-700 text-sm mb-1">{{ comment.note }}</p>
147 <div class="text-gray-400 text-xs">{{ formatDate(comment.updated_time) 151 <div class="text-gray-400 text-xs">{{ formatDate(comment.updated_time)
148 - }}</div> 152 + }}</div>
149 </div> 153 </div>
150 </div> 154 </div>
151 </div> 155 </div>
...@@ -238,6 +242,7 @@ import VideoPlayer from '@/components/ui/VideoPlayer.vue'; ...@@ -238,6 +242,7 @@ import VideoPlayer from '@/components/ui/VideoPlayer.vue';
238 import AudioPlayer from '@/components/ui/AudioPlayer.vue'; 242 import AudioPlayer from '@/components/ui/AudioPlayer.vue';
239 import dayjs from 'dayjs'; 243 import dayjs from 'dayjs';
240 import { formatDate } from '@/utils/tools' 244 import { formatDate } from '@/utils/tools'
245 +import axios from 'axios';
241 246
242 import PdfPreview from '@/components/ui/PdfPreview.vue'; 247 import PdfPreview from '@/components/ui/PdfPreview.vue';
243 248
...@@ -360,7 +365,7 @@ const pdfShow = ref(false); ...@@ -360,7 +365,7 @@ const pdfShow = ref(false);
360 const pdfTitle = ref(''); 365 const pdfTitle = ref('');
361 const pdfUrl = ref(''); 366 const pdfUrl = ref('');
362 367
363 -const showPdf = ({title, url}) => { 368 +const showPdf = ({ title, url }) => {
364 pdfTitle.value = title; 369 pdfTitle.value = title;
365 pdfUrl.value = url; 370 pdfUrl.value = url;
366 pdfShow.value = true; 371 pdfShow.value = true;
...@@ -584,6 +589,59 @@ watch(showCommentPopup, async (newVal) => { ...@@ -584,6 +589,59 @@ watch(showCommentPopup, async (newVal) => {
584 onPopupLoad(); 589 onPopupLoad();
585 } 590 }
586 }); 591 });
592 +
593 +// 下载文件
594 +const downloadFile = ({ title, url }) => {
595 + // 获取文件URL和文件名
596 + const fileUrl = url;
597 + const fileName = title;
598 +
599 + // 根据文件URL后缀获取MIME类型
600 + const getMimeType = (url) => {
601 + const extension = url.split('.').pop().toLowerCase();
602 + const mimeTypes = {
603 + 'pdf': 'application/pdf',
604 + 'doc': 'application/msword',
605 + 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
606 + 'xls': 'application/vnd.ms-excel',
607 + 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
608 + 'ppt': 'application/vnd.ms-powerpoint',
609 + 'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
610 + 'txt': 'text/plain',
611 + 'zip': 'application/zip',
612 + 'rar': 'application/x-rar-compressed',
613 + '7z': 'application/x-7z-compressed',
614 + 'mp3': 'audio/mpeg',
615 + 'mp4': 'video/mp4',
616 + 'jpg': 'image/jpeg',
617 + 'jpeg': 'image/jpeg',
618 + 'png': 'image/png',
619 + 'gif': 'image/gif'
620 + };
621 + return mimeTypes[extension] || 'application/octet-stream';
622 + };
623 +
624 + axios({
625 + method: 'get',
626 + url: fileUrl,
627 + responseType: 'blob' // 表示返回的数据类型是Blob
628 + }).then((response) => {
629 + const blob = new Blob([response.data], { type: getMimeType(fileUrl) });
630 + const url = window.URL.createObjectURL(blob);
631 +
632 + const a = document.createElement('a');
633 + a.href = url;
634 + a.download = fileName;
635 + a.style.display = 'none';
636 + document.body.appendChild(a);
637 + a.click();
638 + document.body.removeChild(a);
639 +
640 + window.URL.revokeObjectURL(url);
641 + }).catch((error) => {
642 + console.error('下载文件出错:', error);
643 + });
644 +}
587 </script> 645 </script>
588 646
589 <style lang="less" scoped> 647 <style lang="less" scoped>
......