上次 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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user