feat(wordcloud): 收口在途开发(布局/存储/前端)+ R4 WCD 生产任务(jobs wcd_file)与生产订单列表
This commit is contained in:
@@ -1,12 +1,33 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { SSEProgress } from '../types';
|
||||
import { IconCross, IconCheckmark, IconGear } from './Icons';
|
||||
import { IconCross, IconCheckmark, IconGear, IconCopy } from './Icons';
|
||||
|
||||
interface ProgressPanelProps {
|
||||
progress: SSEProgress | null;
|
||||
logLines: string[];
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
export default function ProgressPanel({ progress, visible }: ProgressPanelProps) {
|
||||
export default function ProgressPanel({ progress, logLines, visible }: ProgressPanelProps) {
|
||||
const logEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Auto-scroll the log view to the bottom whenever new lines arrive.
|
||||
useEffect(() => {
|
||||
logEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||
}, [logLines]);
|
||||
|
||||
// All hooks MUST run before any early return, so derive the values the
|
||||
// callback needs without depending on `progress` being non-null.
|
||||
const message = progress?.message ?? '';
|
||||
|
||||
const handleCopyError = useCallback(() => {
|
||||
const text = logLines.length > 0 ? logLines.join('\n') : message;
|
||||
navigator.clipboard.writeText(text).catch(() => {
|
||||
const el = document.querySelector('.error-detail-textarea') as HTMLTextAreaElement | null;
|
||||
if (el) { el.select(); document.execCommand('copy'); }
|
||||
});
|
||||
}, [logLines, message]);
|
||||
|
||||
if (!visible || !progress) return null;
|
||||
|
||||
const isFailed = progress.stage === '生成失败' || progress.stage === '错误';
|
||||
@@ -20,6 +41,13 @@ export default function ProgressPanel({ progress, visible }: ProgressPanelProps)
|
||||
<div className="progress-stage" style={isFailed ? { color: 'var(--danger)' } : {}}>
|
||||
{progress.stage}
|
||||
</div>
|
||||
{(progress.elapsedSeconds != null || progress.clientElapsedSeconds != null) && (
|
||||
<div className="progress-timing" style={{ color: isDone ? 'var(--success)' : 'var(--text-muted)' }}>
|
||||
{progress.elapsedSeconds != null && `后端耗时 ${progress.elapsedSeconds.toFixed(2)} 秒`}
|
||||
{progress.elapsedSeconds != null && progress.clientElapsedSeconds != null ? ' · ' : ''}
|
||||
{progress.clientElapsedSeconds != null && `前端显示耗时 ${progress.clientElapsedSeconds.toFixed(2)} 秒`}
|
||||
</div>
|
||||
)}
|
||||
{!isFailed && (
|
||||
<div className="progress-bar-track">
|
||||
<div
|
||||
@@ -31,6 +59,33 @@ export default function ProgressPanel({ progress, visible }: ProgressPanelProps)
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* ── 详细日志(实时滚动) ───────────────────────────────── */}
|
||||
{logLines.length > 0 && (
|
||||
<div
|
||||
className="progress-log-view"
|
||||
style={{
|
||||
marginTop: 8,
|
||||
maxHeight: 320,
|
||||
overflowY: 'auto',
|
||||
background: 'var(--bg-secondary, #1a1a2e)',
|
||||
borderRadius: 6,
|
||||
padding: '8px 10px',
|
||||
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
|
||||
fontSize: 12,
|
||||
lineHeight: 1.6,
|
||||
color: 'var(--text-muted, #888)',
|
||||
border: '1px solid var(--border-color, #333)',
|
||||
}}
|
||||
>
|
||||
{logLines.map((line, i) => (
|
||||
<div key={i} style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>
|
||||
{line}
|
||||
</div>
|
||||
))}
|
||||
<div ref={logEndRef} />
|
||||
</div>
|
||||
)}
|
||||
{/* ── 失败时的错误详情 + 复制按钮 ───────────────────────── */}
|
||||
<div
|
||||
className="progress-message"
|
||||
style={{
|
||||
@@ -41,7 +96,19 @@ export default function ProgressPanel({ progress, visible }: ProgressPanelProps)
|
||||
marginTop: isFailed ? 8 : 0,
|
||||
}}
|
||||
>
|
||||
{progress.message}
|
||||
{isFailed ? (
|
||||
<div className="error-detail-container">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary btn-sm"
|
||||
onClick={handleCopyError}
|
||||
title="复制完整日志"
|
||||
style={{ marginBottom: 6, display: 'inline-flex', alignItems: 'center', gap: 4 }}
|
||||
>
|
||||
<IconCopy /> 复制完整日志
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user