diff --git a/backend/app/services/evaluation_presets.py b/backend/app/services/evaluation_presets.py index ba3861d..d1a1873 100644 --- a/backend/app/services/evaluation_presets.py +++ b/backend/app/services/evaluation_presets.py @@ -136,6 +136,7 @@ async def seed_evaluation_templates(db_session) -> None: """ from sqlalchemy import select from app.models.evaluation_template import EvaluationTemplate + from app.models.tenant import Tenant # 检查是否已有数据 result = await db_session.execute(select(EvaluationTemplate).limit(1)) @@ -145,10 +146,18 @@ async def seed_evaluation_templates(db_session) -> None: logger.info("评价模板表已有数据,跳过种子初始化") return + # 获取第一个租户作为默认租户 + tenant_result = await db_session.execute(select(Tenant).limit(1)) + tenant = tenant_result.scalar_one_or_none() + if not tenant: + logger.warning("无租户数据,跳过评价模板种子初始化") + return + + tenant_id = str(tenant.id) templates = generate_preset_templates() for tmpl_data in templates: - tmpl = EvaluationTemplate(**tmpl_data) + tmpl = EvaluationTemplate(tenant_id=tenant_id, **tmpl_data) db_session.add(tmpl) await db_session.flush() - logger.info("预设评价模板已写入数据库: %d 个", len(templates)) + logger.info("预设评价模板已写入数据库: %d 个 (tenant=%s)", len(templates), tenant_id) diff --git a/frontend/src/app/(investor)/evaluation/compare/page.tsx b/frontend/src/app/(investor)/evaluation/compare/page.tsx new file mode 100644 index 0000000..f996329 --- /dev/null +++ b/frontend/src/app/(investor)/evaluation/compare/page.tsx @@ -0,0 +1,432 @@ +/** 评分对比视图 — 多模板/多企业评分并排对比 + 雷达图。 */ + +"use client"; + +import { useState, useEffect, useCallback } from "react"; +import { GitCompareArrows, Loader2, TrendingUp, TrendingDown, Minus } from "lucide-react"; +import { + listEvaluationTemplates, + listScores, + type EvaluationTemplate, + type ScoreRecord, +} from "@/lib/api-v2"; +import { + DIMENSION_LABELS, + DIMENSION_SCORE_KEYS, + FUND_TYPE_LABELS, + STAGE_LABELS, + INDUSTRY_LABELS, + getScoreColor, + getScoreBgColor, +} from "@/lib/evaluation-constants"; + +/** 维度 key 列表(14 维度)。 */ +const ALL_DIMENSIONS = Object.keys(DIMENSION_LABELS); + +/** 趋势图标。 */ +function TrendIcon({ trend }: { trend: string | null }) { + if (trend === "up") return ; + if (trend === "down") return ; + return ; +} + +export default function EvaluationComparePage() { + const [templates, setTemplates] = useState([]); + const [selectedTemplateIds, setSelectedTemplateIds] = useState([]); + const [scores, setScores] = useState>({}); + const [loading, setLoading] = useState(false); + const [viewMode, setViewMode] = useState<"table" | "radar">("table"); + + /** 加载模板列表。 */ + const loadTemplates = useCallback(async () => { + setLoading(true); + try { + const resp = await listEvaluationTemplates({ is_default: true }); + if (resp.data) { + setTemplates(resp.data); + // 默认选前 2 个 + if (resp.data.length >= 2) { + setSelectedTemplateIds([resp.data[0].id, resp.data[1].id]); + } else if (resp.data.length === 1) { + setSelectedTemplateIds([resp.data[0].id]); + } + } + } catch { + // 静默处理 + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + loadTemplates(); + }, [loadTemplates]); + + /** 加载选中模板的评分记录。 */ + useEffect(() => { + async function loadScores() { + const newScores: Record = {}; + for (const tmplId of selectedTemplateIds) { + try { + const resp = await listScores({ template_id: tmplId, limit: 5 }); + if (resp.data) newScores[tmplId] = resp.data; + } catch { + // 静默处理 + } + } + setScores(newScores); + } + if (selectedTemplateIds.length > 0) { + loadScores(); + } + }, [selectedTemplateIds]); + + /** 切换模板选择。 */ + function toggleTemplate(id: string) { + setSelectedTemplateIds((prev) => { + if (prev.includes(id)) return prev.filter((t) => t !== id); + if (prev.length >= 4) return prev; + return [...prev, id]; + }); + } + + /** 获取模板最新评分。 */ + function getLatestScore(tmplId: string): ScoreRecord | null { + const list = scores[tmplId]; + if (!list || list.length === 0) return null; + return list[0]; + } + + /** 雷达图坐标计算。 */ + function getRadarPoints(scoreRecord: ScoreRecord, dims: string[], centerX: number, centerY: number, radius: number): string { + return dims + .map((dim, i) => { + const scoreKey = DIMENSION_SCORE_KEYS[dim]; + const value = (scoreRecord as unknown as Record)[scoreKey]; + const normalized = value ? value / 100 : 0; + const angle = (i / dims.length) * 2 * Math.PI - Math.PI / 2; + const x = centerX + radius * normalized * Math.cos(angle); + const y = centerY + radius * normalized * Math.sin(angle); + return `${x},${y}`; + }) + .join(" "); + } + + const selectedTemplates = templates.filter((t) => selectedTemplateIds.includes(t.id)); + const radarSize = 320; + const radarCenter = radarSize / 2; + const radarRadius = radarSize / 2 - 40; + const radarColors = ["#6366f1", "#10b981", "#f59e0b", "#f43f5e"]; + + return ( +
+ {/* 页头 */} +
+
+ +
+

评分对比

+

+ 多模板评分并排对比 · 雷达图可视化 +

+
+
+
+ + +
+
+ + {/* 模板选择器 */} +
+

选择模板进行对比(最多 4 个):

+
+ {templates.map((tmpl) => ( + + ))} + {templates.length === 0 && !loading && ( + 暂无模板,请先在模板配置页创建 + )} +
+
+ + {loading && ( +
+ +
+ )} + + {/* 对比内容 */} + {!loading && selectedTemplates.length > 0 && ( + <> + {viewMode === "table" ? ( +
+

维度评分对比

+
+ + + + + {selectedTemplates.map((tmpl, idx) => { + const score = getLatestScore(tmpl.id); + return ( + + ); + })} + + + + {ALL_DIMENSIONS.map((dim) => { + const isEnabled = selectedTemplates.some( + (t) => !t.disabled_dimensions.includes(dim), + ); + if (!isEnabled) return null; + + return ( + + + {selectedTemplates.map((tmpl) => { + const score = getLatestScore(tmpl.id); + const isDisabled = tmpl.disabled_dimensions.includes(dim); + const scoreKey = DIMENSION_SCORE_KEYS[dim]; + const value = score + ? (score as unknown as Record)[scoreKey] + : null; + + return ( + + ); + })} + + ); + })} + +
维度 +
+ + {tmpl.name} + {score ? ( + + {score.total_score.toFixed(1)} + + ) : ( + 暂无评分 + )} +
+
+ {DIMENSION_LABELS[dim]} + + {isDisabled ? ( + + ) : value !== null && value !== undefined ? ( +
+ + {value.toFixed(1)} + +
+
+
+
+ ) : ( + + )} +
+
+ + {/* 模板信息对比 */} +
+

模板配置对比

+
+ {selectedTemplates.map((tmpl) => ( +
+

{tmpl.name}

+
+

基金类型: {FUND_TYPE_LABELS[tmpl.fund_type] ?? tmpl.fund_type}

+

企业阶段: {STAGE_LABELS[tmpl.company_stage] ?? tmpl.company_stage}

+

产业赛道: {INDUSTRY_LABELS[tmpl.industry] ?? tmpl.industry}

+

启用维度: {tmpl.enabled_dimensions.length} / 14

+
+
+ ))} +
+
+
+ ) : ( + /* 雷达图视图 */ +
+

雷达图对比

+ + {/* SVG 雷达图 */} +
+ + {/* 背景网格 */} + {[0.25, 0.5, 0.75, 1.0].map((ratio) => ( + + ))} + {/* 轴线 */} + {ALL_DIMENSIONS.map((dim, i) => { + const angle = (i / ALL_DIMENSIONS.length) * 2 * Math.PI - Math.PI / 2; + const x = radarCenter + radarRadius * Math.cos(angle); + const y = radarCenter + radarRadius * Math.sin(angle); + return ( + + ); + })} + {/* 维度标签 */} + {ALL_DIMENSIONS.map((dim, i) => { + const angle = (i / ALL_DIMENSIONS.length) * 2 * Math.PI - Math.PI / 2; + const labelRadius = radarRadius + 20; + const x = radarCenter + labelRadius * Math.cos(angle); + const y = radarCenter + labelRadius * Math.sin(angle); + return ( + + {DIMENSION_LABELS[dim]} + + ); + })} + {/* 评分多边形 */} + {selectedTemplates.map((tmpl, idx) => { + const score = getLatestScore(tmpl.id); + if (!score) return null; + const enabledDims = tmpl.enabled_dimensions; + const points = getRadarPoints( + score, + enabledDims, + radarCenter, + radarCenter, + radarRadius, + ); + return ( + + ); + })} + + + {/* 图例 */} +
+ {selectedTemplates.map((tmpl, idx) => { + const score = getLatestScore(tmpl.id); + return ( +
+ + {tmpl.name} + {score && ( + + {score.total_score.toFixed(1)} + + )} +
+ ); + })} +
+
+ + {/* 评分历史趋势 */} + {Object.values(scores).some((s) => s.length > 1) && ( +
+

评分历史趋势

+
+ {selectedTemplates.map((tmpl) => { + const tmplScores = scores[tmpl.id]; + if (!tmplScores || tmplScores.length === 0) return null; + return ( +
+ {tmpl.name} +
+ {tmplScores.slice(0, 5).reverse().map((s, i) => ( +
+ {i > 0 && } + + {s.total_score.toFixed(1)} + +
+ ))} +
+
+ ); + })} +
+
+ )} +
+ )} + + )} + + {!loading && selectedTemplates.length === 0 && templates.length > 0 && ( +
+ +

请选择至少 1 个模板进行对比

+
+ )} +
+ ); +} diff --git a/frontend/src/app/(investor)/evaluation/page.tsx b/frontend/src/app/(investor)/evaluation/page.tsx new file mode 100644 index 0000000..bd7abb4 --- /dev/null +++ b/frontend/src/app/(investor)/evaluation/page.tsx @@ -0,0 +1,413 @@ +/** 评价模板配置页 — 6 轴参数选择 + 权重预览 + 模板管理。 */ + +"use client"; + +import { useState, useEffect, useCallback } from "react"; +import { Settings2, Plus, ChevronDown, ChevronRight, Loader2, Save, BarChart3 } from "lucide-react"; +import { + computeWeights, + listEvaluationTemplates, + createEvaluationTemplate, + type EvaluationTemplate, +} from "@/lib/api-v2"; +import { DIMENSION_LABELS } from "@/lib/evaluation-constants"; + +/** 6 轴选项定义。 */ +const AXIS_OPTIONS = { + fund_type: [ + { value: "angel", label: "天使/种子基金" }, + { value: "early_vc", label: "早期VC" }, + { value: "growth_vc", label: "成长期VC" }, + { value: "pe", label: "PE/并购基金" }, + { value: "cvc", label: "产业基金" }, + { value: "fof", label: "母基金" }, + { value: "distress", label: "困境/特殊机会" }, + { value: "esg", label: "ESG/影响力" }, + ], + fund_lifecycle: [ + { value: "investment", label: "投资期" }, + { value: "growth", label: "成长期" }, + { value: "exit_preparation", label: "退出准备期" }, + { value: "liquidation", label: "清算期" }, + ], + company_stage: [ + { value: "seed", label: "种子/天使" }, + { value: "a", label: "A轮" }, + { value: "b", label: "B轮" }, + { value: "c", label: "C轮+" }, + { value: "pre_ipo", label: "Pre-IPO" }, + ], + industry: [ + { value: "ai", label: "AI/SaaS" }, + { value: "saas", label: "企业服务" }, + { value: "hardware", label: "硬科技/芯片" }, + { value: "biotech", label: "生物医药" }, + { value: "consumer", label: "消费品牌" }, + { value: "fintech", label: "金融科技" }, + { value: "manufacturing", label: "新能源/先进制造" }, + ], + strategy: [ + { value: "growth", label: "成长型" }, + { value: "value", label: "价值型" }, + { value: "empowerment", label: "投后赋能型" }, + { value: "turnaround", label: "困境反转型" }, + ], + investor_type: [ + { value: "gp", label: "GP/合伙人" }, + { value: "post_invest_lead", label: "投后负责人" }, + { value: "investor", label: "投资经理" }, + ], +} as const; + +/** 轴标签。 */ +const AXIS_LABELS: Record = { + fund_type: "基金类型", + fund_lifecycle: "存续期阶段", + company_stage: "企业阶段", + industry: "产业赛道", + strategy: "投资策略", + investor_type: "投资人类型", +}; + +export default function EvaluationTemplatesPage() { + const [params, setParams] = useState({ + fund_type: "early_vc", + fund_lifecycle: "investment", + company_stage: "a", + industry: "ai", + strategy: "growth", + investor_type: "investor", + }); + + const [weights, setWeights] = useState | null>(null); + const [enabledDims, setEnabledDims] = useState([]); + const [disabledDims, setDisabledDims] = useState([]); + const [customMetrics, setCustomMetrics] = useState>([]); + const [computing, setComputing] = useState(false); + const [showDisabled, setShowDisabled] = useState(false); + + const [templates, setTemplates] = useState([]); + const [loadingTemplates, setLoadingTemplates] = useState(false); + const [showCreateDialog, setShowCreateDialog] = useState(false); + const [newTemplateName, setNewTemplateName] = useState(""); + const [creating, setCreating] = useState(false); + const [error, setError] = useState(""); + + /** 实时计算权重。 */ + const doCompute = useCallback(async () => { + setComputing(true); + setError(""); + try { + const resp = await computeWeights(params); + const data = resp.data; + if (data) { + setWeights(data.weights); + setEnabledDims(data.enabled_dimensions); + setDisabledDims(data.disabled_dimensions); + setCustomMetrics(data.custom_metrics); + } + } catch (err) { + setError(err instanceof Error ? err.message : "权重计算失败"); + } finally { + setComputing(false); + } + }, [params]); + + useEffect(() => { + doCompute(); + }, [doCompute]); + + /** 加载模板列表。 */ + const loadTemplates = useCallback(async () => { + setLoadingTemplates(true); + try { + const resp = await listEvaluationTemplates({ + fund_type: params.fund_type, + company_stage: params.company_stage, + industry: params.industry, + }); + if (resp.data) setTemplates(resp.data); + } catch { + // 静默处理 + } finally { + setLoadingTemplates(false); + } + }, [params.fund_type, params.company_stage, params.industry]); + + useEffect(() => { + loadTemplates(); + }, [loadTemplates]); + + /** 创建模板。 */ + async function handleCreateTemplate() { + if (!newTemplateName.trim()) { + setError("请输入模板名称"); + return; + } + setCreating(true); + setError(""); + try { + await createEvaluationTemplate({ + name: newTemplateName, + ...params, + }); + setShowCreateDialog(false); + setNewTemplateName(""); + await loadTemplates(); + } catch (err) { + setError(err instanceof Error ? err.message : "创建失败"); + } finally { + setCreating(false); + } + } + + const totalWeight = weights ? Object.values(weights).reduce((a, b) => a + b, 0) : 0; + + return ( +
+ {/* 页头 */} +
+
+ +
+

评价模板配置

+

+ 6 轴动态权重 · 50 个预设 · 自定义模板 +

+
+
+ +
+ + {/* 6 轴参数选择器 */} +
+

6 轴参数配置

+
+ {Object.entries(AXIS_OPTIONS).map(([key, options]) => ( +
+ + +
+ ))} +
+
+ + {/* 权重预览 */} +
+
+
+ +

权重预览

+ {computing && } +
+ + 总权重: {totalWeight.toFixed(2)}% + +
+ + {error && ( +
+ {error} +
+ )} + + {/* 权重条形图 */} +
+ {weights && + Object.entries(weights) + .sort(([, a], [, b]) => b - a) + .map(([dim, weight]) => ( +
+ + {DIMENSION_LABELS[dim] ?? dim} + +
+
+
+ {weight > 3 && `${weight.toFixed(1)}%`} +
+
+
+ + {weight.toFixed(2)}% + +
+ ))} +
+ + {/* 禁用维度 */} + {disabledDims.length > 0 && ( +
+ + {showDisabled && ( +
+ {disabledDims.map((dim) => ( + + {DIMENSION_LABELS[dim] ?? dim} + + ))} +
+ )} +
+ )} + + {/* 专属指标 */} + {customMetrics.length > 0 && ( +
+

赛道专属指标

+
+ {customMetrics.map((metric) => ( +
+

{metric.label}

+

{metric.description}

+
+ ))} +
+
+ )} +
+ + {/* 模板列表 */} +
+
+

+ 匹配模板 ({templates.length}) +

+ {loadingTemplates && } +
+ + {templates.length === 0 && !loadingTemplates ? ( +
+

当前筛选条件下暂无模板

+

可点击右上角"新建模板"创建

+
+ ) : ( +
+ {templates.map((tmpl) => ( +
+
+
+

{tmpl.name}

+

+ {AXIS_OPTIONS.fund_type.find((o) => o.value === tmpl.fund_type)?.label} ·{" "} + {AXIS_OPTIONS.company_stage.find((o) => o.value === tmpl.company_stage)?.label} ·{" "} + {AXIS_OPTIONS.industry.find((o) => o.value === tmpl.industry)?.label} +

+
+ {tmpl.is_default && ( + + 默认 + + )} +
+
+ + {Object.keys(tmpl.weights).length} 维度 + + v{tmpl.version} +
+
+ ))} +
+ )} +
+ + {/* 创建模板对话框 */} + {showCreateDialog && ( +
setShowCreateDialog(false)} + > +
e.stopPropagation()} + > +

新建评价模板

+

+ 基于当前 6 轴参数配置创建,权重将自动计算 +

+
+ + setNewTemplateName(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") handleCreateTemplate(); + }} + /> +
+
+ 基金类型: {AXIS_OPTIONS.fund_type.find((o) => o.value === params.fund_type)?.label} + {" · "} + 阶段: {AXIS_OPTIONS.company_stage.find((o) => o.value === params.company_stage)?.label} + {" · "} + 赛道: {AXIS_OPTIONS.industry.find((o) => o.value === params.industry)?.label} +
+ {error && ( +

{error}

+ )} +
+ + +
+
+
+ )} +
+ ); +} diff --git a/frontend/src/lib/api-v2.ts b/frontend/src/lib/api-v2.ts index 1a2f40e..3b41f46 100644 --- a/frontend/src/lib/api-v2.ts +++ b/frontend/src/lib/api-v2.ts @@ -531,3 +531,194 @@ export async function founderOverview() { export async function founderHealth() { return apiFetch(`/founder/health`); } + +// ============ 评价指标体系 ============ + +/** 权重计算请求参数。 */ +export interface WeightComputeParams { + fund_type: string; + fund_lifecycle?: string; + company_stage?: string; + industry?: string; + strategy?: string; + investor_type?: string; +} + +/** 评价模板。 */ +export interface EvaluationTemplate { + id: string; + name: string; + fund_type: string; + fund_lifecycle: string; + company_stage: string; + industry: string; + strategy: string; + investor_type: string; + weights: Record; + enabled_dimensions: string[]; + disabled_dimensions: string[]; + custom_metrics: { metrics: Array<{ key: string; label: string; description: string }> } | null; + is_default: boolean; + version: number; +} + +/** 评分记录。 */ +export interface ScoreRecord { + id: string; + company_id: string; + total_score: number; + financial_score: number | null; + operational_score: number | null; + ai_commercial_score: number | null; + ai_cost_score: number | null; + org_talent_score: number | null; + product_tech_score: number | null; + market_compete_score: number | null; + governance_score: number | null; + financing_score: number | null; + synergy_score: number | null; + ai_model_product_score: number | null; + data_compliance_score: number | null; + team_tech_score: number | null; + customer_success_score: number | null; + trend: string | null; + template_id: string | null; + fund_type: string | null; + fund_lifecycle: string | null; + company_stage: string | null; + industry: string | null; + strategy: string | null; + calculated_at: string | null; +} + +/** 基金信息。 */ +export interface FundInfo { + id: string; + name: string; + fund_type: string; + strategy: string; + established_date: string | null; + total_lifespan_months: number; + investment_period_months: number; + current_lifecycle: string; + lp_composition: Record | null; + primary_market: string | null; +} + +/** 计算权重(不持久化)。 */ +export async function computeWeights(params: WeightComputeParams) { + return apiFetch<{ + weights: Record; + enabled_dimensions: string[]; + disabled_dimensions: string[]; + custom_metrics: Array<{ key: string; label: string; description: string }>; + }>("/evaluation/weights/compute", { + method: "POST", + body: JSON.stringify({ + fund_type: params.fund_type, + fund_lifecycle: params.fund_lifecycle ?? "investment", + company_stage: params.company_stage ?? "a", + industry: params.industry ?? "ai", + strategy: params.strategy ?? "growth", + investor_type: params.investor_type ?? "investor", + }), + }); +} + +/** 获取模板列表。 */ +export async function listEvaluationTemplates(params?: { + fund_type?: string; + fund_lifecycle?: string; + company_stage?: string; + industry?: string; + strategy?: string; + is_default?: boolean; +}) { + const query = new URLSearchParams(); + if (params?.fund_type) query.set("fund_type", params.fund_type); + if (params?.fund_lifecycle) query.set("fund_lifecycle", params.fund_lifecycle); + if (params?.company_stage) query.set("company_stage", params.company_stage); + if (params?.industry) query.set("industry", params.industry); + if (params?.strategy) query.set("strategy", params.strategy); + if (params?.is_default !== undefined) query.set("is_default", String(params.is_default)); + return apiFetch(`/evaluation/templates?${query.toString()}`); +} + +/** 获取模板详情。 */ +export async function getEvaluationTemplate(id: string) { + return apiFetch(`/evaluation/templates/${id}`); +} + +/** 创建模板。 */ +export async function createEvaluationTemplate(data: { + name: string; + fund_type: string; + fund_lifecycle?: string; + company_stage?: string; + industry?: string; + strategy?: string; + investor_type?: string; + weights_json?: Record; + is_default?: boolean; +}) { + return apiFetch<{ id: string; name: string; weights: Record }>( + "/evaluation/templates", + { method: "POST", body: JSON.stringify(data) }, + ); +} + +/** 计算评分。 */ +export async function calculateScore(data: { + company_id: string; + template_id?: string; + fund_type?: string; + fund_lifecycle?: string; + company_stage?: string; + industry?: string; + strategy?: string; + structured_data?: Record; +}) { + return apiFetch<{ + score_id?: string; + total_score: number; + dimension_scores: Record; + template: { id: string; name: string; weights: Record } | null; + }>("/evaluation/score", { + method: "POST", + body: JSON.stringify(data), + }); +} + +/** 获取评分历史。 */ +export async function listScores(params?: { + company_id?: string; + template_id?: string; + limit?: number; +}) { + const query = new URLSearchParams(); + if (params?.company_id) query.set("company_id", params.company_id); + if (params?.template_id) query.set("template_id", params.template_id); + if (params?.limit) query.set("limit", String(params.limit)); + return apiFetch(`/evaluation/scores?${query.toString()}`); +} + +/** 获取基金列表(评价体系)。 */ +export async function listEvaluationFunds() { + return apiFetch("/evaluation/funds"); +} + +/** 创建基金(评价体系)。 */ +export async function createEvaluationFund(data: { + name: string; + fund_type: string; + strategy?: string; + established_date?: string; + total_lifespan_months?: number; + investment_period_months?: number; + primary_market?: string; +}) { + return apiFetch<{ id: string; name: string; current_lifecycle: string }>( + "/evaluation/funds", + { method: "POST", body: JSON.stringify(data) }, + ); +} diff --git a/frontend/src/lib/evaluation-constants.ts b/frontend/src/lib/evaluation-constants.ts new file mode 100644 index 0000000..15aa09e --- /dev/null +++ b/frontend/src/lib/evaluation-constants.ts @@ -0,0 +1,101 @@ +/** 评价体系共享常量 — 维度标签、轴标签等。 */ + +/** 14 维度中文标签。 */ +export const DIMENSION_LABELS: Record = { + financial: "财务", + operational: "经营", + ai_commercial: "AI商业化", + ai_cost: "AI成本", + org_talent: "组织人才", + product_tech: "产品技术", + market_compete: "市场竞争", + governance: "治理合规", + financing: "融资资本", + synergy: "协同赋能", + ai_model_product: "AI模型产品", + data_compliance: "数据合规", + team_tech: "团队技术", + customer_success: "客户成功", +}; + +/** 维度 key → 评分字段 key 映射。 */ +export const DIMENSION_SCORE_KEYS: Record = { + financial: "financial_score", + operational: "operational_score", + ai_commercial: "ai_commercial_score", + ai_cost: "ai_cost_score", + org_talent: "org_talent_score", + product_tech: "product_tech_score", + market_compete: "market_compete_score", + governance: "governance_score", + financing: "financing_score", + synergy: "synergy_score", + ai_model_product: "ai_model_product_score", + data_compliance: "data_compliance_score", + team_tech: "team_tech_score", + customer_success: "customer_success_score", +}; + +/** 基金类型标签。 */ +export const FUND_TYPE_LABELS: Record = { + angel: "天使/种子基金", + early_vc: "早期VC", + growth_vc: "成长期VC", + pe: "PE/并购基金", + cvc: "产业基金", + fof: "母基金", + distress: "困境/特殊机会", + esg: "ESG/影响力", +}; + +/** 存续期标签。 */ +export const LIFECYCLE_LABELS: Record = { + investment: "投资期", + growth: "成长期", + exit_preparation: "退出准备期", + liquidation: "清算期", +}; + +/** 企业阶段标签。 */ +export const STAGE_LABELS: Record = { + seed: "种子/天使", + a: "A轮", + b: "B轮", + c: "C轮+", + pre_ipo: "Pre-IPO", +}; + +/** 产业赛道标签。 */ +export const INDUSTRY_LABELS: Record = { + ai: "AI/SaaS", + saas: "企业服务", + hardware: "硬科技/芯片", + biotech: "生物医药", + consumer: "消费品牌", + fintech: "金融科技", + manufacturing: "新能源/先进制造", +}; + +/** 投资策略标签。 */ +export const STRATEGY_LABELS: Record = { + growth: "成长型", + value: "价值型", + empowerment: "投后赋能型", + turnaround: "困境反转型", +}; + +/** 评分颜色等级。 */ +export function getScoreColor(score: number): string { + if (score >= 80) return "text-emerald-600"; + if (score >= 60) return "text-indigo-600"; + if (score >= 40) return "text-amber-600"; + return "text-rose-600"; +} + +/** 评分背景颜色。 */ +export function getScoreBgColor(score: number): string { + if (score >= 80) return "bg-emerald-500"; + if (score >= 60) return "bg-indigo-500"; + if (score >= 40) return "bg-amber-500"; + return "bg-rose-500"; +} diff --git a/frontend/src/lib/navConfig.ts b/frontend/src/lib/navConfig.ts index 1eea405..a3e3607 100644 --- a/frontend/src/lib/navConfig.ts +++ b/frontend/src/lib/navConfig.ts @@ -5,7 +5,7 @@ import { AlertTriangle, FileText, Receipt, ScrollText, Users, Flag, ListTodo, Network, UserCircle, TrendingUp, Target, GitBranch, Lightbulb, Radar, Crosshair, TrendingUp as TrendUp, LogOut, Bot, Cpu, Network as Net, - BookOpen, ShieldCheck, FileBarChart, Settings, + BookOpen, ShieldCheck, FileBarChart, Settings, Settings2, type LucideIcon, } from "lucide-react"; @@ -99,6 +99,8 @@ export const NAV_DOMAINS: Record = { { href: "/aars", label: "AAR 复盘", icon: BookOpen }, { href: "/pre-mortems", label: "Pre-mortem", icon: ShieldCheck }, { href: "/reports/templates", label: "投后报告", icon: FileBarChart }, + { href: "/evaluation", label: "评价模板", icon: Settings2 }, + { href: "/evaluation/compare", label: "评分对比", icon: GitCompareArrows }, ], }, };