Files
aiops/frontend/src/components/approval/ApprovalDetailDialog.tsx
T
attacker 5364d93ed7 fix(q1): 修复 tsc -b 构建中断 — 回退不安全的 unknown→any + 移除 Ansible 残留引用
- 781处 tsc -b 错误清零(unknown 属性访问/隐式any返回类型/类型转换不匹配)
- 移除 App.tsx 中已删除的 AnsibleManager/AnsibleWorkflow 导入和路由
- 修复 DeploymentView 中 _prefix 解构导致的属性不存在错误
- 修复 Header/Sidebar/PipelineEditor 中无效的类型断言
- asList() 返回类型 unknown[]→any[], 恢复 AgentMonitor 正常工作
- 放宽 noImplicitAny(临时), 待 types/ 定义完善后逐步恢复
- ESLint: unused=0, console=0, any=1041(Q1 后续降低)
- npm run build: ✅ 0 errors
2026-06-25 07:17:47 +08:00

141 lines
5.7 KiB
TypeScript

import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { CheckCircle2, XCircle, Clock } from 'lucide-react';
import {
renderFormData,
renderStepProgress,
getApprovalTypeLabel,
getApprovalTypeColor,
getInstanceType,
} from './helpers';
interface ApprovalDetailDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
instance: any;
templates: any[];
onApprove: ( instance: any) => void;
onReject: ( instance: any) => void;
}
// 审批详情弹窗 — 展示流程详情/表单/进度/操作记录 // Logan.Li
export function ApprovalDetailDialog({
open,
onOpenChange,
instance,
templates,
onApprove,
onReject,
}: ApprovalDetailDialogProps) {
if (!instance) return null;
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-[var(--dg-text-primary)]">审批详情</DialogTitle>
<DialogDescription className="text-[var(--dg-text-muted)]">查看审批流程详细信息</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-2">
<div>
<h3 className="text-lg font-medium text-[var(--dg-text-primary)]">{instance.title}</h3>
<div className="flex items-center gap-2 mt-1">
<Badge variant="outline" className="text-xs border-[var(--dg-border)]" style={{ color: getApprovalTypeColor(getInstanceType(instance)) }}>
{getApprovalTypeLabel(getInstanceType(instance))}
</Badge>
<span className="text-xs text-[var(--dg-text-muted)]">
申请人:{instance.applicant_name || instance.applicant || '--'}
</span>
<span className="text-xs text-[var(--dg-text-muted)]">
提交于 {instance.created_at ? new Date(instance.created_at).toLocaleString() : '--'}
</span>
</div>
</div>
{/* 审批内容 */}
{instance.content && (
<div className="bg-[var(--dg-bg-primary)] rounded-lg p-4">
<p className="text-sm text-[var(--dg-text-secondary)] whitespace-pre-wrap">{instance.content}</p>
</div>
)}
{/* 申请表单 */}
{(() => {
const tpl = templates.find((t) => t.id === instance.template_id);
const rendered = renderFormData(instance.form_data, tpl);
if (!rendered) return null;
return (
<div className="bg-[var(--dg-bg-primary)] rounded-lg p-4">
<p className="text-sm font-medium text-[var(--dg-text-secondary)] mb-3">申请表单</p>
{rendered}
</div>
);
})()}
{/* 审批进度 */}
<div className="bg-[var(--dg-bg-primary)] rounded-lg p-4">
<p className="text-sm font-medium text-[var(--dg-text-secondary)] mb-3">审批进度</p>
{renderStepProgress(instance)}
</div>
{/* 操作记录 */}
{instance.actions && (
<div className="bg-[var(--dg-bg-primary)] rounded-lg p-4">
<p className="text-sm font-medium text-[var(--dg-text-secondary)] mb-3">操作记录</p>
<div className="space-y-2">
{instance.actions.map(( action: any, index: number) => (
<div key={index} className="flex items-center gap-3 text-sm">
{action.action === 'approve' ? (
<CheckCircle2 className="h-4 w-4 text-emerald-400" />
) : action.action === 'reject' ? (
<XCircle className="h-4 w-4 text-red-400" />
) : (
<Clock className="h-4 w-4 text-[var(--dg-text-muted)]" />
)}
<div>
<span className="text-[var(--dg-text-secondary)]">
{action.operator_name || action.operator}
</span>
<span className="text-[var(--dg-text-muted)] mx-1">·</span>
<span className="text-[var(--dg-text-secondary)]">
{action.action === 'approve' ? '通过' : action.action === 'reject' ? '拒绝' : '提交'}
</span>
{action.comment && (
<p className="text-xs text-[var(--dg-text-muted)] mt-0.5">{action.comment}</p>
)}
</div>
<span className="text-xs text-[var(--dg-text-muted)] ml-auto">
{action.created_at ? new Date(action.created_at).toLocaleString() : ''}
</span>
</div>
))}
</div>
</div>
)}
</div>
<DialogFooter>
{instance.status === 'pending' && (
<>
<Button variant="outline" className="border-red-500/30 text-red-400 hover:bg-red-500/10" onClick={() => { onOpenChange(false); onReject(instance); }}>
<XCircle className="h-4 w-4 mr-2" />拒绝
</Button>
<Button className="bg-emerald-500/90 hover:bg-emerald-500 text-white" onClick={() => { onOpenChange(false); onApprove(instance); }}>
<CheckCircle2 className="h-4 w-4 mr-2" />通过
</Button>
</>
)}
<Button variant="ghost" onClick={() => onOpenChange(false)}>关闭</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}