覆盖 AC-53/54/55(BR-50)。审计 P1-2「新界面未接入、原落地页与导航未改」 由此关闭:导航改为原型 token 的深色壳,三个模块各有独立路由与入口。 界面: - navigation.css:导航改用原型 token(nav.bg #102B27 / active #244D41 / muted #B1C6BE),身份选择器在深色底上改用 dark 变体(否则白底白字); 工作区画布保持浅色 #f5f7f9——"深色导航+浅色工作区"是两件事。 保留可读性守卫锁定的两条规则(导航项 min-height:42px/font-size:14px、 身份选择器 min-height:42px),原有 7 项守卫全部继续通过。 - AgentNav:品牌改为「兴邦金租/智审工作台」,加「业务工作区」分组标签, 三个模块入口(AI 预审 / 历史经验加工 / 轨迹复盘)指向各自路由; 权限仍按角色裁剪,身份仍来自服务端。 - 新增 /prescreen-workbench(AI 预审)、/trajectory-center(轨迹复盘)两个模块路由; / 从旧聊天落地页改为业务工作区首页,呈现三个模块入口;原落地页内容保留在 /assistant,没有被删掉(AC-53 要求的是不再"只保留"旧布局)。 - TrajectoryPanel:消费既有只读轨迹端点,失败/跳过步骤照常列出并带错误码, 成本缺失显示"未记录(不填 0)",并展示服务端对轨迹范围的说明; 会议结果只读展示(录入属第四批)。 复用而非复制:三个模块路由都 import 既有的 PrescreenPanel / ImportBatchPanel / ExperienceCenter,没有在路由里再写一份实现。 守卫:新增 module-shell-ui(已登记 verify.py,共 13 项检查),机械核对 深色 token、三入口存在、已合并旧页仍为重定向、按角色裁剪、 ?project= 上下文与不硬编码项目 id、轨迹面板不渲染推理字段, 并做前后端路径一致性断言(界面调用的端点必须真的在后端声明过)。 深色底对比度按 WCAG 公式在守卫内**实算**(阈值 AA 4.5:1,实测最低 8.37:1), 不在文档里声称"应该够清楚"。 Co-Authored-By: Claude Code <noreply@anthropic.com>
81 lines
3.9 KiB
TypeScript
81 lines
3.9 KiB
TypeScript
"use client";
|
|
import {useEffect, useState} from "react";
|
|
import {ArrowRight, GitBranch, History, ShieldCheck} from "lucide-react";
|
|
import AgentNav from "@/components/xingbang/AgentNav";
|
|
import {messageOf} from "@/lib/prescreen";
|
|
import "./data-access/workspace.css";
|
|
|
|
/** 业务工作区首页(AC-53 / BR-50)。
|
|
*
|
|
* 三个模块**各有独立入口**:AI 预审 / 历史经验加工 / 轨迹复盘,指向各自的路由。
|
|
* 深色导航 + 浅色工作区取自已确认原型;不再以旧版聊天落地页作为唯一首屏。
|
|
* 本页只负责导航与项目上下文,不在这里叠加任何模块面板。
|
|
*/
|
|
|
|
const MODULES = [
|
|
{key: "prescreen", href: "/prescreen-workbench", icon: ShieldCheck, name: "AI 预审",
|
|
summary: "按已发布方案执行受限编排,给出有依据的意见、缺证与未完成项。",
|
|
detail: "概率状态与依据 · 动态维度 · 推进沙盘"},
|
|
{key: "experience", href: "/experience-center", icon: History, name: "历史经验加工",
|
|
summary: "从历史过程恢复时间线,用真实模型提炼核查方法,经专家修订与独立审核发布。",
|
|
detail: "来源时间线 · 候选治理 · 卡片入方案"},
|
|
{key: "trajectory", href: "/trajectory-center", icon: GitBranch, name: "轨迹复盘",
|
|
summary: "只读回放每一次运行的步骤与版本依据,与会议结果对照。",
|
|
detail: "步骤与依据 · 失败保留 · 会议结果对照"},
|
|
];
|
|
|
|
export default function BusinessHome() {
|
|
const [identity, setIdentity] = useState<any>(null);
|
|
const [projectId, setProjectId] = useState("");
|
|
const [error, setError] = useState("");
|
|
|
|
function headers(json = true) {
|
|
const token = sessionStorage.getItem("xingbang-access-token");
|
|
const role = sessionStorage.getItem("xingbang-demo-role");
|
|
return {...(json ? {"Content-Type": "application/json"} : {}),
|
|
...(token ? {Authorization: `Bearer ${token}`} : {}),
|
|
...(role ? {"X-Xingbang-Demo-Role": role} : {})};
|
|
}
|
|
|
|
useEffect(() => {
|
|
setProjectId(new URLSearchParams(location.search).get("project") || "");
|
|
fetch("/api/risk-agent/access/context", {headers: headers(false)})
|
|
.then(async (response) => {
|
|
if (!response.ok) throw Error(messageOf(await response.json().catch(() => ({})), "无法读取身份"));
|
|
setIdentity(await response.json());
|
|
})
|
|
.catch((e: any) => setError(e.message));
|
|
}, []);
|
|
|
|
const withProject = (href: string) => projectId ? `${href}?project=${encodeURIComponent(projectId)}` : href;
|
|
return <div className="ra-app">
|
|
<AgentNav active="home" project={projectId || undefined} identity={identity || undefined}/>
|
|
<main className="ra-main">
|
|
<div className="ra-heading">
|
|
<div>
|
|
<div className="ra-eyebrow">业务工作区</div>
|
|
<h1>兴邦金租 · 智审工作台</h1>
|
|
<p>三个模块各有独立入口;项目上下文在模块之间保持。所有数值与结论都标明来源,未接入的能力会如实显示未完成。</p>
|
|
</div>
|
|
</div>
|
|
{error && <div role="alert" className="ra-alert error">{error}</div>}
|
|
{projectId && <div className="ra-alert">当前项目上下文:<b>{projectId}</b>(在模块之间保持)</div>}
|
|
|
|
<div className="xb-project-grid">
|
|
{MODULES.map((item) => <a className="xb-project-card" key={item.key} href={withProject(item.href)}>
|
|
<span>{item.detail}</span>
|
|
<h2><item.icon size={18} aria-hidden="true"/> {item.name}</h2>
|
|
<p>{item.summary}</p>
|
|
<footer><small>进入模块</small><ArrowRight size={15} aria-hidden="true"/></footer>
|
|
</a>)}
|
|
</div>
|
|
|
|
<section className="xb-panel xb-wide">
|
|
<h2>项目工作台</h2>
|
|
<p>需要按项目办理材料、报告与评审结论时,进入项目工作台。</p>
|
|
<div className="xb-actions"><a className="ra-button" href={withProject("/xingbang-workbench")}>进入项目工作台</a></div>
|
|
</section>
|
|
</main>
|
|
</div>;
|
|
}
|