diff --git a/src/app/globals.css b/src/app/globals.css index b2f2d42..d0546d3 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -120,6 +120,22 @@ h3 { min-width: min(16rem, 100%); } +.app-tabs { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.app-tabs button { + background: #ffffff; + color: #172033; +} + +.app-tabs button.active-tab { + background: #172033; + color: #ffffff; +} + .workflow-grid { align-items: start; display: grid; @@ -539,8 +555,28 @@ h3 { padding: 0.75rem 0.75rem 0.75rem 1.4rem; } +.copy-optimizer-grid { + align-items: start; + display: grid; + gap: 1rem; + grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); +} + +.copy-result { + background: #f6f7f9; + border: 1px solid #e5e9f0; + border-radius: 6px; + color: #172033; + margin: 0; + min-height: 10rem; + overflow: auto; + padding: 0.8rem; + white-space: pre-wrap; +} + @media (max-width: 900px) { .workflow-grid, + .copy-optimizer-grid, .two-col, .fact-summary-grid, .calibration-metrics { diff --git a/src/app/page.tsx b/src/app/page.tsx index e3b3192..52079e5 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,6 +11,7 @@ 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 { RenweiCopyOptimizerPanel } from "../components/renwei-copy-optimizer-panel"; import type { OptimizationFactCard, OptimizedArticle, @@ -45,6 +46,7 @@ interface TimingSummary { } export default function Home() { + const [activeTab, setActiveTab] = useState<"geo" | "copy">("geo"); const [input, setInput] = useState(initialInput); const [jobId, setJobId] = useState(null); const [factCard, setFactCard] = useState(null); @@ -177,39 +179,63 @@ export default function Home() { /> - -
- - - - - + + + + {activeTab === "geo" ? ( + <> + +
+ + + + + +
+ + ) : ( + -
+ )} ); } diff --git a/src/components/renwei-copy-optimizer-panel.tsx b/src/components/renwei-copy-optimizer-panel.tsx new file mode 100644 index 0000000..9e3a66a --- /dev/null +++ b/src/components/renwei-copy-optimizer-panel.tsx @@ -0,0 +1,184 @@ +"use client"; + +import { useState, type FormEvent } from "react"; + +import type { + CopyOptimizationIntensity, + CopyOptimizationResult, +} from "../lib/domain/types"; + +interface RenweiCopyOptimizerPanelProps { + apiAccessKey: string; +} + +interface CopyOptimizeResponse { + result?: CopyOptimizationResult; + error?: string; +} + +const intensityOptions: Array<{ + value: CopyOptimizationIntensity; + label: string; +}> = [ + { value: "light", label: "轻微整理" }, + { value: "medium", label: "适度润色" }, + { value: "conversational", label: "更口语自然" }, +]; + +export function RenweiCopyOptimizerPanel({ + apiAccessKey, +}: RenweiCopyOptimizerPanelProps) { + const [sourceText, setSourceText] = useState(""); + const [goal, setGoal] = useState("保留原意,减少 AI 味"); + const [intensity, setIntensity] = + useState("light"); + const [userInstructions, setUserInstructions] = useState(""); + const [result, setResult] = useState(null); + const [message, setMessage] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); + + async function submit(event: FormEvent) { + event.preventDefault(); + setIsSubmitting(true); + setMessage(""); + setResult(null); + + try { + const response = await fetch("/api/copy/renwei-optimize", { + method: "POST", + headers: apiHeaders(apiAccessKey), + body: JSON.stringify({ + source_text: sourceText, + goal, + intensity, + user_instructions: userInstructions, + }), + }); + const body = (await response.json()) as CopyOptimizeResponse; + if (!response.ok || !body.result) { + throw new Error(body.error ?? "文案优化失败"); + } + setResult(body.result); + setMessage("文案优化完成。"); + } catch (error) { + setMessage(error instanceof Error ? error.message : "文案优化失败"); + } finally { + setIsSubmitting(false); + } + } + + async function copyResult() { + if (!result?.optimized_text) return; + await navigator.clipboard.writeText(result.optimized_text); + setMessage("已复制优化结果。"); + } + + return ( +
+
+
+ 普通文案优化 + +
+