feat(client): show digital artifact sources

This commit is contained in:
baiyanyun
2026-06-08 10:31:36 +08:00
parent f4af77cb2b
commit 700f6a40b8
9 changed files with 294 additions and 141 deletions

View File

@@ -8,6 +8,7 @@ import {
} from '@/lib/api';
import type {
DigitalEmployeeDailyReport,
DigitalEmployeeReportArtifactSource,
DigitalEmployeeRunDetail,
DigitalEmployeeTaskExecutionSummary,
DigitalEmployeeWorkdayProjection,
@@ -94,6 +95,7 @@ function buildReportDownloadText(
report: DigitalEmployeeDailyReport,
sections: DigitalEmployeeTaskExecutionSummary[],
details: DigitalEmployeeRunDetail[],
artifacts: DigitalEmployeeReportArtifactSource[],
): string {
return [
report.title || `${date} 数字员工日报`,
@@ -121,6 +123,11 @@ function buildReportDownloadText(
? details.map((detail) => `- ${detail.time || '--:--'} ${detail.task_name} / ${detail.step_name || detail.operation_name || '任务执行'}${detail.status_label}${detail.business_detail}`)
: ['暂无运行明细。']),
'',
'## 产物来源',
...(artifacts.length > 0
? artifacts.map((artifact) => `- ${artifact.name}${artifact.source_label || artifact.source_type || '未知来源'}${artifact.uri ? `${artifact.uri}` : ''}`)
: ['暂无产物来源。']),
'',
'## 详情',
...((report.detail_lines ?? []).length > 0 ? report.detail_lines : ['暂无详情。']),
].join('\n');
@@ -173,6 +180,24 @@ function DetailList({ details }: { details: DigitalEmployeeRunDetail[] }) {
);
}
function ArtifactSourceList({ artifacts }: { artifacts: DigitalEmployeeReportArtifactSource[] }) {
if (artifacts.length === 0) {
return <p className="de-empty-text" style={{ padding: '18px 0 6px' }}></p>;
}
return (
<div className="de-artifact-source-list" aria-label="日报产物来源">
{artifacts.map((artifact) => (
<div key={artifact.id} className="de-artifact-source-row">
<strong>{artifact.name}</strong>
<span>{artifact.source_label || artifact.source_type || '未知来源'}</span>
{artifact.uri ? <code>{artifact.uri}</code> : <em>{artifact.kind || 'artifact'}</em>}
</div>
))}
</div>
);
}
export default function DailyReport({ focusSection }: { focusSection?: string | null }) {
const today = useMemo(() => todayInputDate(), []);
const [projection, setProjection] = useState<DigitalEmployeeWorkdayProjection | null>(null);
@@ -186,6 +211,7 @@ export default function DailyReport({ focusSection }: { focusSection?: string |
const report = isLiveProjection ? (projection?.daily_report ?? null) : null;
const taskSections = isLiveProjection ? (report?.task_sections ?? projection?.execution_summaries ?? []) : [];
const runDetails = isLiveProjection ? (projection?.run_details ?? []) : [];
const artifactSources = isLiveProjection ? (report?.artifact_sources ?? []) : [];
const sourceNotice = isLiveProjection ? null : (projection?.demo_reason ?? '当前没有真实运行投影,日报区域暂不展示兜底数据。');
const totals = report?.totals ?? {
task_count: taskSections.length,
@@ -272,8 +298,8 @@ export default function DailyReport({ focusSection }: { focusSection?: string |
const downloadDailyReport = useCallback(() => {
if (!report || report.status !== 'ready') return;
downloadTextFile(reportFileName(today), buildReportDownloadText(today, report, taskSections, runDetails));
}, [report, runDetails, taskSections, today]);
downloadTextFile(reportFileName(today), buildReportDownloadText(today, report, taskSections, runDetails, artifactSources));
}, [artifactSources, report, runDetails, taskSections, today]);
return (
<div style={{ padding: '14px 18px' }}>
@@ -355,6 +381,10 @@ export default function DailyReport({ focusSection }: { focusSection?: string |
<h3 className="de-settings-heading" style={{ marginTop: 0 }}></h3>
<DetailList details={runDetails} />
</section>
<section id="report-section-artifacts" style={panelStyle}>
<h3 className="de-settings-heading" style={{ marginTop: 0 }}></h3>
<ArtifactSourceList artifacts={artifactSources} />
</section>
<section id="report-section-summary" style={panelStyle}>
<h3 className="de-settings-heading" style={{ marginTop: 0 }}></h3>
{(report?.detail_lines ?? []).length === 0 ? (