上次 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>
|
</button>
|
||||||
</a-badge>
|
</a-badge>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<transition name="stats-fade" mode="out-in">
|
<transition name="stats-fade" mode="out-in">
|
||||||
@@ -167,7 +147,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else key="category-view" class="stats-content">
|
<div v-else key="category-view" class="stats-content">
|
||||||
<div class="categories-grid">
|
<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">
|
<div class="category-icon">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 12 12" fill="white">
|
<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>
|
<use :xlink:href="`#${category.icon}`"></use>
|
||||||
@@ -240,192 +220,6 @@
|
|||||||
<path d="M18 4h2v16h-2zM4 4h14v2H4zM4 8h10v2H4zM4 12h10v2H4zM4 16h6v2H4zM4 20h6v2H4z" />
|
<path d="M18 4h2v16h-2zM4 4h14v2H4zM4 8h10v2H4zM4 12h10v2H4zM4 16h6v2H4zM4 20h6v2H4z" />
|
||||||
</symbol>
|
</symbol>
|
||||||
</svg>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -485,11 +279,6 @@ const videoSeriesSize = ref<string>('0.00');
|
|||||||
|
|
||||||
const categories = ref<Category[]>([]);
|
const categories = ref<Category[]>([]);
|
||||||
const authors = ref<Author[]>([]);
|
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 currentTab = ref<string>('author');
|
||||||
const tabCount = ref<number>(0);
|
const tabCount = ref<number>(0);
|
||||||
|
|
||||||
@@ -816,15 +605,6 @@ watch(
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
generateSyncData();
|
generateSyncData();
|
||||||
loadDashboardData();
|
loadDashboardData();
|
||||||
|
|
||||||
// 每60秒自动刷新一次数据
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
loadDashboardData();
|
|
||||||
}, 60000);
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
clearInterval(timer);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 新增:窗口关闭时销毁全屏图表
|
// 新增:窗口关闭时销毁全屏图表
|
||||||
@@ -837,7 +617,6 @@ const changeTab = (e: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadDashboardData = async () => {
|
const loadDashboardData = async () => {
|
||||||
loading.value = true;
|
|
||||||
try {
|
try {
|
||||||
const res = await useApiStore().VideoStatics();
|
const res = await useApiStore().VideoStatics();
|
||||||
totalAuthors.value = res.data.authorCount;
|
totalAuthors.value = res.data.authorCount;
|
||||||
@@ -868,8 +647,6 @@ const loadDashboardData = async () => {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('加载仪表盘数据失败:', err);
|
console.error('加载仪表盘数据失败:', err);
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1275,11 +1052,6 @@ const handleDeleteItem = (item: Author) => {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.author-card {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
.author-card:hover {
|
.author-card:hover {
|
||||||
transform: translateY(-3px);
|
transform: translateY(-3px);
|
||||||
@@ -1343,7 +1115,6 @@ const handleDeleteItem = (item: Author) => {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
.category-card:hover {
|
.category-card:hover {
|
||||||
transform: translateY(-3px);
|
transform: translateY(-3px);
|
||||||
|
|||||||
Reference in New Issue
Block a user