fix: 用 feature 分支的 statics.vue 覆盖 main 上的孤儿版本
部署到群晖 / deploy (push) Failing after 1m6s

上次 upstream merge (cf40a7e) 用 -X theirs 策略时,main 上残留了
upstream 独有的模板 HTML(清理无效记录按钮、刷新数据按钮、删除作者
弹窗),但对应的 setup 定义没跟上。结果:模板引用 handleCancelDeleteAuthor
handleDeleteAndBlacklist、handleDeleteOnly、loading、cleaning 等 5 处,
setup 里 grep -c 全为 0 → vue-tsc 严格类型检查报 TS2339 / TS2304
→ CI 构建镜像时 pnpm build 失败。

修法:用 feature 分支上 self-consistent 的 statics.vue 覆盖。不丢
任何能实际运行的功能(那些孤儿引用本来就跑不起来)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
yuming
2026-07-04 14:46:38 +08:00
parent 3759f264a5
commit d0ac3d98b0
+1 -230
View File
@@ -119,26 +119,6 @@
</button>
</a-badge>
</div>
<div class="header-actions">
<a-button
type="default"
:loading="cleaning"
@click="handleCleanup"
size="small"
style="margin-right: 8px;"
>
清理无效记录
</a-button>
<a-button
type="primary"
:loading="loading"
@click="loadDashboardData"
size="small"
>
<template #icon><ReloadOutlined /></template>
刷新数据
</a-button>
</div>
</div>
<transition name="stats-fade" mode="out-in">
@@ -167,7 +147,7 @@
</div>
<div v-else key="category-view" class="stats-content">
<div class="categories-grid">
<div class="category-card" v-for="(category, index) in categories" :key="index" :style="{ '--category-color': category.color }" @click="handleCategoryClick(category)">
<div class="category-card" v-for="(category, index) in categories" :key="index" :style="{ '--category-color': category.color }">
<div class="category-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 12 12" fill="white">
<use :xlink:href="`#${category.icon}`"></use>
@@ -240,192 +220,6 @@
<path d="M18 4h2v16h-2zM4 4h14v2H4zM4 8h10v2H4zM4 12h10v2H4zM4 16h6v2H4zM4 20h6v2H4z" />
</symbol>
</svg>
<!-- 作者视频列表模态框 -->
<a-modal
v-model:visible="authorVideoModalVisible"
:title="`${selectedAuthor?.name || ''} 的视频列表`"
:width="1200"
:footer="null"
@cancel="handleCloseAuthorModal"
class="author-video-modal"
>
<div class="author-video-content">
<!-- 卡片式视频列表 -->
<div v-if="authorVideoLoading" class="video-list-loading">
<a-spin size="large" tip="加载中..." />
</div>
<div v-else class="video-list-grid">
<div
v-for="video in authorVideoList"
:key="video.id"
class="video-card"
@click="handlePreviewVideo(video)"
>
<!-- 封面图片 -->
<div class="video-cover-wrapper">
<img
v-if="coverImageCache[video.id]"
:src="coverImageCache[video.id]"
:alt="video.videoTitle"
class="video-cover"
@error="handleCoverError"
@load="console.log('封面图片加载成功:', video.id)"
loading="lazy"
/>
<div v-else class="video-cover-placeholder">
<PlayCircleOutlined class="placeholder-icon" />
<span class="placeholder-text">{{ coverImageLoading.has(video.id) ? '加载中...' : '暂无封面' }}</span>
</div>
<!-- 播放按钮遮罩 -->
<div class="video-play-overlay">
<PlayCircleOutlined class="play-icon" />
</div>
<!-- 视频时长(如果有) -->
<div v-if="video.duration" class="video-duration">{{ formatDuration(video.duration) }}</div>
</div>
<!-- 视频信息 -->
<div class="video-info">
<div class="video-title" :title="video.videoTitle">{{ video.videoTitle || '未命名视频' }}</div>
<div class="video-meta">
<span class="video-category">{{ video.viedoCate || '未分类' }}</span>
<span class="video-time">{{ video.syncTimeStr }}</span>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="video-list-pagination">
<a-pagination
v-model:current="authorVideoPagination.current"
v-model:page-size="authorVideoPagination.pageSize"
:total="authorVideoPagination.total"
:show-total="authorVideoPagination.showTotal"
:show-size-changer="true"
:page-size-options="['10', '20', '50', '100']"
@change="handleAuthorVideoTableChange"
@showSizeChange="handleAuthorVideoTableChange"
/>
</div>
</div>
</a-modal>
<!-- 分类视频列表模态框 -->
<a-modal
v-model:visible="categoryVideoModalVisible"
:title="`${selectedCategory?.name || ''} 的视频列表`"
:width="1200"
:footer="null"
@cancel="handleCloseCategoryModal"
class="category-video-modal"
>
<div class="category-video-content">
<!-- 卡片式视频列表 -->
<div v-if="categoryVideoLoading" class="video-list-loading">
<a-spin size="large" tip="加载中..." />
</div>
<div v-else class="video-list-grid">
<div
v-for="video in categoryVideoList"
:key="video.id"
class="video-card"
@click="handlePreviewVideo(video)"
>
<!-- 封面图片 -->
<div class="video-cover-wrapper">
<img
v-if="coverImageCache[video.id]"
:src="coverImageCache[video.id]"
:alt="video.videoTitle"
class="video-cover"
@error="handleCoverError"
@load="console.log('封面图片加载成功:', video.id)"
loading="lazy"
/>
<div v-else class="video-cover-placeholder">
<PlayCircleOutlined class="placeholder-icon" />
<span class="placeholder-text">{{ coverImageLoading.has(video.id) ? '加载中...' : '暂无封面' }}</span>
</div>
<!-- 播放按钮遮罩 -->
<div class="video-play-overlay">
<PlayCircleOutlined class="play-icon" />
</div>
<!-- 视频时长(如果有) -->
<div v-if="video.duration" class="video-duration">{{ formatDuration(video.duration) }}</div>
</div>
<!-- 视频信息 -->
<div class="video-info">
<div class="video-title" :title="video.videoTitle">{{ video.videoTitle || '未命名视频' }}</div>
<div class="video-meta">
<span class="video-category">{{ video.viedoCate || '未分类' }}</span>
<span class="video-time">{{ video.syncTimeStr }}</span>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="video-list-pagination">
<a-pagination
v-model:current="categoryVideoPagination.current"
v-model:page-size="categoryVideoPagination.pageSize"
:total="categoryVideoPagination.total"
:show-total="categoryVideoPagination.showTotal"
:show-size-changer="true"
:page-size-options="['10', '20', '50', '100']"
@change="handleCategoryVideoTableChange"
@showSizeChange="handleCategoryVideoTableChange"
/>
</div>
</div>
</a-modal>
<!-- 删除作者确认弹窗 -->
<a-modal
v-model:visible="deleteAuthorModalVisible"
title="删除作者确认"
:width="500"
:footer="null"
@cancel="handleCancelDeleteAuthor"
>
<div class="delete-author-content">
<div class="warning-icon">
<ExclamationCircleOutlined style="font-size: 48px; color: #faad14;" />
</div>
<p class="delete-warning-text">
是否删除该作者<strong>「{{ selectedAuthorForDelete?.name }}」</strong>的数据?
</p>
<p class="delete-sub-text">
您可以选择以下操作:
</p>
<div class="delete-actions">
<a-button
type="primary"
danger
size="large"
:loading="deleteLoading"
@click="handleDeleteAndBlacklist"
block
>
删除并加入黑名单(不再同步)
</a-button>
<a-button
type="default"
size="large"
:loading="deleteLoading"
@click="handleDeleteOnly"
style="margin-top: 12px;"
block
>
仅删除当前数据(后续继续同步)
</a-button>
</div>
</div>
</a-modal>
<!-- 视频播放器 -->
<VideoPlayer ref="videoPlayerRef" />
</div>
</template>
@@ -485,11 +279,6 @@ const videoSeriesSize = ref<string>('0.00');
const categories = ref<Category[]>([]);
const authors = ref<Author[]>([]);
// 删除作者相关
const deleteAuthorModalVisible = ref<boolean>(false);
const selectedAuthorForDelete = ref<Author | null>(null);
const deleteLoading = ref<boolean>(false);
const currentTab = ref<string>('author');
const tabCount = ref<number>(0);
@@ -816,15 +605,6 @@ watch(
onMounted(() => {
generateSyncData();
loadDashboardData();
// 每60秒自动刷新一次数据
const timer = setInterval(() => {
loadDashboardData();
}, 60000);
onUnmounted(() => {
clearInterval(timer);
});
});
// 新增:窗口关闭时销毁全屏图表
@@ -837,7 +617,6 @@ const changeTab = (e: string) => {
};
const loadDashboardData = async () => {
loading.value = true;
try {
const res = await useApiStore().VideoStatics();
totalAuthors.value = res.data.authorCount;
@@ -868,8 +647,6 @@ const loadDashboardData = async () => {
});
} catch (err) {
console.error('加载仪表盘数据失败:', err);
} finally {
loading.value = false;
}
};
@@ -1275,11 +1052,6 @@ const handleDeleteItem = (item: Author) => {
border-radius: 8px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
position: relative;
}
.author-card {
cursor: pointer;
}
.author-card:hover {
transform: translateY(-3px);
@@ -1343,7 +1115,6 @@ const handleDeleteItem = (item: Author) => {
border-radius: 8px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
cursor: pointer;
}
.category-card:hover {
transform: translateY(-3px);