前端文案挑选页按 A/B/C 三套分组展示
- 后端 _fmt_text 返回 strategy 字段,SSE 与 hydrate 两条路径贯通 - 新建 TextStrategyGroup 组件 + groupByStrategy(A/B/C/未分套排序,旧 NULL 数据归未分套) - text 页平铺改分组渲染,选中态/改稿回调透传不变 - 复用 ImageStrategyGroup 的 STRATEGY_LABEL,单向依赖无环 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
62
frontend/src/components/tasks/TextStrategyGroup.tsx
Normal file
62
frontend/src/components/tasks/TextStrategyGroup.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
'use client';
|
||||
/**
|
||||
* TextStrategyGroup — 文案按"套"(A/B/C)分组展示
|
||||
* A8(倩倩姐2026-06-18拍板"按三套分组展示"):让运营一眼看出三套不同角度,
|
||||
* 与图片侧 ImageStrategyGroup 同轴(套1痛点/套2场景/套3成分背书),复用 STRATEGY_LABEL。
|
||||
* 无 strategy 的旧数据归入"未分套"组,向后兼容不丢卡。
|
||||
*/
|
||||
import { TextCandidate } from '@/types/index';
|
||||
import { TextCandidateCard } from './TextCandidateCard';
|
||||
import { STRATEGY_LABEL } from './ImageStrategyGroup';
|
||||
|
||||
interface TextStrategyGroupProps {
|
||||
strategy: string; // 'A'|'B'|'C'|'_'(未分套)
|
||||
candidates: TextCandidate[];
|
||||
selectedTextIds: number[];
|
||||
onToggle: (id: number) => void;
|
||||
onSaveEdit?: (id: number, content: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export function TextStrategyGroup({
|
||||
strategy, candidates, selectedTextIds, onToggle, onSaveEdit,
|
||||
}: TextStrategyGroupProps) {
|
||||
const label = strategy === '_' ? '未分套' : (STRATEGY_LABEL[strategy] || `套${strategy}`);
|
||||
return (
|
||||
<section className="mb-6">
|
||||
<h3 className="mb-3 flex items-center text-sm font-semibold text-text-primary">
|
||||
{label}
|
||||
<span className="ml-2 text-xs font-normal text-text-tertiary">
|
||||
{candidates.length} 条
|
||||
</span>
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 xl:grid-cols-3 gap-4">
|
||||
{candidates.map((c) => (
|
||||
<TextCandidateCard
|
||||
key={c.candidate_id}
|
||||
candidate={c}
|
||||
selected={selectedTextIds.includes(c.candidate_id)}
|
||||
onToggle={onToggle}
|
||||
onSaveEdit={onSaveEdit}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// 把平铺候选按 strategy 分组,返回有序 [strategy, candidates][](A/B/C 固定序,未分套垫底)。
|
||||
// 仅含真实存在的套(不硬塞空套),避免某套还没生成出来时显示空壳。
|
||||
export function groupByStrategy(
|
||||
candidates: TextCandidate[],
|
||||
): Array<[string, TextCandidate[]]> {
|
||||
const order = ['A', 'B', 'C', '_'];
|
||||
const buckets = new Map<string, TextCandidate[]>();
|
||||
for (const c of candidates) {
|
||||
const key = c.strategy ?? '_';
|
||||
if (!buckets.has(key)) buckets.set(key, []);
|
||||
buckets.get(key)!.push(c);
|
||||
}
|
||||
return order
|
||||
.filter((k) => buckets.has(k))
|
||||
.map((k) => [k, buckets.get(k) ?? []] as [string, TextCandidate[]]);
|
||||
}
|
||||
Reference in New Issue
Block a user