改造一键流式优化首页

This commit is contained in:
czj
2026-07-01 13:25:07 +08:00
parent 8a1ceb3b14
commit 016b653b76
5 changed files with 621 additions and 351 deletions
+180 -113
View File
@@ -1,12 +1,13 @@
"use client";
import type { CandidateFactCard, ConfirmedFactCard } from "../lib/domain/types";
import { useState } from "react";
import type { CandidateFactCard, OptimizationFactCard } from "../lib/domain/types";
interface FactCardEditorProps {
factCard: CandidateFactCard | null;
factCard: OptimizationFactCard | null;
isSaving: boolean;
onChange: (factCard: CandidateFactCard) => void;
onConfirm: () => void;
onChange: (factCard: OptimizationFactCard) => void;
}
const listFields = [
@@ -47,30 +48,35 @@ export function FactCardEditor({
factCard,
isSaving,
onChange,
onConfirm,
}: FactCardEditorProps) {
const [isEditing, setIsEditing] = useState(false);
if (!factCard) {
return (
<section className="panel empty-panel">
<div className="panel-heading"></div>
<section className="panel compact-panel fact-card-panel empty-panel">
<div className="panel-heading">
<span></span>
<span className="status-pill warn"></span>
</div>
</section>
);
}
const currentFactCard = factCard;
const canOptimize = currentFactCard.uncertain_items.length === 0;
const hasUncertainItems = factCard.uncertain_items.length > 0;
function update<K extends keyof CandidateFactCard>(
function update<K extends keyof OptimizationFactCard>(
key: K,
value: CandidateFactCard[K],
value: OptimizationFactCard[K],
) {
if (!factCard) return;
onChange({
...currentFactCard,
...factCard,
[key]: value,
confirmed_by_user: false,
is_ready_for_optimization:
key === "uncertain_items"
? (value as string[]).length === 0
: currentFactCard.uncertain_items.length === 0,
: factCard.uncertain_items.length === 0,
});
}
@@ -78,122 +84,183 @@ export function FactCardEditor({
itemIndex: number,
target: UncertainItemResolutionTarget,
) {
onChange(resolveUncertainItem(currentFactCard, itemIndex, target));
if (!factCard) return;
onChange(resolveUncertainItem(factCard, itemIndex, target));
}
return (
<section className="panel stack">
<section className="panel compact-panel fact-card-panel stack">
<div className="panel-heading">
<span></span>
<button disabled={!canOptimize || isSaving} onClick={onConfirm} type="button">
{isSaving ? "保存中..." : "确认事实卡"}
</button>
<div className="panel-actions">
<span className={`status-pill ${hasUncertainItems ? "warn" : "pass"}`}>
{hasUncertainItems ? "待复核" : "可用"}
</span>
<button
className="secondary-button"
disabled={isSaving}
onClick={() => setIsEditing((editing) => !editing)}
type="button"
>
{isEditing ? "收起编辑" : "编辑事实卡"}
</button>
</div>
</div>
<label>
<span></span>
<input
value={factCard.company_full_name}
onChange={(event) => update("company_full_name", event.target.value)}
/>
</label>
<div className="two-col">
<label>
<span></span>
<input
value={factCard.target_industry}
onChange={(event) => update("target_industry", event.target.value)}
/>
</label>
<label>
<span></span>
<input
value={factCard.target_audience}
onChange={(event) => update("target_audience", event.target.value)}
/>
</label>
<div className="fact-summary-grid">
<div>
<span></span>
<strong>{factCard.company_full_name || "待补充"}</strong>
</div>
<div>
<span></span>
<strong>{factCard.target_industry || "待补充"}</strong>
</div>
<div>
<span></span>
<strong>{factCard.target_audience || "待补充"}</strong>
</div>
<div>
<span></span>
<strong>
{factCard.experience_years === null
? "待补充"
: `${factCard.experience_years}`}
</strong>
</div>
</div>
<label>
<span></span>
<input
min="0"
type="number"
value={factCard.experience_years ?? ""}
onChange={(event) =>
update(
"experience_years",
event.target.value ? Number(event.target.value) : null,
)
}
/>
</label>
{listFields.map((field) => (
<label key={field}>
<span>{fieldLabels[field]}</span>
<textarea
value={factCard[field].join("\n")}
onChange={(event) =>
update(
field,
event.target.value
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean) as CandidateFactCard[typeof field],
)
}
/>
</label>
))}
<div className="field-group">
<div className="field-label"></div>
{factCard.uncertain_items.length > 0 ? (
<div className="uncertain-list">
{factCard.uncertain_items.map((item, index) => (
<div className="uncertain-row" key={`${item}-${index}`}>
<p>{item}</p>
<div className="uncertain-actions">
{uncertainItemActions.map((action) => (
<button
className="secondary-button"
key={action.target}
onClick={() => resolveItem(index, action.target)}
type="button"
>
{action.label}
</button>
))}
</div>
</div>
{factCard.core_claims.length > 0 && (
<div className="fact-compact-list">
<span className="field-label"></span>
<div className="tag-row">
{factCard.core_claims.slice(0, 4).map((claim) => (
<span className="tag" key={claim}>
{claim}
</span>
))}
</div>
) : (
<p className="status-text pass"></p>
)}
</div>
{!canOptimize && (
<p className="status-text fail">
</p>
</div>
)}
{hasUncertainItems && (
<div className="fact-compact-list">
<span className="field-label"></span>
<div className="tag-row">
{factCard.uncertain_items.slice(0, 4).map((item) => (
<span className="tag warn-tag" key={item}>
{item}
</span>
))}
</div>
</div>
)}
{isEditing && (
<div className="fact-edit-grid">
<label>
<span></span>
<input
disabled={isSaving}
value={factCard.company_full_name}
onChange={(event) =>
update("company_full_name", event.target.value)
}
/>
</label>
<div className="two-col">
<label>
<span></span>
<input
disabled={isSaving}
value={factCard.target_industry}
onChange={(event) =>
update("target_industry", event.target.value)
}
/>
</label>
<label>
<span></span>
<input
disabled={isSaving}
value={factCard.target_audience}
onChange={(event) =>
update("target_audience", event.target.value)
}
/>
</label>
</div>
<label>
<span></span>
<input
disabled={isSaving}
min="0"
type="number"
value={factCard.experience_years ?? ""}
onChange={(event) =>
update(
"experience_years",
event.target.value ? Number(event.target.value) : null,
)
}
/>
</label>
{listFields.map((field) => (
<label key={field}>
<span>{fieldLabels[field]}</span>
<textarea
disabled={isSaving}
value={factCard[field].join("\n")}
onChange={(event) =>
update(
field,
event.target.value
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean) as OptimizationFactCard[typeof field],
)
}
/>
</label>
))}
<div className="field-group">
<div className="field-label"></div>
{factCard.uncertain_items.length > 0 ? (
<div className="uncertain-list">
{factCard.uncertain_items.map((item, index) => (
<div className="uncertain-row" key={`${item}-${index}`}>
<p>{item}</p>
<div className="uncertain-actions">
{uncertainItemActions.map((action) => (
<button
className="secondary-button"
disabled={isSaving}
key={action.target}
onClick={() => resolveItem(index, action.target)}
type="button"
>
{action.label}
</button>
))}
</div>
</div>
))}
</div>
) : (
<p className="status-text pass"></p>
)}
</div>
</div>
)}
</section>
);
}
export function toConfirmedFactCard(
factCard: CandidateFactCard,
): ConfirmedFactCard {
return {
...factCard,
uncertain_items: [],
is_ready_for_optimization: true,
confirmed_by_user: true,
};
}
export function resolveUncertainItem(
factCard: CandidateFactCard,
export function resolveUncertainItem<T extends CandidateFactCard>(
factCard: T,
itemIndex: number,
target: UncertainItemResolutionTarget,
): CandidateFactCard {
): T {
const item = factCard.uncertain_items[itemIndex]?.trim();
if (!item) return factCard;