feat: 定时任务编辑入口全栈打通 + 修复ApprovalFlow编译错误 + 完成大文件拆分重构
- CronJobManager: 编辑按钮接通 onClick → openEdit(job),弹出编辑对话框预填数据,
复用新建/编辑共用 Dialog(editingId 区分模式),标题/按钮文案动态切换,
保存时根据 editingId 调用 createJob 或 updateJob — 消除占位按钮
- ApprovalFlow: 修复 API 方法名不匹配导致 tsc -b 编译失败
(getInstances→getProcesses, getMyApprovals→getProcesses({status:'pending'}),
approveInstance→approveProcess, rejectInstance→rejectProcess)
+ 添加 ApprovalFlowManager 导出别名兼容 App.tsx 导入
- 合并已完成的大文件拆分重构: automation.go→automation_cron/script/playbook,
ldap.go→ldap_ou/ldap_users, server.go→cloud_accounts/domain/platform/region,
user.go→user_admin; 前端 Dashboard/K8s/Server/Approval/Monitor 子组件拆分
// Logan.Li
This commit is contained in:
1 parent
8be63ac168
commit
a7e2351ad2
37 files changed
+7374
-4891
No files matched your search
@@ -0,0 +1,185 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Filter, Edit, GitBranch, ArrowRight } from 'lucide-react';
|
||||
|
||||
function parseSteps(steps: any) {
|
||||
if (Array.isArray(steps)) return steps;
|
||||
if (typeof steps === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(steps);
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// 审批类型字典
|
||||
const APPROVAL_TYPE_OPTIONS = [
|
||||
{ value: 'deploy', label: '发布审批', color: 'var(--dg-blue)' },
|
||||
{ value: 'server', label: '服务器操作', color: 'var(--dg-cyan)' },
|
||||
{ value: 'permission', label: '权限申请', color: 'var(--dg-purple)' },
|
||||
];
|
||||
|
||||
const getApprovalTypeLabel = (value: string): string =>
|
||||
APPROVAL_TYPE_OPTIONS.find((o) => o.value === value)?.label || value || '通用审批';
|
||||
|
||||
const getApprovalTypeColor = (value: string): string =>
|
||||
APPROVAL_TYPE_OPTIONS.find((o) => o.value === value)?.color || 'var(--dg-text-secondary)';
|
||||
|
||||
// 模拟审批流程图
|
||||
function renderFlowChart(flow: any) {
|
||||
const nodes = parseSteps(flow.steps);
|
||||
if (nodes.length === 0) {
|
||||
return <div className="text-[var(--dg-text-muted)] text-center py-4">暂无流程节点</div>;
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center gap-2 overflow-x-auto py-4">
|
||||
{nodes.map((node: any, index: number) => (
|
||||
<div key={node.id || index} className="flex items-center">
|
||||
<div
|
||||
className={cn(
|
||||
'px-4 py-2 rounded-lg border min-w-[100px] text-center',
|
||||
node.type === 'start' && 'bg-emerald-500/10 border-emerald-500/30 text-emerald-400',
|
||||
node.type === 'approval' && 'bg-[var(--dg-blue)]/10 border-[var(--dg-blue)]/30 text-[var(--dg-blue)]',
|
||||
node.type === 'condition' && 'bg-amber-500/10 border-amber-500/30 text-amber-400',
|
||||
node.type === 'end' && 'bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-secondary)]'
|
||||
)}
|
||||
>
|
||||
<p className="text-sm font-medium">{node.name}</p>
|
||||
{node.approvers && (
|
||||
<p className="text-xs text-[var(--dg-text-muted)] mt-1">
|
||||
{Array.isArray(node.approvers) ? node.approvers.join(', ') : node.approvers}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{index < nodes.length - 1 && (
|
||||
<ArrowRight className="h-4 w-4 text-[var(--dg-text-muted)] mx-2" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ApprovalTemplateTabProps {
|
||||
filteredTemplates: any[];
|
||||
templateTypeFilter: string;
|
||||
setTemplateTypeFilter: (value: string) => void;
|
||||
}
|
||||
|
||||
export function ApprovalTemplateTab({
|
||||
filteredTemplates,
|
||||
templateTypeFilter,
|
||||
setTemplateTypeFilter,
|
||||
}: ApprovalTemplateTabProps) {
|
||||
return (
|
||||
<>
|
||||
{/* 类型筛选 */}
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<div className="flex items-center gap-1.5 text-sm text-[var(--dg-text-secondary)]">
|
||||
<Filter className="h-4 w-4" />
|
||||
<span>类型筛选:</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg p-1">
|
||||
<button
|
||||
onClick={() => setTemplateTypeFilter('all')}
|
||||
className={cn(
|
||||
'px-3 py-1.5 rounded-md text-sm transition-colors',
|
||||
templateTypeFilter === 'all'
|
||||
? 'bg-[var(--dg-bg-hover)] text-[var(--dg-text-primary)] font-medium'
|
||||
: 'text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)]'
|
||||
)}
|
||||
>
|
||||
全部类型
|
||||
</button>
|
||||
{APPROVAL_TYPE_OPTIONS.map((opt) => (
|
||||
<button
|
||||
key={opt.value}
|
||||
onClick={() => setTemplateTypeFilter(opt.value)}
|
||||
className={cn(
|
||||
'px-3 py-1.5 rounded-md text-sm transition-colors flex items-center gap-1.5',
|
||||
templateTypeFilter === opt.value
|
||||
? 'bg-[var(--dg-bg-hover)] text-[var(--dg-text-primary)] font-medium'
|
||||
: 'text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)]'
|
||||
)}
|
||||
>
|
||||
<span style={{ color: opt.color }}>●</span>
|
||||
{opt.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
{filteredTemplates.length === 0 ? (
|
||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardContent className="p-8">
|
||||
<div className="text-center">
|
||||
<GitBranch className="h-12 w-12 mx-auto text-[var(--dg-text-muted)] mb-4" />
|
||||
<h3 className="text-lg font-medium text-[var(--dg-text-secondary)] mb-2">暂无流程模板</h3>
|
||||
<p className="text-sm text-[var(--dg-text-muted)]">当前筛选条件下没有匹配的模板</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
filteredTemplates.map((flow) => (
|
||||
<Card key={flow.id} className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardHeader className="pb-2">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-[var(--dg-blue)]/10 flex items-center justify-center">
|
||||
<GitBranch className="h-5 w-5 text-[var(--dg-blue)]" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CardTitle className="text-base text-[var(--dg-text-primary)]">{flow.name}</CardTitle>
|
||||
{flow.type && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs border-[var(--dg-border)]"
|
||||
style={{ color: getApprovalTypeColor(flow.type) }}
|
||||
>
|
||||
{getApprovalTypeLabel(flow.type)}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<CardDescription className="text-[var(--dg-text-muted)]">{flow.description}</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={
|
||||
flow.status === 1 || flow.status === 'active' || flow.status === true
|
||||
? 'border-emerald-500/30 text-emerald-400 bg-emerald-500/10'
|
||||
: 'border-[var(--dg-border)] text-[var(--dg-text-secondary)] bg-[var(--dg-bg-tertiary)]'
|
||||
}
|
||||
>
|
||||
{flow.status === 1 || flow.status === 'active' || flow.status === true ? '已启用' : '已停用'}
|
||||
</Badge>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)]">
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="bg-[var(--dg-bg-primary)] rounded-lg p-4 overflow-x-auto">
|
||||
{renderFlowChart(flow)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-4 text-sm text-[var(--dg-text-muted)]">
|
||||
<span>创建于 {flow.created_at ? new Date(flow.created_at).toLocaleDateString() : '--'}</span>
|
||||
<span>{parseSteps(flow.steps).length} 个节点</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user