feat(会商第一步): 分析记录按业务链筛选(漏斗下拉, 选项从草稿chainTitle去重)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
368673c3b4
commit
27fffbec61
1 file changed
+95
-2
@@ -40,6 +40,7 @@ import {
|
||||
Clock,
|
||||
Cpu,
|
||||
Download,
|
||||
Filter,
|
||||
History,
|
||||
Layers,
|
||||
ListOrdered,
|
||||
@@ -61,6 +62,14 @@ import {
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { useArtifacts } from "@/components/workspace/artifacts";
|
||||
import { useSidebarSafe } from "@/components/ui/sidebar";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { desensitize } from "@/lib/desensitize";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useDesensitizeSubtree } from "@/roundtable-planning/lib/use-desensitize-subtree";
|
||||
@@ -2860,6 +2869,10 @@ function getChainStageGroups(chain: BusinessChain): BusinessChain["seats"][] {
|
||||
return chain.seats.map((s) => [s]);
|
||||
}
|
||||
|
||||
/** 分析记录「按业务链筛选」的两个特殊值:全部 / 无业务链(自由模式)。 */
|
||||
const RECORD_FILTER_ALL = "__all__";
|
||||
const RECORD_FILTER_FREE = "__free__";
|
||||
|
||||
/**
|
||||
* 需求 0616 #2:多智能体会商 Step1(落地)左栏入口栏。
|
||||
* - 业务链:仅公共链条(``listChains("public")``)前 3 个;点击以该链席位启动会商。
|
||||
@@ -2886,6 +2899,38 @@ function RoundtableEntryRail({
|
||||
const [chains, setChains] = useState<BusinessChain[]>([]);
|
||||
const [expandedChainId, setExpandedChainId] = useState<string | null>(null);
|
||||
const { drafts: records, refresh } = useRoundtableDrafts(taskId);
|
||||
// 分析记录「按业务链筛选」—— 选项从已加载草稿的 chainTitle 去重得到,
|
||||
// 无业务链的草稿(自由 / recommend 模式)归入「自由模式」。
|
||||
const [chainFilter, setChainFilter] = useState<string>(RECORD_FILTER_ALL);
|
||||
const chainFilterOptions = useMemo(() => {
|
||||
const titles = new Set<string>();
|
||||
let hasFree = false;
|
||||
for (const rec of records) {
|
||||
const title = rec.chainTitle?.trim();
|
||||
if (title) titles.add(title);
|
||||
else hasFree = true;
|
||||
}
|
||||
const opts = Array.from(titles)
|
||||
.sort((a, b) => a.localeCompare(b, "zh-CN"))
|
||||
.map((title) => ({ value: title, label: title }));
|
||||
if (hasFree) opts.push({ value: RECORD_FILTER_FREE, label: "自由模式" });
|
||||
return opts;
|
||||
}, [records]);
|
||||
const filteredRecords = useMemo(() => {
|
||||
if (chainFilter === RECORD_FILTER_ALL) return records;
|
||||
if (chainFilter === RECORD_FILTER_FREE)
|
||||
return records.filter((rec) => !rec.chainTitle?.trim());
|
||||
return records.filter((rec) => rec.chainTitle?.trim() === chainFilter);
|
||||
}, [records, chainFilter]);
|
||||
// 草稿刷新后所选业务链可能已不存在(被删/改名)→ 回落到「全部」,避免列表空白无解。
|
||||
useEffect(() => {
|
||||
if (
|
||||
chainFilter !== RECORD_FILTER_ALL &&
|
||||
!chainFilterOptions.some((opt) => opt.value === chainFilter)
|
||||
) {
|
||||
setChainFilter(RECORD_FILTER_ALL);
|
||||
}
|
||||
}, [chainFilter, chainFilterOptions]);
|
||||
// 深链接(taskId)模式:左上角不显示「业务链选择」(链条已由 businessCode 自动决定),改为展示
|
||||
// 当前任务(rwfx/3qfx)或当前行动(xdfx)。xdfx 的根是「行动」,故展示行动名称 + 叙事。
|
||||
const deeplink = Boolean(taskId);
|
||||
@@ -3159,13 +3204,59 @@ function RoundtableEntryRail({
|
||||
<History className="w-3 h-3" strokeWidth={2.5} />
|
||||
</span>
|
||||
<h2 className="text-sm font-semibold tracking-wide text-slate-900">分析记录</h2>
|
||||
{/* 按业务链筛选 —— 仅当存在多于 1 个可选业务链时才显示图标按钮。 */}
|
||||
{chainFilterOptions.length > 1 && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
title="按业务链筛选"
|
||||
aria-label="按业务链筛选"
|
||||
className={cn(
|
||||
"ml-auto flex h-6 w-6 shrink-0 items-center justify-center rounded-md border transition-colors",
|
||||
chainFilter === RECORD_FILTER_ALL
|
||||
? "border-[#ECE7E4] text-neutral-400 hover:border-primary/30 hover:text-primary"
|
||||
: "border-primary/30 bg-primary/5 text-primary",
|
||||
)}
|
||||
>
|
||||
<Filter className="h-3 w-3" strokeWidth={2.5} />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="min-w-[12rem]">
|
||||
<DropdownMenuLabel className="text-[16px] text-neutral-500">
|
||||
按业务链筛选
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuRadioGroup
|
||||
value={chainFilter}
|
||||
onValueChange={setChainFilter}
|
||||
>
|
||||
<DropdownMenuRadioItem value={RECORD_FILTER_ALL} className="text-[16px]">
|
||||
全部
|
||||
</DropdownMenuRadioItem>
|
||||
{chainFilterOptions.map((opt) => (
|
||||
<DropdownMenuRadioItem
|
||||
key={opt.value}
|
||||
value={opt.value}
|
||||
className="text-[16px]"
|
||||
>
|
||||
<span className="truncate">{opt.label}</span>
|
||||
</DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
{records.length === 0 ? (
|
||||
<p className="flex-1 text-xs text-neutral-400 text-center py-8">暂无分析记录</p>
|
||||
) : filteredRecords.length === 0 ? (
|
||||
<p className="flex-1 text-xs text-neutral-400 text-center py-8">
|
||||
该业务链下暂无分析记录
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="space-y-2 overflow-y-auto flex-1 px-1 py-0.5 custom-scrollbar">
|
||||
{records.map((rec) => {
|
||||
{filteredRecords.map((rec) => {
|
||||
// 已完成:进行到第 3 步(执行过方案总结报告)
|
||||
const completed = rec.furthestStep === 3;
|
||||
// 选中:当前已加载的草稿(正在查看的那条)
|
||||
@@ -3230,7 +3321,9 @@ function RoundtableEntryRail({
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-2 pt-2 border-t border-neutral-100 text-center text-[11px] text-neutral-400">
|
||||
共 {records.length} 条分析记录
|
||||
{chainFilter === RECORD_FILTER_ALL
|
||||
? `共 ${records.length} 条分析记录`
|
||||
: `筛选出 ${filteredRecords.length} / ${records.length} 条分析记录`}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user