Template
Complete frontend console redesign
This commit is contained in:
@@ -115,7 +115,7 @@
|
||||
:disabled="!canStart"
|
||||
@click="startIngest"
|
||||
>
|
||||
<app-icon name="play" :size="16" filled />
|
||||
<app-icon name="play" :size="16" :filled="true" />
|
||||
<span>执行一键入库</span>
|
||||
</button>
|
||||
<button
|
||||
@@ -288,14 +288,21 @@
|
||||
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import AppIcon from './common/AppIcon.vue';
|
||||
import { getConfig } from '../api/config';
|
||||
import { startIngest as apiStartIngest, getIngestStatus, getIngestCurrentStatus } from '../api/ingest';
|
||||
import type { IngestStatusResponse } from '../api/ingest';
|
||||
import { useWebSocket } from '../composables/useWebSocket';
|
||||
import type { ProgressMessage } from '../composables/useWebSocket';
|
||||
import { useAppState } from '../composables/useAppState';
|
||||
|
||||
const { state: appState, isConfigured, refreshConfig, setIngestRunning, setIngestPercentage } = useAppState();
|
||||
const {
|
||||
state: appState,
|
||||
isConfigured,
|
||||
refreshConfig,
|
||||
setIngestRunning,
|
||||
setIngestPercentage,
|
||||
claimIngestViewOwnership,
|
||||
releaseIngestViewOwnership
|
||||
} = useAppState();
|
||||
|
||||
const submitting = ref(false);
|
||||
const configLoading = ref(false);
|
||||
@@ -330,10 +337,12 @@ const percentage = computed(() => {
|
||||
return Math.min(100, Math.round((progress.processed / progress.total) * 100));
|
||||
});
|
||||
|
||||
/** 被拒绝类别之和(不含已入库)。 */
|
||||
/**
|
||||
* 被拒绝类别之和(不含已入库、不含重复跳过)。
|
||||
* 重复文件是设计上的跳过,不计入拒绝/部分失败。
|
||||
*/
|
||||
const totalRejected = computed(() =>
|
||||
(progress.duplicateFiles ?? 0)
|
||||
+ (progress.missingMetadataFiles ?? 0)
|
||||
(progress.missingMetadataFiles ?? 0)
|
||||
+ (progress.unreadableFiles ?? 0)
|
||||
+ (progress.conversionFailedFiles ?? 0)
|
||||
+ (progress.otherRejectedFiles ?? 0)
|
||||
@@ -393,7 +402,11 @@ const resultText = computed(() => {
|
||||
if (totalRejected.value > 0) {
|
||||
return `导入完成:成功入库 ${progress.ingestedFiles ?? 0} 首,${totalRejected.value} 首被拒绝`;
|
||||
}
|
||||
return `导入完成:全部 ${progress.processed ?? 0} 首成功入库`;
|
||||
const dups = progress.duplicateFiles ?? 0;
|
||||
if (dups > 0) {
|
||||
return `导入完成:成功入库 ${progress.ingestedFiles ?? 0} 首,${dups} 首重复跳过`;
|
||||
}
|
||||
return `导入完成:全部 ${progress.ingestedFiles ?? progress.processed ?? 0} 首成功入库`;
|
||||
});
|
||||
|
||||
/* ---------------- 活动记录(仅由真实状态转移构建) ---------------- */
|
||||
@@ -439,9 +452,20 @@ function classifyLevel(msg: ProgressMessage): { level: ActivityLevel; badge: str
|
||||
if (text.includes('损坏') || text.includes('不可读') || text.includes('转码失败')) {
|
||||
return { level: 'error', badge: '[错误]' };
|
||||
}
|
||||
// 重复/跳过按设计是警告级事件记录,但不计入完成态「拒绝」统计。
|
||||
if (text.includes('重复') || text.includes('跳过') || text.includes('缺') || text.includes('拒绝')) {
|
||||
return { level: 'warning', badge: '[警告]' };
|
||||
}
|
||||
if (msg.completed) {
|
||||
// 完成分类仅看真实拒绝类别,不含 duplicateFiles。
|
||||
const rejected =
|
||||
(msg.missingMetadataFiles ?? 0)
|
||||
+ (msg.unreadableFiles ?? 0)
|
||||
+ (msg.conversionFailedFiles ?? 0)
|
||||
+ (msg.otherRejectedFiles ?? 0);
|
||||
if (rejected > 0) return { level: 'warning', badge: '[部分]' };
|
||||
return { level: 'normal', badge: '[完成]' };
|
||||
}
|
||||
return { level: 'normal', badge: '[进行]' };
|
||||
}
|
||||
|
||||
@@ -678,6 +702,9 @@ async function copyPath(text: string) {
|
||||
/* ---------------- 生命周期 ---------------- */
|
||||
|
||||
onMounted(async () => {
|
||||
// 接管细粒度进度源,暂停外壳后台监视(避免双重 HTTP 轮询)。
|
||||
claimIngestViewOwnership();
|
||||
|
||||
// 若外壳尚未加载配置,则此处兜底加载真值。
|
||||
if (!appState.config.attempted) {
|
||||
await reloadConfig();
|
||||
@@ -688,8 +715,8 @@ onMounted(async () => {
|
||||
onUnmounted(() => {
|
||||
wsDisconnect();
|
||||
stopPolling();
|
||||
// 组件卸载不代表任务停止,但本视图不再持有运行指示。
|
||||
setIngestRunning(false);
|
||||
// 交还所有权:若任务仍在跑,由外壳后台监视继续同步真实完成态。
|
||||
releaseIngestViewOwnership(submitting.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1476,5 +1503,32 @@ onUnmounted(() => {
|
||||
.stats-secondary {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.console-head {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 390px) {
|
||||
.ingest-view {
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.stat-num {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.log-line {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user