c670b9e454
- 确定性领域引擎(分类/评分/分级/红线/费用/裁决)+LLM(通义千问)语言理解 - 6步评估向导、服务端草稿持久化(跨设备/编辑草稿保护) - 工作流(草稿→风控→管理层)、RBAC、报告导出、校准、客户/费率/红线/最低工资管理 - 专业图标体系替换全部emoji、看板美化 - 生产化:API_BASE可配置(同源反代)、auth密钥惰性读取修复RBAC - 444单测+204前端测试+51 e2e
146 lines
5.9 KiB
TypeScript
146 lines
5.9 KiB
TypeScript
/**
|
||
* DefaultView — 角色化默认首屏视图(task 23.3,Req 25.1–25.5)。
|
||
*
|
||
* 给定登录用户角色,本组件渲染与该角色匹配的默认视图首屏,并将角色匹配的功能入口
|
||
* 呈现于无需额外导航即可见的位置(Req 25.4)。目标视图复用任务 15 的角色化视图
|
||
* (renderView/renderPortfolio)作为导航目标;此处呈现进入这些视图的功能入口
|
||
* (而非领域侧的完整视图实现)。
|
||
*
|
||
* 路由目标由纯函数 `resolveDefaultView` 决定(见 routing.ts,Property 81):
|
||
* 商务/销售 → SalesView、风控 → RiskView、管理层 → ManagementDashboard。
|
||
* 对 `无角色` 渲染「需分配角色」提示视图,且不呈现任何评估数据(Req 25.5)。
|
||
*
|
||
* 可访问性:以 `<section>` 区域 + 标题层级表达结构;功能入口以 `Nav` 语义化导航
|
||
* 暴露(landmark),便于辅助技术直接定位(Req 25.4)。视觉值统一取自 Design Tokens。
|
||
*/
|
||
|
||
import type { CSSProperties } from 'react';
|
||
import { Card, Nav } from '../design-system/index.js';
|
||
import type { NavItem } from '../design-system/index.js';
|
||
import { colorVar, FONT_FAMILY, space, typographyStyle } from '../design-system/components/styles.js';
|
||
import { resolveDefaultView } from './routing.js';
|
||
import type { Role, Route } from './routing.js';
|
||
|
||
/** 单条功能入口定义(角色匹配,呈现为导航目标)。 */
|
||
interface EntryPoint {
|
||
/** 唯一键(亦作为导航目标标识)。 */
|
||
readonly key: string;
|
||
/** 显示文本。 */
|
||
readonly label: string;
|
||
/** 进入目标视图的路由片段(占位链接,领域侧视图于任务 15 实现)。 */
|
||
readonly href: string;
|
||
}
|
||
|
||
/** 每个角色默认视图的标题与可见功能入口(Req 25.4)。 */
|
||
interface ViewDescriptor {
|
||
/** 默认视图标题。 */
|
||
readonly heading: string;
|
||
/** 功能入口导航的无障碍标签。 */
|
||
readonly navLabel: string;
|
||
/** 角色匹配的功能入口集合(首屏可见,无需额外导航)。 */
|
||
readonly entryPoints: readonly EntryPoint[];
|
||
}
|
||
|
||
/**
|
||
* 路由 → 默认视图描述符。功能入口对应任务 15 角色化视图的关键能力区块:
|
||
* - SalesView:结论 / 接受条件 / 风险调整后报价(Req 13.1)。
|
||
* - RiskView:评分明细 / 红线检查 / 缺口尽调(Req 13.2)。
|
||
* - ManagementDashboard:风险热力图 / TopN 风险 / 利润 vs 风险看板(Req 13.3)。
|
||
* `AssignRolePrompt` 不在此表内——其不呈现任何评估数据(Req 25.5)。
|
||
*/
|
||
const VIEW_DESCRIPTORS: Readonly<Record<Exclude<Route, 'AssignRolePrompt'>, ViewDescriptor>> = {
|
||
SalesView: {
|
||
heading: '商务/销售视图',
|
||
navLabel: '商务/销售功能入口',
|
||
entryPoints: [
|
||
{ key: 'sales-conclusion', label: '评估结论', href: '#/sales/conclusion' },
|
||
{ key: 'sales-acceptance', label: '接受条件', href: '#/sales/acceptance' },
|
||
{ key: 'sales-quote', label: '风险调整后报价', href: '#/sales/quote' },
|
||
],
|
||
},
|
||
RiskView: {
|
||
heading: '风控视图',
|
||
navLabel: '风控功能入口',
|
||
entryPoints: [
|
||
{ key: 'risk-scoring', label: '评分明细', href: '#/risk/scoring' },
|
||
{ key: 'risk-redline', label: '红线检查', href: '#/risk/redline' },
|
||
{ key: 'risk-gap', label: '缺口尽调', href: '#/risk/gap' },
|
||
],
|
||
},
|
||
ManagementDashboard: {
|
||
heading: '管理层看板',
|
||
navLabel: '管理层功能入口',
|
||
entryPoints: [
|
||
{ key: 'mgmt-heatmap', label: '风险热力图', href: '#/management/heatmap' },
|
||
{ key: 'mgmt-topn', label: 'TopN 风险', href: '#/management/topn' },
|
||
{ key: 'mgmt-profit-risk', label: '利润 vs 风险看板', href: '#/management/profit-risk' },
|
||
],
|
||
},
|
||
} as const;
|
||
|
||
/** `DefaultView` 组件属性。 */
|
||
export interface DefaultViewProps {
|
||
/** 登录用户角色。 */
|
||
readonly role: Role;
|
||
}
|
||
|
||
/**
|
||
* 渲染角色匹配的默认首屏视图。
|
||
*
|
||
* - 已分配业务角色:渲染对应视图标题与首屏可见的功能入口导航(Req 25.1–25.4)。
|
||
* - 无角色:渲染「需分配角色」提示,且不呈现任何评估数据(Req 25.5)。
|
||
*/
|
||
export function DefaultView({ role }: DefaultViewProps): JSX.Element {
|
||
const { route, showsAssessmentData } = resolveDefaultView(role);
|
||
|
||
const containerStyle: CSSProperties = {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: `${space(4)}px`,
|
||
fontFamily: FONT_FAMILY,
|
||
color: colorVar('color.text.primary'),
|
||
};
|
||
|
||
if (route === 'AssignRolePrompt') {
|
||
// Req 25.5:未分配业务角色 → 提示分配角色,且不呈现任何评估数据。
|
||
return (
|
||
<section
|
||
data-default-view="true"
|
||
data-route={route}
|
||
data-shows-assessment-data={showsAssessmentData}
|
||
aria-label="需分配角色"
|
||
style={containerStyle}
|
||
>
|
||
<h1 style={{ ...typographyStyle('heading'), margin: 0 }}>需分配角色</h1>
|
||
<Card title="暂无可访问的评估视图">
|
||
<p style={{ ...typographyStyle('body'), margin: 0 }}>
|
||
当前账号尚未分配商务/销售、风控或管理层角色,暂无法查看评估数据。
|
||
请联系管理员为您分配相应角色后重试。
|
||
</p>
|
||
</Card>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
const descriptor = VIEW_DESCRIPTORS[route];
|
||
const navItems: readonly NavItem[] = descriptor.entryPoints.map((entry) => ({
|
||
key: entry.key,
|
||
label: entry.label,
|
||
href: entry.href,
|
||
}));
|
||
|
||
return (
|
||
<section
|
||
data-default-view="true"
|
||
data-route={route}
|
||
data-shows-assessment-data={showsAssessmentData}
|
||
aria-label={descriptor.heading}
|
||
style={containerStyle}
|
||
>
|
||
<h1 style={{ ...typographyStyle('heading'), margin: 0 }}>{descriptor.heading}</h1>
|
||
{/* Req 25.4:角色匹配的功能入口呈现于无需额外导航即可见的位置。 */}
|
||
<Nav items={navItems} ariaLabel={descriptor.navLabel} />
|
||
</section>
|
||
);
|
||
}
|