import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { cn } from '@/lib/utils'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Progress } from '@/components/ui/progress'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { Rocket, Plus, Search, Play, RotateCcw, CheckCircle, XCircle, Clock, GitBranch, GitCommit, User, MoreHorizontal, Eye, Settings, History, Pause, Loader2, ArrowLeft, ArrowRight, } from 'lucide-react'; import { releaseApi } from '@/services/api'; import { toast } from 'sonner'; export function AppReleaseManager() { const navigate = useNavigate(); const [searchQuery, setSearchQuery] = useState(''); const [envFilter, setEnvFilter] = useState('all'); const [statusFilter, setStatusFilter] = useState('all'); const [releases, setReleases] = useState([]); const [loading, setLoading] = useState(true); const [selectedRelease, setSelectedRelease] = useState(null); const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); const [submitting, setSubmitting] = useState(false); const [newRelease, setNewRelease] = useState({ appName: '', version: '', environment: 'dev', commitId: '', commitMsg: '', description: '', }); const loadData = async () => { try { setLoading(true); const data = await releaseApi.getReleases(); setReleases(data.list || []); } catch (error) { console.error('Failed to load releases:', error); } finally { setLoading(false); } }; useEffect(() => { loadData(); }, []); const filteredReleases = releases.filter((release) => { const matchesSearch = (release.app_name || '').toLowerCase().includes(searchQuery.toLowerCase()) || (release.version || '').includes(searchQuery); const matchesEnv = envFilter === 'all' || release.environment === envFilter; const matchesStatus = statusFilter === 'all' || release.status === statusFilter; return matchesSearch && matchesEnv && matchesStatus; }); const getStatusBadge = (status: any) => { switch (status) { case 'success': return ( 成功 ); case 'failed': return ( 失败 ); case 'deploying': return ( 发布中 ); case 'building': return ( 构建中 ); case 'rolled_back': return ( 已回滚 ); case 'pending': return ( 待发布 ); default: return null; } }; const getEnvBadge = (env: any) => { const colors: Record = { dev: 'border-slate-500/30 text-slate-400 bg-slate-500/10', test: 'border-blue-500/30 text-blue-400 bg-blue-500/10', staging: 'border-amber-500/30 text-amber-400 bg-amber-500/10', prod: 'border-emerald-500/30 text-emerald-400 bg-emerald-500/10', }; const labels: Record = { dev: '开发环境', test: '测试环境', staging: '预发布', prod: '生产环境', }; return ( {labels[env] || env} ); }; const handleRollback = async (id: any) => { try { await releaseApi.rollbackRelease(id); loadData(); } catch (error) { console.error('Failed to rollback release:', error); } }; // 新建发布提交(复用 releaseApi.createRelease) // Logan.Li const handleCreateRelease = async () => { if (!newRelease.appName.trim() || !newRelease.version.trim()) { toast.error('请填写应用名称和版本号'); return; } setSubmitting(true); try { await releaseApi.createRelease({ app_name: newRelease.appName.trim(), version: newRelease.version.trim(), environment: newRelease.environment, commit_id: newRelease.commitId.trim(), commit_msg: newRelease.commitMsg.trim(), description: newRelease.description.trim(), }); toast.success('发布申请已提交'); setIsCreateDialogOpen(false); setNewRelease({ appName: '', version: '', environment: 'dev', commitId: '', commitMsg: '', description: '' }); loadData(); } catch { toast.error('创建失败'); } finally { setSubmitting(false); } }; // 模拟流水线步骤 const pipelineSteps = [ { name: '代码检出', status: 'success', duration: '15s' }, { name: '依赖安装', status: 'success', duration: '45s' }, { name: '代码检查', status: 'success', duration: '30s' }, { name: '单元测试', status: 'success', duration: '2m 15s' }, { name: '构建镜像', status: 'success', duration: '3m 30s' }, { name: '推送镜像', status: 'success', duration: '1m 20s' }, { name: '部署到K8s', status: 'running', duration: '--' }, { name: '健康检查', status: 'pending', duration: '--' }, ]; return (
{/* Page Header */}

应用发布

CI/CD流水线管理与发布历史

{/* Stats Cards */}

今日发布

{releases.length}

发布成功

{releases.filter((r) => r.status === 'success').length}

发布失败

{releases.filter((r) => r.status === 'failed').length}

成功率

{releases.length > 0 ? `${Math.round((releases.filter((r) => r.status === 'success').length / releases.length) * 100)}%` : '0%'}

{/* Filters */}
setSearchQuery(e.target.value)} className="pl-9 h-9 bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500" />
{/* Releases Table */}
应用名称 版本 环境 状态 代码 发布人 开始时间 操作 {loading ? ( 加载中... ) : filteredReleases.map((release) => (
{release.app_name}
{release.version} {getEnvBadge(release.environment)} {getStatusBadge(release.status)}
{release.branch} · {release.commit_id}
{release.deployer} {release.created_at ? new Date(release.created_at).toLocaleString() : '--'}
{release.status === 'failed' && ( )} {release.status === 'success' && ( )}
))}
{/* Release Detail Dialog */} setSelectedRelease(null)}> {selectedRelease?.app_name} {selectedRelease?.version} 环境: {selectedRelease?.environment}
{/* Pipeline Steps */}

发布进度

{pipelineSteps.map((step, index) => (
{step.status === 'success' ? ( ) : step.status === 'running' ? ( ) : ( {index + 1} )}
{step.name} {step.duration}
))}
{/* Build Logs */}

构建日志

[10:00:00] Starting pipeline for {selectedRelease?.app_name}...

[10:00:15] Checkout code from branch {selectedRelease?.branch}

[10:00:30] Installing dependencies...

[10:01:15] Dependencies installed successfully

[10:01:15] Running code analysis...

[10:01:45] Code analysis passed

[10:01:45] Running unit tests...

[10:04:00] All tests passed (156/156)

[10:04:00] Building Docker image...

[10:07:30] Image built successfully

[10:07:30] Pushing image to registry...

[10:08:50] Image pushed successfully

[10:08:50] Deploying to Kubernetes...

[10:10:00] Deployment in progress...

{selectedRelease?.status === 'failed' && ( )}
{/* Create Release Dialog */} 新建发布 创建新的应用版本发布申请
setNewRelease({ ...newRelease, appName: e.target.value })} placeholder="如:用户中心服务" className="mt-1 bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500" />
setNewRelease({ ...newRelease, version: e.target.value })} placeholder="v1.0.0" className="mt-1 bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500" />
setNewRelease({ ...newRelease, commitId: e.target.value })} placeholder="abc1234" className="mt-1 bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500" />
setNewRelease({ ...newRelease, commitMsg: e.target.value })} placeholder="feat: 新增功能" className="mt-1 bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500" />