Download src/components/CaseWorkflowTimeline.tsx from sgfw/wz: direct link, hf CLI and curl.
- Browser
- Download file 6.29 kB
-
https://huggingface.co/spaces/sgfw/wz/resolve/main/src/components/CaseWorkflowTimeline.tsx
- Command line
-
hf download hf://spaces/sgfw/wz/src/components/CaseWorkflowTimeline.tsx
-
curl -L -o CaseWorkflowTimeline.tsx https://huggingface.co/spaces/sgfw/wz/resolve/main/src/components/CaseWorkflowTimeline.tsx
6.29 kB
| /** | |
| * @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> | |
| ); | |
| } | |