feat(scope): add company scope switcher — two views (by-task all companies / by-company all tasks)

- CompanyScopeContext + Provider with localStorage persistence
- Sidebar company selector (desktop + mobile)
- apiFetch auto-injects company_id on GET requests
- Dashboard summary API supports company_id filter
- Milestones/Financial/DigitalTwins pages use scope instead of manual input
- HealthHeatmap/HealthTrends components react to scope changes
This commit is contained in:
selfrelease
2026-07-20 07:47:53 +08:00
parent 8b8926bde3
commit e81aa6828e
10 changed files with 306 additions and 53 deletions
+6 -14
View File
@@ -1,8 +1,9 @@
"use client";
import { useEffect, useState } from "react";
import { listFinancialData} from "@/lib/api-v2";
import { PageContainer,Badge, TableEmpty } from "@/components/shared/PageContainer";
import { listFinancialData } from "@/lib/api-v2";
import { useCompanyScope } from "@/lib/company-scope";
import { PageContainer, Badge, TableEmpty } from "@/components/shared/PageContainer";
import { LoadingSpinner } from "@/components/shared/LoadingSpinner";
import { EmptyState } from "@/components/shared/EmptyState";
@@ -19,13 +20,14 @@ interface FinancialData {
* 投资人端 — 财务数据校验页面。
*/
export default function FinancialPage() {
const { companyId, companyName } = useCompanyScope();
const [items, setItems] = useState<FinancialData[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [companyId, setCompanyId] = useState("");
useEffect(() => {
if (!companyId) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setItems([]);
setIsLoading(false);
return;
}
@@ -37,20 +39,10 @@ export default function FinancialPage() {
return (
<PageContainer title="财务数据校验" description="AI 交叉验证不同来源数据一致性,检测报表内部逻辑矛盾">
<div className="flex items-center gap-2">
<input
type="text"
placeholder="输入企业 ID"
value={companyId}
onChange={(e) => setCompanyId(e.target.value)}
className="rounded-md border border-gray-300 px-3 py-1.5 text-sm"
/>
</div>
{isLoading ? (
<LoadingSpinner />
) : !companyId ? (
<EmptyState title="请输入企业 ID" description="输入企业 ID 后查看财务数据" />
<EmptyState title="请先在侧边栏选择企业" description="财务数据需要指定具体企业,请在左上角企业选择器中选择" />
) : (
<div className="overflow-x-auto rounded-lg border border-gray-200">
<table className="min-w-full divide-y divide-gray-200 text-sm">