外包风险评估系统:领域引擎+前端+服务端持久化与生产部署
- 确定性领域引擎(分类/评分/分级/红线/费用/裁决)+LLM(通义千问)语言理解 - 6步评估向导、服务端草稿持久化(跨设备/编辑草稿保护) - 工作流(草稿→风控→管理层)、RBAC、报告导出、校准、客户/费率/红线/最低工资管理 - 专业图标体系替换全部emoji、看板美化 - 生产化:API_BASE可配置(同源反代)、auth密钥惰性读取修复RBAC - 444单测+204前端测试+51 e2e
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user