|
|
@@ -69,22 +69,13 @@
|
|
|
@click="openWorkDetail(work)"
|
|
|
>
|
|
|
<div class="work-cover">
|
|
|
- <img :src="getSecureCoverUrl(work.coverUrl)" :alt="work.title" @error="handleImageError" />
|
|
|
- <span class="work-duration">{{ formatDuration(work.duration) }}</span>
|
|
|
- <el-tag
|
|
|
- class="work-status"
|
|
|
- :type="getStatusType(work.status)"
|
|
|
- size="small"
|
|
|
- >
|
|
|
- {{ getStatusText(work.status) }}
|
|
|
- </el-tag>
|
|
|
+ <img :src="getWorkCoverSrc(work)" :alt="work.title" @error="handleImageError" />
|
|
|
</div>
|
|
|
|
|
|
<div class="work-info">
|
|
|
<div class="work-title" :title="work.title">{{ work.title || '无标题' }}</div>
|
|
|
<div class="work-meta">
|
|
|
<el-tag size="small" type="info">{{ getPlatformName(work.platform) }}</el-tag>
|
|
|
- <span class="work-time">{{ formatDate(work.publishTime) }}</span>
|
|
|
</div>
|
|
|
<div class="work-stats">
|
|
|
<span><el-icon><VideoPlay /></el-icon> {{ formatNumber(work.playCount) }}</span>
|
|
|
@@ -193,7 +184,7 @@
|
|
|
destroy-on-close
|
|
|
>
|
|
|
<div class="comments-drawer-header" v-if="commentsWork">
|
|
|
- <img :src="getSecureCoverUrl(commentsWork.coverUrl)" class="work-thumb" @error="handleImageError" />
|
|
|
+ <img :src="getWorkCoverSrc(commentsWork)" class="work-thumb" @error="handleImageError" />
|
|
|
<div class="work-brief">
|
|
|
<div class="work-brief-title">{{ commentsWork.title || '无标题' }}</div>
|
|
|
<div class="work-brief-meta">
|
|
|
@@ -495,6 +486,15 @@ function formatDuration(seconds: number | string | undefined): string {
|
|
|
return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
|
}
|
|
|
|
|
|
+// 作品列表/评论等处的封面:兼容 coverUrl / cover_url,无封面时用占位图
|
|
|
+const COVER_PLACEHOLDER = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect fill="%23f0f0f0" width="100" height="100"/><text x="50" y="55" text-anchor="middle" fill="%23999" font-size="12">无封面</text></svg>';
|
|
|
+
|
|
|
+function getWorkCoverSrc(work: Work): string {
|
|
|
+ const url = (work as { coverUrl?: string; cover_url?: string }).coverUrl ?? (work as { cover_url?: string }).cover_url ?? '';
|
|
|
+ if (!url || typeof url !== 'string' || !url.trim()) return COVER_PLACEHOLDER;
|
|
|
+ return getSecureCoverUrl(url);
|
|
|
+}
|
|
|
+
|
|
|
// 将 HTTP 图片 URL 转换为 HTTPS(小红书等平台的图片 URL 可能是 HTTP)
|
|
|
function getSecureCoverUrl(url: string): string {
|
|
|
if (!url) return '';
|
|
|
@@ -507,7 +507,7 @@ function getSecureCoverUrl(url: string): string {
|
|
|
|
|
|
function handleImageError(e: Event) {
|
|
|
const img = e.target as HTMLImageElement;
|
|
|
- img.src = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect fill="%23f0f0f0" width="100" height="100"/><text x="50" y="55" text-anchor="middle" fill="%23999" font-size="12">无封面</text></svg>';
|
|
|
+ img.src = COVER_PLACEHOLDER;
|
|
|
}
|
|
|
|
|
|
async function loadWorks() {
|
|
|
@@ -936,23 +936,6 @@ onUnmounted(() => {
|
|
|
height: 100%;
|
|
|
object-fit: cover;
|
|
|
}
|
|
|
-
|
|
|
- .work-duration {
|
|
|
- position: absolute;
|
|
|
- bottom: 8px;
|
|
|
- right: 8px;
|
|
|
- padding: 2px 6px;
|
|
|
- background: rgba(0, 0, 0, 0.7);
|
|
|
- color: #fff;
|
|
|
- font-size: 12px;
|
|
|
- border-radius: 4px;
|
|
|
- }
|
|
|
-
|
|
|
- .work-status {
|
|
|
- position: absolute;
|
|
|
- top: 8px;
|
|
|
- left: 8px;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
.work-info {
|
|
|
@@ -973,11 +956,6 @@ onUnmounted(() => {
|
|
|
align-items: center;
|
|
|
gap: 8px;
|
|
|
margin-bottom: 8px;
|
|
|
-
|
|
|
- .work-time {
|
|
|
- font-size: 12px;
|
|
|
- color: $text-secondary;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
.work-stats {
|