File size: 6,290 Bytes
f4cac62 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /**
* @license
* SPDX-License-Identifier: Apache-2.0
*/
import React from 'react';
import { AccountabilityCase } from '../types';
import { Check, AlertCircle, Clock, CheckCircle2 } from 'lucide-react';
interface TimelineProps {
currentCase: AccountabilityCase;
}
export const STEPS_DEFINITION = [
{ index: 1, name: '启动立案', desc: '成立问责调查组,录入案件基础信息' },
{ index: 2, name: '调查核实', desc: '开展谈话、送达事实见面材料、核查申辩、形成调查报告' },
{ index: 3, name: '合规审理', desc: '审理职责部门受理并出具合规审理意见书' },
{ index: 4, name: '会议审议', desc: '问责委员会委员召开会议并线上表决与签名' },
{ index: 5, name: '申诉窗口期', desc: '10个工作日内申诉确认或直接进入发文阶段' },
{ index: 6, name: '提起申诉', desc: '被问责人提交申诉材料(有异议时触发)' },
{ index: 7, name: '指派复查', desc: '指派审理组或协助调查组,出具复查意见' },
{ index: 8, name: '按程序复议', desc: '重新提请委员会,做出复议决议' },
{ index: 9, name: '发文归档', desc: '下发红头问责文件,抄送人力资源部归档' }
];
export default function CaseWorkflowTimeline({ currentCase }: TimelineProps) {
const currentStep = currentCase.currentStep;
// 判断哪些步骤被跳过或不需要
// 如果案件没有申诉(在步骤5时选择无异议,或者已直接跳到步骤9),那么 6, 7, 8 被视为 skipped
const isSkippedStep = (stepIdx: number): boolean => {
if (!currentCase.hasAppeal && [6, 7, 8].includes(stepIdx)) {
// 已经完成申诉期,或者在申诉期之前,且未触发申诉
if (currentStep === 9 || currentStep === 5) {
return true;
}
}
return false;
};
const getStepStatus = (stepIdx: number): 'completed' | 'active' | 'pending' | 'skipped' => {
if (isSkippedStep(stepIdx)) return 'skipped';
if (currentStep > stepIdx) return 'completed';
if (currentStep === stepIdx) return 'active';
return 'pending';
};
// 期限计算说明
const limitText = currentCase.category === 'asset'
? '问题资产:启动后90天内需完成调查'
: '非问题资产:发起后30个工作日内需完成调查';
return (
<div className="bg-white border border-slate-200 rounded-lg p-4 shadow-2xs" id="workflow-timeline-card">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-2 mb-4 border-b border-slate-100 pb-3">
<div>
<h3 className="text-sm font-bold text-slate-900 flex items-center gap-1.5">
<Clock className="w-4 h-4 text-slate-500" />
银行问责生命周期追踪
</h3>
<p className="text-[11px] text-slate-500 mt-0.5">流程闭环可追溯,确保每个程序节点合规守信</p>
</div>
<div className="flex items-center gap-1.5 bg-slate-50 border border-slate-200 px-2.5 py-1 rounded text-[11px] font-medium text-slate-600">
<AlertCircle className="w-3.5 h-3.5 text-amber-500" />
<span>时限要求:{limitText}</span>
</div>
</div>
{/* 9步流程横向/纵向进度条 */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-9 gap-2 relative">
{STEPS_DEFINITION.map((step) => {
const status = getStepStatus(step.index);
return (
<div
key={step.index}
className={`relative flex flex-col p-2 rounded border transition-all ${
status === 'completed'
? 'bg-slate-50/80 border-slate-200 text-slate-700'
: status === 'active'
? 'bg-blue-50/50 border-blue-200 ring-2 ring-blue-500/5 text-blue-900'
: status === 'skipped'
? 'bg-slate-100/50 border-slate-200/50 text-slate-400 opacity-60'
: 'bg-white border-slate-100 text-slate-400'
}`}
>
{/* 编号 & 状态图标 */}
<div className="flex justify-between items-center mb-1">
<span className={`text-[9px] font-mono font-bold uppercase tracking-wider px-1 py-0.5 rounded ${
status === 'completed'
? 'bg-slate-200 text-slate-700'
: status === 'active'
? 'bg-blue-600 text-white'
: status === 'skipped'
? 'bg-slate-200/60 text-slate-400'
: 'bg-slate-100 text-slate-400'
}`}>
STEP 0{step.index}
</span>
{status === 'completed' && <Check className="w-3.5 h-3.5 text-emerald-600" />}
{status === 'active' && (
<span className="flex h-1.5 w-1.5 relative">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-blue-600"></span>
</span>
)}
{status === 'skipped' && <span className="text-[9px] text-slate-400">跳过</span>}
</div>
{/* 步骤名称 */}
<h4 className={`text-[11px] font-bold ${
status === 'active' ? 'text-blue-600' : 'text-slate-800'
}`}>
{step.name}
</h4>
{/* 简单说明 */}
<p className="text-[10px] leading-relaxed text-slate-500 mt-1 line-clamp-2 md:line-clamp-none">
{step.desc}
</p>
{/* 进度提示线 - 仅在非最后一步且大屏幕时显示 */}
{step.index < 9 && (
<div className="hidden lg:block absolute -right-1.5 top-1/2 -translate-y-1/2 z-10">
<span className={`text-sm font-bold ${
status === 'completed' ? 'text-slate-300' : 'text-slate-100'
}`}>→</span>
</div>
)}
</div>
);
})}
</div>
</div>
);
}
|