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
@@ -3,15 +3,8 @@ import { cn } from '@/lib/utils';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from '@/components/ui/collapsible';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -28,14 +21,6 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -59,49 +44,12 @@ import {
|
||||
Cloud,
|
||||
FileSpreadsheet,
|
||||
RefreshCw,
|
||||
ChevronDown,
|
||||
Loader2,
|
||||
Save,
|
||||
} from 'lucide-react';
|
||||
import { serverApi } from '@/services/api';
|
||||
import type { Server as ServerType } from '@/types';
|
||||
import { CloudServerImport } from './CloudServerImport';
|
||||
import { ExcelServerImport } from './ExcelServerImport';
|
||||
|
||||
// 表单数据结构 — 与后端 CreateServerRequest/UpdateServerRequest 字段对齐
|
||||
interface ServerFormData {
|
||||
hostname: string;
|
||||
ip: string;
|
||||
public_ip: string;
|
||||
port: string;
|
||||
username: string;
|
||||
auth_type: string; // password | key
|
||||
password: string;
|
||||
os: string;
|
||||
cpu: string;
|
||||
memory: string; // GB (表单) → MB (后端)
|
||||
disk: string;
|
||||
status: string;
|
||||
tags: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const EMPTY_FORM: ServerFormData = {
|
||||
hostname: '',
|
||||
ip: '',
|
||||
public_ip: '',
|
||||
port: '22',
|
||||
username: 'root',
|
||||
auth_type: 'password',
|
||||
password: '',
|
||||
os: '',
|
||||
cpu: '',
|
||||
memory: '',
|
||||
disk: '',
|
||||
status: 'running',
|
||||
tags: '',
|
||||
description: '',
|
||||
};
|
||||
import { ServerForm, EMPTY_FORM, type ServerFormData } from './ServerForm';
|
||||
|
||||
export function ServerManager() {
|
||||
const [servers, setServers] = useState<any[]>([]);
|
||||
@@ -660,230 +608,17 @@ export function ServerManager() {
|
||||
</Card>
|
||||
|
||||
{/* 创建/编辑服务器 Dialog */}
|
||||
<Dialog open={formOpen} onOpenChange={setFormOpen}>
|
||||
<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)] flex items-center gap-2">
|
||||
<Server className="h-5 w-5 text-[var(--dg-blue)]" />
|
||||
{editingId ? '编辑服务器' : '添加服务器'}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-[var(--dg-text-muted)]">
|
||||
{editingId ? '修改服务器资产信息' : '录入新的服务器资产到 CMDB'}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 py-2">
|
||||
{/* 基础信息 */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">
|
||||
主机名 <span className="text-red-400">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
placeholder="web-server-01"
|
||||
value={formData.hostname}
|
||||
onChange={(e) => setFormData({ ...formData, hostname: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">
|
||||
IP地址 <span className="text-red-400">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
placeholder="10.0.0.1"
|
||||
value={formData.ip}
|
||||
onChange={(e) => setFormData({ ...formData, ip: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* SSH 连接配置 */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">公网IP</Label>
|
||||
<Input
|
||||
placeholder="如 203.0.113.10"
|
||||
value={formData.public_ip}
|
||||
onChange={(e) => setFormData({ ...formData, public_ip: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">SSH端口</Label>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="22"
|
||||
value={formData.port}
|
||||
onChange={(e) => setFormData({ ...formData, port: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">登录用户</Label>
|
||||
<Input
|
||||
placeholder="root"
|
||||
value={formData.username}
|
||||
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">认证方式</Label>
|
||||
<Select
|
||||
value={formData.auth_type}
|
||||
onValueChange={(v) => setFormData({ ...formData, auth_type: v })}
|
||||
>
|
||||
<SelectTrigger className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]">
|
||||
<SelectItem value="password">密码认证</SelectItem>
|
||||
<SelectItem value="key">密钥认证</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">
|
||||
{formData.auth_type === 'key' ? 'SSH 私钥' : '登录密码'}
|
||||
{!editingId && <span className="text-red-400 ml-1">*</span>}
|
||||
{editingId && <span className="text-[var(--dg-text-muted)] ml-1 text-xs">(留空则不修改)</span>}
|
||||
</Label>
|
||||
<Textarea
|
||||
placeholder={formData.auth_type === 'key' ? '粘贴 SSH 私钥内容...' : '输入登录密码...'}
|
||||
value={formData.password}
|
||||
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] min-h-[50px] font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 更多详情 - 可折叠区域 */}
|
||||
<Collapsible open={detailsOpen} onOpenChange={setDetailsOpen}>
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full justify-between text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)]"
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<Server className="h-4 w-4" />
|
||||
更多详情(OS版本 / 运行状态 / CPU / 内存 / 磁盘 / 标签 / 描述)
|
||||
</span>
|
||||
<ChevronDown className={cn('h-4 w-4 transition-transform', detailsOpen && 'rotate-180')} />
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="space-y-4 pt-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">操作系统</Label>
|
||||
<Input
|
||||
placeholder="如 CentOS 7.9"
|
||||
value={formData.os}
|
||||
onChange={(e) => setFormData({ ...formData, os: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">运行状态</Label>
|
||||
<Select
|
||||
value={formData.status}
|
||||
onValueChange={(v) => setFormData({ ...formData, status: v })}
|
||||
>
|
||||
<SelectTrigger className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]">
|
||||
<SelectItem value="running">运行中</SelectItem>
|
||||
<SelectItem value="stopped">已停止</SelectItem>
|
||||
<SelectItem value="maintenance">维护中</SelectItem>
|
||||
<SelectItem value="offline">离线</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">CPU核数</Label>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="4"
|
||||
value={formData.cpu}
|
||||
onChange={(e) => setFormData({ ...formData, cpu: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">内存 (GB)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="8"
|
||||
value={formData.memory}
|
||||
onChange={(e) => setFormData({ ...formData, memory: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">磁盘 (GB)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="100"
|
||||
value={formData.disk}
|
||||
onChange={(e) => setFormData({ ...formData, disk: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">标签</Label>
|
||||
<Input
|
||||
placeholder='JSON 格式,如 {"project":"web","team":"ops"}'
|
||||
value={formData.tags}
|
||||
onChange={(e) => setFormData({ ...formData, tags: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">描述</Label>
|
||||
<Textarea
|
||||
placeholder="补充说明..."
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] min-h-[60px]"
|
||||
/>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setFormOpen(false)}
|
||||
className="border-[var(--dg-border)] text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)] hover:text-[var(--dg-text-primary)]"
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={!formData.hostname || !formData.ip || formSaving}
|
||||
className="bg-[var(--dg-blue)] hover:opacity-90 text-white"
|
||||
>
|
||||
{formSaving ? (
|
||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||
) : (
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
)}
|
||||
{editingId ? '保存修改' : '创建'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<ServerForm
|
||||
open={formOpen}
|
||||
onOpenChange={setFormOpen}
|
||||
editingId={editingId}
|
||||
formData={formData}
|
||||
setFormData={setFormData}
|
||||
detailsOpen={detailsOpen}
|
||||
setDetailsOpen={setDetailsOpen}
|
||||
onSave={handleSave}
|
||||
formSaving={formSaving}
|
||||
/>
|
||||
|
||||
{/* Import Dialogs */}
|
||||
<CloudServerImport
|
||||
|
||||
Reference in new issue
Block a user