feat(frontend): 全部页面接入后端API并同步全局企业选择

- ai-plus: Class组件→函数组件,接入 listHealthScores + listAgentExecutions API
- ooda: 硬编码→接入 risks/weak-signals/sentinels/tasks API 构建OODA各阶段
- threads: 硬编码→接入 risks/tasks/events API 组合决策线程
- today: 硬编码→接入 risks/weak-signals/synergies/reports API 构建行动项
- compare: 硬编码企业名→使用全局企业列表 + listHealthScores + listRisks API
- innovation/knowledge-graph/portfolio: 加 useCompanyScope 标题显示企业名
- 所有页面标题在单企业选择时显示企业名
- 编译验证通过
This commit is contained in:
selfrelease
2026-07-20 08:40:54 +08:00
parent 006fd7de7e
commit af91d843d8
8 changed files with 654 additions and 185 deletions
+96 -9
View File
@@ -1,24 +1,105 @@
/** 决策线程列表页 — 按状态/企业筛选 + 线程卡片。 */
/** 决策线程列表页 — 按状态/企业筛选 + 线程卡片,数据来自后端 API。 */
"use client";
import { useState } from "react";
import { GitBranch } from "lucide-react";
import { ThreadList, type ThreadItem } from "@/components/threads/ThreadList";
import { useCompanyScope, useScopeEffect, useCompanyName } from "@/lib/company-scope";
import { listRisks, type RiskEvent } from "@/lib/risks";
import { listTasks } from "@/lib/api-v2";
import { listMajorEvents } from "@/lib/api-v2";
import { LoadingSpinner } from "@/components/shared/LoadingSpinner";
import { EmptyState } from "@/components/shared/EmptyState";
/** 任务项。 */
interface TaskItem {
id: string;
company_id?: string;
title: string;
status?: string;
}
/** 重大事项项。 */
interface MajorEventItem {
id: string;
company_id?: string;
event_type: string;
title: string;
}
/** 决策线程列表页面。 */
export default function ThreadsPage() {
const threads: ThreadItem[] = [
{ id: "thread-001", title: "云栈数据 A+轮融资时机选择", company: "云栈数据", status: "analyzing", updatedAt: "2026-07-12", riskCount: 2 },
{ id: "thread-002", title: "深瞳智能 CTO retention 策略", company: "深瞳智能", status: "identified", updatedAt: "2026-07-10", riskCount: 1 },
{ id: "thread-003", title: "量子芯微 融资策略:天使+ vs Pre-A", company: "量子芯微", status: "acted", updatedAt: "2026-07-14", riskCount: 3 },
{ id: "thread-004", title: "智链科技 客户流失干预方案", company: "智链科技", status: "closed", updatedAt: "2026-06-28", riskCount: 1 },
];
const { companyName } = useCompanyScope();
const getCompanyName = useCompanyName();
const [threads, setThreads] = useState<ThreadItem[]>([]);
const [isLoading, setIsLoading] = useState(true);
useScopeEffect(() => {
Promise.all([
listRisks({ page_size: 50 }),
listTasks(),
listMajorEvents(),
])
.then(([risksResp, tasksResp, eventsResp]) => {
const risks = (risksResp.data?.items as RiskEvent[]) ?? [];
const tasks = (tasksResp.data as TaskItem[]) ?? [];
const events = (eventsResp.data as MajorEventItem[]) ?? [];
// 将风险、任务、事件组合为决策线程
const combined: ThreadItem[] = [];
// 风险 → 线程
risks.forEach((risk) => {
combined.push({
id: `risk-${risk.id}`,
title: risk.title,
company: getCompanyName(risk.company_id),
status: risk.status === "closed" ? "closed" : risk.status === "in_progress" ? "acted" : risk.status === "assigned" ? "identified" : "analyzing",
updatedAt: risk.updated_at?.slice(0, 10) ?? "",
riskCount: 1,
});
});
// 重大事项 → 线程
events.forEach((event) => {
combined.push({
id: `event-${event.id}`,
title: event.title,
company: getCompanyName(event.company_id),
status: "identified",
updatedAt: "",
riskCount: 0,
});
});
// 任务 → 线程
tasks.forEach((task) => {
combined.push({
id: `task-${task.id}`,
title: task.title,
company: getCompanyName(task.company_id),
status: task.status === "completed" ? "closed" : task.status === "in_progress" ? "acted" : "identified",
updatedAt: "",
riskCount: 0,
});
});
setThreads(combined.slice(0, 20));
})
.catch(() => {
setThreads([]);
})
.finally(() => setIsLoading(false));
});
return (
<div className="space-y-6">
<div className="flex items-center gap-2">
<GitBranch className="text-[var(--investor-primary)]" size={24} />
<h1 className="text-2xl font-bold">线</h1>
<h1 className="text-2xl font-bold">
线{companyName ? `${companyName}` : ""}
</h1>
</div>
<div className="rounded-lg border bg-white p-4 shadow-sm">
@@ -27,7 +108,13 @@ export default function ThreadsPage() {
</p>
</div>
<ThreadList threads={threads} />
{isLoading ? (
<LoadingSpinner />
) : threads.length === 0 ? (
<EmptyState description="暂无决策线程" />
) : (
<ThreadList threads={threads} />
)}
</div>
);
}