From ed176b9face08eafd92d71159af3983961cc677f Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 24 Jun 2026 11:16:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8F=91=E5=B8=83=E8=A1=A8?= =?UTF-8?q?=E7=8E=B0=E6=A0=A1=E5=87=86=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/globals.css | 19 +- src/app/page.tsx | 7 + .../performance-calibration-panel.tsx | 214 ++++++++++++++++++ 3 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 src/components/performance-calibration-panel.tsx diff --git a/src/app/globals.css b/src/app/globals.css index 414d24d..07a96d4 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -295,9 +295,26 @@ h3 { padding: 0.25rem 0.55rem; } +.calibration-metrics { + display: grid; + gap: 0.75rem; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.calibration-observations { + border: 1px solid #e5e9f0; + border-radius: 8px; + color: #586174; + display: grid; + gap: 0.45rem; + margin: 0; + padding: 0.75rem 0.75rem 0.75rem 1.4rem; +} + @media (max-width: 900px) { .workflow-grid, - .two-col { + .two-col, + .calibration-metrics { grid-template-columns: 1fr; } diff --git a/src/app/page.tsx b/src/app/page.tsx index c1d738d..d6ef980 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,6 +11,7 @@ import { toConfirmedFactCard, } from "../components/fact-card-editor"; import { OptimizedPreview } from "../components/optimized-preview"; +import { PerformanceCalibrationPanel } from "../components/performance-calibration-panel"; import { ProgressPanel } from "../components/progress-panel"; import { QaReportPanel } from "../components/qa-report-panel"; import type { @@ -270,6 +271,12 @@ export default function Home() { jobId={jobId} /> + ); diff --git a/src/components/performance-calibration-panel.tsx b/src/components/performance-calibration-panel.tsx new file mode 100644 index 0000000..59fd3d9 --- /dev/null +++ b/src/components/performance-calibration-panel.tsx @@ -0,0 +1,214 @@ +"use client"; + +import { useState } from "react"; + +import type { PublishPlatform } from "../lib/domain/types"; + +interface PerformanceCalibrationPanelProps { + apiAccessKey: string; + jobId: string | null; + optimizedRevision: number | null; +} + +interface ScoreResponse { + scoringRun?: { id: string; composite_score: number; rationale: string }; + error?: string; +} + +interface PublicationResponse { + publication?: { id: string; url: string }; + error?: string; +} + +interface PerformanceResponse { + snapshot?: { metrics: Record }; + calibrationEvent?: { observations: string[]; recommended_action: string }; + error?: string; +} + +export function PerformanceCalibrationPanel({ + apiAccessKey, + jobId, + optimizedRevision, +}: PerformanceCalibrationPanelProps) { + const [platform, setPlatform] = useState("official_site"); + const [url, setUrl] = useState(""); + const [publishedAt, setPublishedAt] = useState(() => + new Date().toISOString().slice(0, 16), + ); + const [publicationId, setPublicationId] = useState(null); + const [views, setViews] = useState(""); + const [clicks, setClicks] = useState(""); + const [inquiries, setInquiries] = useState(""); + const [feedbackSummary, setFeedbackSummary] = useState(""); + const [message, setMessage] = useState(""); + const [observations, setObservations] = useState([]); + + const disabled = !jobId || !optimizedRevision; + + async function scoreRevision() { + if (!jobId) return; + setMessage(""); + const response = await fetch(`/api/jobs/${jobId}/calibration/score`, { + method: "POST", + headers: apiHeaders(apiAccessKey), + }); + const body = (await response.json()) as ScoreResponse; + if (!response.ok || !body.scoringRun) { + setMessage(body.error ?? "评分失败"); + return; + } + setMessage( + `校准评分 ${body.scoringRun.composite_score}/10:${body.scoringRun.rationale}`, + ); + } + + async function registerPublication() { + if (!jobId || !publishedAt) return; + setMessage(""); + const response = await fetch(`/api/jobs/${jobId}/publications`, { + method: "POST", + headers: apiHeaders(apiAccessKey), + body: JSON.stringify({ + platform, + url, + published_at: new Date(publishedAt).toISOString(), + notes: "", + }), + }); + const body = (await response.json()) as PublicationResponse; + if (!response.ok || !body.publication) { + setMessage(body.error ?? "发布记录保存失败"); + return; + } + setPublicationId(body.publication.id); + setMessage("发布记录已保存。"); + } + + async function recordPerformance() { + if (!publicationId) return; + setMessage(""); + const response = await fetch(`/api/publications/${publicationId}/performance`, { + method: "POST", + headers: apiHeaders(apiAccessKey), + body: JSON.stringify({ + window_label: "T+7d", + views, + clicks, + inquiries, + feedback_summary: feedbackSummary, + }), + }); + const body = (await response.json()) as PerformanceResponse; + if (!response.ok || !body.calibrationEvent) { + setMessage(body.error ?? "表现数据保存失败"); + return; + } + setObservations(body.calibrationEvent.observations); + setMessage(body.calibrationEvent.recommended_action); + } + + return ( +
+
+ 发布表现校准 + +
+ + + + +
+ + + +
+