/** 企业视角切换 Context — 全局企业筛选器。 */ "use client"; import { createContext, useContext, useEffect, useState, type ReactNode } from "react"; import { listCompanies, type Company } from "@/lib/companies"; interface CompanyScopeValue { /** 选中的企业 ID,null 表示"全部企业" */ companyId: string | null; /** 选中的企业名称,null 表示"全部企业" */ companyName: string | null; /** 企业列表 */ companies: Company[]; /** 切换企业 */ setCompanyId: (id: string | null) => void; /** 是否加载中 */ isLoading: boolean; } const CompanyScopeContext = createContext(undefined); /** * 企业视角 Provider。 * * 在侧边栏顶部提供企业选择器,选中后所有页面自动按该企业过滤数据。 * 选择"全部企业"时恢复全局视图。 */ export function CompanyScopeProvider({ children }: { children: ReactNode }) { const [companyId, setCompanyIdState] = useState(null); const [companies, setCompanies] = useState([]); const [isLoading, setIsLoading] = useState(true); // 加载企业列表 useEffect(() => { listCompanies({ page_size: 100 }) .then((resp) => { if (resp.data?.items) setCompanies(resp.data.items); }) .catch(() => { // ignore }) .finally(() => setIsLoading(false)); }, []); // 从 localStorage 恢复选择 useEffect(() => { const saved = localStorage.getItem("company_scope_id"); if (saved && saved !== "all") { setCompanyIdState(saved); } }, []); // 持久化选择 + 派发事件通知各页面重新加载 function setCompanyId(id: string | null) { setCompanyIdState(id); if (id) { localStorage.setItem("company_scope_id", id); } else { localStorage.setItem("company_scope_id", "all"); } // 派发全局事件,让监听了 useScopeEffect 的页面重新加载数据 window.dispatchEvent(new CustomEvent("company-scope-change", { detail: { companyId: id } })); } const companyName = companyId ? companies.find((c) => c.id === companyId)?.name ?? null : null; return ( {children} ); } /** * 使用企业视角 Context。 * * 返回当前选中的企业 ID,用于各页面过滤数据。 */ export function useCompanyScope() { const ctx = useContext(CompanyScopeContext); if (!ctx) { throw new Error("useCompanyScope 必须在 CompanyScopeProvider 内使用"); } return ctx; } /** * 监听企业视角变化,自动触发回调重新加载数据。 * * 各页面在 useEffect 中调用此 hook,当全局企业选择变化时, * 会自动触发回调重新加载(apiFetch 会自动注入新的 company_id)。 * * @param callback 数据加载回调 * @param deps 额外依赖(如分页、筛选状态) */ export function useScopeEffect(callback: () => void, deps: unknown[] = []) { const { companyId } = useCompanyScope(); useEffect(() => { callback(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [companyId, ...deps]); }