开屏页面细节增加;聊天页面批量解密/专业补充进度徽章并可在导出页面任务列表查询具体进度

This commit is contained in:
cc committed 2026-07-01 16:52:22 +08:00
1 parent 301cb5abce
commit 82bce9f784
7 files changed
+229 -18

No files matched your search

+3 -2
View File
@@ -120,6 +120,7 @@ function ExportPage() {
resumeTask: resumeBgTask,
cancelTask: cancelBgTask
} = useBackgroundTasks()
const taskCenterBackgroundTasks = backgroundTasks.filter(task => task.sourcePage === 'export' || task.sourcePage === 'chat')
// ── 4. Dialog & Exports ──
const { dialogState, openDialog, closeDialog } = useExportDialog()
@@ -286,13 +287,13 @@ function ExportPage() {
/>
</section>
{(exportTasks.length > 0 || backgroundTasks.filter(t => t.sourcePage === 'export').length > 0) && (
{(exportTasks.length > 0 || taskCenterBackgroundTasks.length > 0) && (
<section className="export-v2-task-panel">
<TaskCenter
exportTasks={exportTasks}
onCancelExportTask={cancelExportTask}
onClearCompletedExportTasks={clearCompletedExportTasks}
backgroundTasks={backgroundTasks.filter(t => t.sourcePage === 'export')}
backgroundTasks={taskCenterBackgroundTasks}
onPauseBackgroundTask={pauseBgTask}
onResumeBackgroundTask={resumeBgTask}
onCancelBackgroundTask={cancelBgTask}
@@ -87,6 +87,20 @@
font-size: 13px;
font-weight: 500;
color: var(--text-primary);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-source-badge {
flex-shrink: 0;
font-size: 11px;
font-weight: 500;
padding: 2px 6px;
border-radius: 4px;
background: color-mix(in srgb, var(--border-color) 55%, transparent);
color: var(--text-tertiary);
}
.task-status-badge {
@@ -164,11 +178,23 @@
.progress-stats {
display: flex;
align-items: center;
gap: 8px;
font-size: 11px;
color: var(--text-secondary);
white-space: nowrap;
min-width: 80px;
justify-content: flex-end;
.phase {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.count {
flex-shrink: 0;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
}
}
@@ -211,6 +237,38 @@
background: #ef4444;
}
}
&.status-completed {
border-color: color-mix(in srgb, #10b981 30%, var(--border-color));
.task-status-badge {
background: color-mix(in srgb, #10b981 15%, transparent);
color: #10b981;
}
.progress-bar-fill {
background: #10b981;
}
}
&.status-failed,
&.status-canceled {
border-color: color-mix(in srgb, #ef4444 30%, var(--border-color));
.task-status-badge {
background: color-mix(in srgb, #ef4444 15%, transparent);
color: #ef4444;
}
.progress-bar-fill {
background: #ef4444;
}
}
&.status-pause_requested,
&.status-paused,
&.status-cancel_requested {
.task-status-badge {
background: color-mix(in srgb, #f59e0b 15%, transparent);
color: #d97706;
}
}
}
.bg-tasks-section {
@@ -229,9 +287,20 @@
}
.bg-task {
.task-progress-text {
font-size: 12px;
color: var(--text-secondary);
.task-info {
min-width: 0;
}
.bg-progress-area {
flex-direction: column;
align-items: stretch;
gap: 6px;
.progress-stats {
width: 100%;
justify-content: space-between;
min-width: 0;
}
}
}
}
@@ -6,7 +6,6 @@ import {
backgroundTaskSourceLabels,
backgroundTaskStatusLabels
} from '../../constants'
import { formatDurationMs } from '../../utils/format'
import './TaskCenter.scss'
interface TaskCenterProps {
@@ -20,6 +19,20 @@ interface TaskCenterProps {
onCancelBackgroundTask: (taskId: string) => void
}
const parseBackgroundTaskProgress = (progressText?: string): { current: number; total: number; percent: number } | null => {
const match = String(progressText || '').match(/(\d[\d,]*)\s*\/\s*(\d[\d,]*)/)
if (!match) return null
const current = Number(match[1].replace(/,/g, ''))
const total = Number(match[2].replace(/,/g, ''))
if (!Number.isFinite(current) || !Number.isFinite(total) || total <= 0) return null
const safeCurrent = Math.max(0, Math.min(current, total))
return {
current: safeCurrent,
total,
percent: Math.max(0, Math.min(100, Math.round((safeCurrent / total) * 100)))
}
}
const TaskCenter: React.FC<TaskCenterProps> = ({
exportTasks,
onCancelExportTask,
@@ -104,13 +117,22 @@ const TaskCenter: React.FC<TaskCenterProps> = ({
const isPausable = bgTask.status === 'running'
const isResumable = bgTask.status === 'paused'
const isCancelable = bgTask.status !== 'completed' && bgTask.status !== 'canceled' && bgTask.status !== 'failed'
const progress = parseBackgroundTaskProgress(bgTask.progressText)
const sourceLabel = backgroundTaskSourceLabels[bgTask.sourcePage] || '后台任务'
return (
<div key={bgTask.id} className={`task-card bg-task status-${bgTask.status}`}>
<div className="task-card-header">
<div className="task-info">
<span className="task-title">{backgroundTaskSourceLabels[bgTask.sourcePage] || '后台任务'}</span>
<span className="task-status-badge">{backgroundTaskStatusLabels[bgTask.status]}</span>
<span className="task-title">{bgTask.title || sourceLabel}</span>
<span className="task-source-badge">{sourceLabel}</span>
<span className="task-status-badge">
{bgTask.status === 'running' && <Loader2 className="spin" size={12} />}
{(bgTask.status === 'completed') && <CheckCircle2 size={12} />}
{(bgTask.status === 'failed' || bgTask.status === 'canceled') && <XCircle size={12} />}
{(bgTask.status === 'pause_requested' || bgTask.status === 'cancel_requested') && <Loader2 className="spin" size={12} />}
{backgroundTaskStatusLabels[bgTask.status]}
</span>
</div>
<div className="task-actions">
{isPausable && (
@@ -130,9 +152,20 @@ const TaskCenter: React.FC<TaskCenterProps> = ({
)}
</div>
</div>
{bgTask.progressText && (
<div className="task-progress-text">
{bgTask.progressText}
{(bgTask.detail || bgTask.progressText) && (
<div className="task-progress-area bg-progress-area">
{progress && (
<div className="progress-bar-bg">
<div
className="progress-bar-fill"
style={{ width: `${progress.percent}%` }}
/>
</div>
)}
<div className="progress-stats">
{bgTask.detail && <span className="phase">{bgTask.detail}</span>}
{bgTask.progressText && <span className="count">{bgTask.progressText}</span>}
</div>
</div>
)}
</div>