fix: fallback to file icon when thumbnail load fails

This commit is contained in:
2026-02-17 19:29:42 +08:00
parent 978ae545e1
commit dd6c439eb3
2 changed files with 34 additions and 5 deletions

View File

@@ -1693,9 +1693,10 @@
<div v-for="file in files" :key="file.name" class="file-grid-item" @click="handleFileClick(file)" @contextmenu.prevent.stop="showFileContextMenu(file, $event)" @touchstart="handleLongPressStart(file, $event)" @touchmove="handleLongPressMove($event)" @touchend="handleLongPressEnd" @touchcancel="handleLongPressEnd" @selectstart.prevent @dragstart.prevent>
<div class="file-icon">
<!-- 图片缩略图 -->
<img v-if="file.name.match(/\.(jpg|jpeg|png|gif|bmp|svg|webp)$/i) && getThumbnailUrl(file)"
<img v-if="file.name.match(/\.(jpg|jpeg|png|gif|bmp|svg|webp)$/i) && getThumbnailUrl(file) && !isThumbnailLoadFailed(file)"
:src="getThumbnailUrl(file)"
:alt="file.name"
@error="markThumbnailLoadFailed(file)"
class="file-thumbnail">
<!-- 视频图标(不预加载,避免慢) -->
<div v-else-if="file.name.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/i)"
@@ -1742,9 +1743,10 @@
<td class="file-list-name-cell">
<div class="file-list-name-wrap">
<!-- 图片缩略图 -->
<img v-if="file.name.match(/\.(jpg|jpeg|png|gif|bmp|svg|webp)$/i) && getThumbnailUrl(file)"
<img v-if="file.name.match(/\.(jpg|jpeg|png|gif|bmp|svg|webp)$/i) && getThumbnailUrl(file) && !isThumbnailLoadFailed(file)"
:src="getThumbnailUrl(file)"
:alt="file.name"
@error="markThumbnailLoadFailed(file)"
class="file-list-thumb">
<!-- 视频图标 -->
<div v-else-if="file.name.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/i)"

View File

@@ -260,6 +260,7 @@ createApp({
currentMediaUrl: '',
currentMediaName: '',
currentMediaType: '', // 'image', 'video', 'audio'
thumbnailLoadErrors: {}, // 缩略图加载失败标记(按文件路径)
longPressDuration: 420, // 长按时间(毫秒)
// 管理员编辑用户存储权限
showEditStorageModal: false,
@@ -1474,6 +1475,7 @@ handleDragLeave(e) {
if (response.data.success) {
this.files = response.data.items;
this.thumbnailLoadErrors = {};
// 更新存储类型信息
if (response.data.storageType) {
@@ -1886,6 +1888,25 @@ handleDragLeave(e) {
// ===== 媒体预览功能 =====
getCurrentFilePath(file) {
if (!file || !file.name) return '';
return this.currentPath === '/' ? `/${file.name}` : `${this.currentPath}/${file.name}`;
},
isThumbnailLoadFailed(file) {
const filePath = this.getCurrentFilePath(file);
return !!(filePath && this.thumbnailLoadErrors[filePath]);
},
markThumbnailLoadFailed(file) {
const filePath = this.getCurrentFilePath(file);
if (!filePath || this.thumbnailLoadErrors[filePath]) return;
this.thumbnailLoadErrors = {
...this.thumbnailLoadErrors,
[filePath]: true
};
},
// 获取媒体文件URLOSS直连或后端代理
async getMediaUrl(file) {
const filePath = this.currentPath === '/'
@@ -1922,12 +1943,18 @@ handleDragLeave(e) {
if (!isImage && !isVideo) return null;
const downloadQuota = Number(this.user?.download_traffic_quota);
const downloadUsed = Number(this.user?.download_traffic_used || 0);
if (Number.isFinite(downloadQuota) && downloadQuota >= 0) {
if (downloadQuota === 0 || downloadUsed >= downloadQuota) {
return null;
}
}
// 本地存储模式:返回同步的下载 URL
// OSS 模式下缩略图功能暂不支持(需要预签名 URL建议点击文件预览
if (this.storageType !== 'oss') {
const filePath = this.currentPath === '/'
? `/${file.name}`
: `${this.currentPath}/${file.name}`;
const filePath = this.getCurrentFilePath(file);
return `${this.apiBase}/api/files/download?path=${encodeURIComponent(filePath)}`;
}