@@ -1,7 +1,7 @@
import { useEffect , useMemo , useRef , useState } from "react" ;
import IncomeDistribution from "./IncomeDistribution" ;
import {
categorySummary , evidenceUrl , type ResearchEvidence , type ResearchSnapshot ,
categorySummary , evidenceUrl , technicalReviewLabels , type ResearchEvidence , type ResearchSnapshot ,
} from "@/lib/ai-research" ;
const pageSize = 12 ;
@@ -41,6 +41,7 @@ export default function AICompanyResearch() {
const [ sourcePage , setSourcePage ] = useState ( 0 ) ;
const [ depth , setDepth ] = useState ( "" ) ;
const [ teamBand , setTeamBand ] = useState ( "" ) ;
const [ technicalLevel , setTechnicalLevel ] = useState ( "" ) ;
const [ selected , setSelected ] = useState < string | null > ( null ) ;
const [ tab , setTab ] = useState < "companies" | "revenue" | "sources" | "reviews" > ( "companies" ) ;
const companySection = useRef < HTMLElement > ( null ) ;
@@ -63,9 +64,9 @@ export default function AICompanyResearch() {
const depthMatch = ! depth || ( depth === "profile" ? c . profileOnly : ! c . profileOnly ) ;
const teamMatch = ! teamBand || ( teamBand === "unknown" ? c . team === null || ( ! ! c . teamBound && c . team < 51 )
: c . team !== null && ( teamBand === "xl" ? c . team >= 51 : ! c . teamBound && ( teamBand === "small" ? c . team <= 5 : teamBand === "medium" ? c . team >= 6 && c . team <= 20 : c.team >= 21 && c . team <= 50 ) ) ) ;
return depthMatch && teamMatch && ( ! category || c . cat === category ) && yearMatch && ( ! income || c . revenueStatus === income )
return ( ! technicalLevel || ( c . technicalReview ? . level ? ? "unreviewed" ) === technicalLevel ) && depthMatch && teamMatch && ( ! category || c . cat === category ) && yearMatch && ( ! income || c . revenueStatus === income )
&& search . includes ( query . trim ( ) . toLocaleLowerCase ( ) ) ;
} ) ? ? [ ] , [ data , query , category , cohort , income , depth , teamBand ] ) ;
} ) ? ? [ ] , [ data , query , category , cohort , income , depth , teamBand , technicalLevel ] ) ;
if ( error ) return < div className = "research-message" role = "alert" > 研 究 数 据 库 暂 时 不 可 用 。 < button onClick = { ( ) = > { setError ( false ) ; setAttempt ( ( n ) = > n + 1 ) ; } } > 重 新 读 取 < / button > < / div > ;
if ( ! data ) return < p className = "research-message" role = "status" > 正 在 读 取 研 究 数 据 库 … < / p > ;
const categories = new Map ( data . categories . map ( ( c ) = > [ c . id , c ] ) ) ;
@@ -76,6 +77,7 @@ export default function AICompanyResearch() {
const selectedObservations = data . observations . filter ( ( o ) = > o . company === selected ) ;
const selectedEvidenceIds = new Set ( [
. . . ( selectedCompany ? . evidenceIds ? ? [ ] ) , . . . ( selectedCompany ? . pricingEvidence ? [ selectedCompany . pricingEvidence ] : [ ] ) ,
. . . ( selectedCompany ? . technicalReview ? . evidenceIds ? ? [ ] ) ,
. . . selectedObservations . map ( ( o ) = > o . evidence ) ,
] ) ;
const selectedEvidence = data . evidence . filter ( ( e ) = > e . companyId === selected || selectedEvidenceIds . has ( e . id ) ) ;
@@ -118,14 +120,16 @@ export default function AICompanyResearch() {
< label > 成 立 或 发 布 < select value = { cohort } onChange = { ( e ) = > { setCohort ( e . target . value ) ; setPage ( 0 ) ; } } > < option value = "" > 全 部 组 别 < / option > < option value = "2025" > 2025 年 成 立 < / option > < option value = "2026" > 2026 年 成 立 < / option > < option value = "other" > 公 开 发 布 与 转 型 对 照 < / option > < / select > < / label >
< label > 资 料 深 度 < select value = { depth } onChange = { ( e ) = > { setDepth ( e . target . value ) ; setPage ( 0 ) ; } } > < option value = "" > 全 部 资 料 < / option > < option value = "case" > 原 有 个 案 研 究 < / option > < option value = "profile" > 目 录 基 础 资 料 < / option > < / select > < / label >
< label > 记 录 团 队 规 模 < select value = { teamBand } onChange = { ( e ) = > { setTeamBand ( e . target . value ) ; setPage ( 0 ) ; } } > < option value = "" > 全 部 规 模 < / option > < option value = "small" > 1 — 5 人 < / option > < option value = "medium" > 6 — 20 人 < / option > < option value = "large" > 21 — 50 人 < / option > < option value = "xl" > 至 少 51 人 < / option > < option value = "unknown" > 未 披 露 或 分 组 不 确 定 < / option > < / select > < / label >
< label > 技 术 验 证 证 据 < select value = { technicalLevel } onChange = { ( e ) = > { setTechnicalLevel ( e . target . value ) ; setPage ( 0 ) ; } } > < option value = "" > 全 部 验 证 层 级 < / option > { Object . entries ( technicalReviewLabels ) . map ( ( [ value , label ] ) = > < option key = { value } value = { value } > { label } < / option > ) } < / select > < / label >
< label > 收 入 资 料 < select value = { income } onChange = { ( e ) = > { setIncome ( e . target . value ) ; setPage ( 0 ) ; } } > < option value = "" > 全 部 状 态 < / option > < option value = "self" > 找 到 公 司 披 露 < / option > < option value = "estimate" > 仅 第 三 方 估 算 < / option > < option value = "platform" > 仅 支 付 平 台 记 录 < / option > < option value = "unknown" > 暂 无 可 用 数 字 < / option > < / select > < / label >
< / div >
< div className = "research-result-count" role = "status" > { filtered . length } 条 匹 配 记 录 < button onClick = { ( ) = > { setCategory ( "" ) ; setQuery ( "" ) ; setCohort ( "" ) ; setIncome ( "" ) ; setDepth ( "" ) ; setTeamBand ( "" ) ; setPage ( 0 ) ; } } > 清 除 筛 选 < / button > < / div >
< div className = "research-table-wrap" > < table > < caption className = "sr-only" > 公 司 / 产 品 分 类 与 经 营 资 料 。 选 择 名 称 查 看 指 标 和 原 始 证 据 。 < / caption > < thead > < tr > < th > 公 司 或 产 品 / 地 区 < / th > < th > 业 务 与 买 家 < / th > < th > 成 立 / 发 布 组 别 < / th > < th > 团 队 规 模 < / th > < th > 商 业 成 熟 度 < / th > < th > 收 入 资 料 < / th > < / tr > < / thead > < tbody >
< p className = "research-muted" > 技 术 验 证 层 级 记 录 我 们 查 到 了 什 么 , 不 是 能 力 强 弱 评 分 。 技 术 专 项 评 审 与 业 务 分 类 复 核 分 别 记 录 。 < / p >
< div className = "research-result-count" role = "status" > { filtered . length } 条 匹 配 记 录 < button onClick = { ( ) = > { setCategory ( "" ) ; setQuery ( "" ) ; setCohort ( "" ) ; setIncome ( "" ) ; setDepth ( "" ) ; setTeamBand ( "" ) ; setTechnicalLevel ( "" ) ; setPage ( 0 ) ; } } > 清 除 筛 选 < / button > < / div >
< div className = "research-table-wrap" > < table > < caption className = "sr-only" > 公 司 / 产 品 分 类 与 经 营 资 料 。 选 择 名 称 查 看 指 标 和 原 始 证 据 。 < / caption > < thead > < tr > < th > 公 司 或 产 品 / 地 区 < / th > < th > 业 务 与 买 家 < / th > < th > 成 立 / 发 布 组 别 < / th > < th > 团 队 规 模 < / th > < th > 交 付 与 技 术 验 证 证 据 < / th > < th > 收 入 资 料 < / th > < / tr > < / thead > < tbody >
{ filtered . slice ( page * pageSize , ( page + 1 ) * pageSize ) . map ( ( c ) = > < tr key = { c . id } data-selected = { selected === c . id || undefined } >
< td > < button className = "research-company-name" aria-expanded = { selected === c . id } aria-controls = "research-company-detail" onClick = { ( ) = > setSelected ( c . id ) } > { c . name } < / button > < small > { c . location ? ? "地区未注明" } < / small > < / td >
< td > < span className = "research-tag" > { categories . get ( c . cat ) ? . name } < / span > { c . profileOnly && < small > 规 则 初 分 · 待 复 核 < / small > } < p > { c . business } < / p > < small > 买 家 : { c . buyer } < / small > < / td >
< td > { c . cohort } < / td > < td > { c . team !== null && c . teamBound } { c . team ? ? "未披露" } { c . team !== null && " 人" } < / td > < td > { c . stage } < / td > < td > { c . profileOnly ? "收入尚未逐一核验" : revenueLabels [ c . revenueStatus ] } < / td >
< td > < span className = "research-tag" > { categories . get ( c . cat ) ? . name } < / span > { c . profileOnly && < small > 目 录 基 础 资 料 · 分 类 依 据 见 明 细 < / small > } < p > { c . business } < / p > < small > 买 家 : { c . buyer } < / small > < / td >
< td > { c . cohort } < / td > < td > { c . team !== null && c . teamBound } { c . team ? ? "未披露" } { c . team !== null && " 人" } < / td > < td > { c . stage } < small > { technicalReviewLabels [ c . technicalReview ? . level ? ? "unreviewed" ] } < / small > < / td > < td > { c . profileOnly ? "收入尚未逐一核验" : revenueLabels [ c . revenueStatus ] } < / td >
< / tr > ) }
< / tbody > < / table > < / div >
{ ! filtered . length && < p className = "research-message" > 没 有 匹 配 公 司 , 请 调 整 筛 选 条 件 。 < / p > }
@@ -149,7 +153,7 @@ export default function AICompanyResearch() {
{ selectedCompany && < section id = "research-company-detail" ref = { detailSection } tabIndex = { - 1 } className = "research-detail research-section" aria-label = { ` ${ selectedCompany . name } 资料明细 ` } >
< div className = "research-detail-heading" > < h2 > { selectedCompany . name } < / h2 > < button onClick = { ( ) = > setSelected ( null ) } aria-label = "关闭公司明细" > 关 闭 < / button > < / div >
< p > < External url = { selectedCompany . url } > 资 料 入 口 < / External > { selectedCompany . extraUrl && < External url = { selectedCompany . extraUrl } > 额 外 核 验 入 口 < / External > } < / p >
< dl className = "research-company-facts" > < div > < dt > 主 营 业 务 < / dt > < dd > { selectedCompany . business } < / dd > < / div > < div > < dt > 样 本 组 别 < / dt > < dd > { selectedCompany . cohort } < / dd > < / div > < div > < dt > 买 家 < / dt > < dd > { selectedCompany . buyer } < / dd > < / div > < div > < dt > 收 费 / 交 付 模 式 < / dt > < dd > { selectedCompany . businessModel ? ? "待进一步核验" } < / dd > < / div > < div > < dt > 收 费 方 式 < / dt > < dd > { selectedCompany . price } < / dd > < / div > < div > < dt > 交 付 证 据 < / dt > < dd > { selectedCompany . delivery } < / dd > < / div > < div > < dt > 团 队 口 径 < / dt > < dd > { selectedCompany . teamBasis ? ? "本轮未核验人数" } < / dd > < / div > < div > < dt > 分 类 依 据 < / dt > < dd > { selectedCompany . classificationBasis ? ? "原有个案分类;随研究复核" } < / dd > < / div > < div > < dt > 技 术 水 平 < / dt > < dd > 尚 未 独 立 测 试 , 不 据 融 资 或 收 入 作 能 力 排 名 。 < / dd > < / div > < div > < dt > 观 测 主 体 < / dt > < dd > { selectedCompany . entityBasis ? ? "公司或经营品牌,法律主体未逐一核验" } < / dd > < / div > < div > < dt > 身 份 与 限 制 < / dt > < dd > { selectedCompany . notes } < / dd > < / div > < / dl >
< dl className = "research-company-facts" > < div > < dt > 主 营 业 务 < / dt > < dd > { selectedCompany . business } < / dd > < / div > < div > < dt > 样 本 组 别 < / dt > < dd > { selectedCompany . cohort } < / dd > < / div > < div > < dt > 买 家 < / dt > < dd > { selectedCompany . buyer } < / dd > < / div > < div > < dt > 收 费 / 交 付 模 式 < / dt > < dd > { selectedCompany . businessModel ? ? "待进一步核验" } < / dd > < / div > < div > < dt > 收 费 方 式 < / dt > < dd > { selectedCompany . price } < / dd > < / div > < div > < dt > 交 付 证 据 < / dt > < dd > { selectedCompany . delivery } < / dd > < / div > < div > < dt > 团 队 口 径 < / dt > < dd > { selectedCompany . teamBasis ? ? "本轮未核验人数" } < / dd > < / div > < div > < dt > 分 类 依 据 < / dt > < dd > { selectedCompany . classificationBasis ? ? "原有个案分类;随研究复核" } < / dd > < / div > < div > < dt > 技 术 验 证 证 据 < / dt > < dd > { technicalReviewLabels [ selectedCompany . technicalReview ? . level ? ? "unreviewed" ] } < p > { selectedCompany . technicalReview ? . summary ? ? "本轮未形成专项技术评审记录;不表示公司没有技术能力。" } < / p > { selectedCompany . technicalReview && < small > 评 审 日 期 : { selectedCompany . technicalReview . reviewedAt } < / small > } { selectedCompany . technicalReview ? . testScope && < p > 测 试 范 围 : { selectedCompany . technicalReview . testScope } < / p > } < p > 材 料 可 访 问 仅 确 认 可 查 看 , 不 证 明 性 能 、 可 靠 性 或 商 业 交 付 质 量 ; 不 同 任 务 不 作 统 一 能 力 排 名 。 < / p > < / dd > < / div > < div > < dt > 观 测 主 体 < / dt > < dd > { selectedCompany . entityBasis ? ? "公司或经营品牌,法律主体未逐一核验" } < / dd > < / div > < div > < dt > 身 份 与 限 制 < / dt > < dd > { selectedCompany . notes } < / dd > < / div > < / dl >
< h3 > 经 营 指 标 与 排 除 记 录 < / h3 >
{ selectedObservations . length ? < div className = "research-table-wrap" > < table > < thead > < tr > < th > 指 标 / 归 属 < / th > < th > 原 始 数 值 < / th > < th > 数 据 时 点 < / th > < th > 用 途 及 限 制 < / th > < th > 证 据 < / th > < / tr > < / thead > < tbody > { selectedObservations . map ( ( o ) = > < tr key = { o . id } > < td > { o . kind } < small > { o . scope } < / small > < / td > < td > { o . bound !== "=" && o . bound } { money ( o . value , o . unit ) } < / td > < td > { o . timing } < / td > < td > { useLabels [ o . selection ] ? ? o . selection } < small > { o . reason } < / small > < / td > < td > < a href = { ` #detail-evidence- ${ o . evidence } ` } > { o . evidence } < / a > < / td > < / tr > ) } < / tbody > < / table > < / div > : < p > 本 轮 尚 未 找 到 可 记 录 的 量 化 经 营 指 标 。 收 入 保 持 未 知 。 < / p > }
< h3 > 原 始 证 据 < / h3 > < ul className = "research-evidence-list" > { selectedEvidence . map ( ( e ) = > < Evidence key = { e . id } prefix = "detail-evidence" item = { e } / > ) } < / ul >