import React, { useState, useEffect } from ‘react’;
import {
ChevronRight,
CheckCircle,
Circle,
AlertCircle,
Clock,
Play,
Pause,
Settings,
Code,
Server,
Shield,
Database,
Globe,
Zap,
FileText,
Users,
GitBranch,
Package,
Monitor,
ChevronDown
} from ‘lucide-react’;
const DevOpsPipelineSystem = () => {
const [expandedStep, setExpandedStep] = useState(null);
const [completedSteps, setCompletedSteps] = useState(new Set());
const [stepStatuses, setStepStatuses] = useState({});
const [isProcessing, setIsProcessing] = useState(false);
const [currentExecutingStep, setCurrentExecutingStep] = useState(null);
// 主流程定義
const mainPipeline = [
{
id: ‘preparation’,
title: ‘準備階段’,
icon: FileText,
color: ‘#3B82F6’,
description: ‘項目初始化和環境準備’,
subSteps: [
{ id: ‘code-review’, title: ‘代碼審查’, icon: Code, description: ‘代碼質量檢查和同行評審’, details: ‘執行靜態代碼分析,檢查代碼規范,確保代碼質量符合團隊標準。’ },
{ id: ‘dependency-check’, title: ‘依賴檢查’, icon: Package, description: ‘檢查項目依賴和版本兼容性’, details: ‘掃描項目依賴,檢查版本沖突,確保所有依賴項都是最新且兼容的版本。’ },
{ id: ‘env-config’, title: ‘環境配置’, icon: Settings, description: ‘配置部署環境參數’, details: ‘設置環境變量、配置文件、數據庫連接等部署所需的各項參數。’ }
]
},
{
id: ‘build’,
title: ‘構建階段’,
icon: Package,
color: ‘#10B981’,
description: ‘代碼編譯和打包’,
subSteps: [
{ id: ‘compile’, title: ‘代碼編譯’, icon: Code, description: ‘編譯源代碼并生成可執行文件’, details: ‘使用構建工具編譯源代碼,處理TypeScript、Sass等預處理文件。’ },
{ id: ‘unit-test’, title: ‘單元測試’, icon: CheckCircle, description: ‘執行單元測試確保代碼質量’, details: ‘運行所有單元測試用例,確保代碼覆蓋率達到標準,驗證功能正確性。’ },
{ id: ‘package-build’, title: ‘打包構建’, icon: Package, description: ‘打包應用程序和資源文件’, details: ‘將編譯后的代碼和資源文件打包成可部署的格式,優化文件大小。’ }
]
},
{
id: ‘security’,
title: ‘安全檢測’,
icon: Shield,
color: ‘#F59E0B’,
description: ‘安全漏洞掃描和檢測’,
subSteps: [
{ id: ‘vulnerability-scan’, title: ‘漏洞掃描’, icon: Shield, description: ‘掃描代碼中的安全漏洞’, details: ‘使用安全掃描工具檢測SQL注入、XSS等常見安全漏洞。’ },
{ id: ‘dependency-security’, title: ‘依賴安全檢查’, icon: AlertCircle, description: ‘檢查第三方依賴的安全性’, details: ‘掃描第三方依賴包,檢查是否存在已知的安全漏洞和風險。’ },
{ id: ‘compliance-check’, title: ‘合規性檢查’, icon: FileText, description: ‘確保符合安全合規要求’, details: ‘驗證應用程序是否符合GDPR、SOX等法規要求和公司安全政策。’ }
]
},
{
id: ‘deployment’,
title: ‘部署階段’,
icon: Server,
color: ‘#8B5CF6’,
description: ‘應用程序部署和配置’,
subSteps: [
{ id: ‘staging-deploy’, title: ‘預發布部署’, icon: Server, description: ‘部署到預發布環境進行測試’, details: ‘將應用部署到預發布環境,進行功能驗證和性能測試。’ },
{ id: ‘integration-test’, title: ‘集成測試’, icon: Zap, description: ‘執行集成測試驗證功能’, details: ‘在預發布環境中執行端到端測試,驗證各系統間的集成。’ },
{ id: ‘production-deploy’, title: ‘生產部署’, icon: Globe, description: ‘部署到生產環境’, details: ‘使用藍綠部署或滾動更新方式,將應用安全地部署到生產環境。’ }
]
},
{
id: ‘monitoring’,
title: ‘監控驗證’,
icon: Monitor,
color: ‘#EF4444’,
description: ‘系統監控和健康檢查’,
subSteps: [
{ id: ‘health-check’, title: ‘健康檢查’, icon: Monitor, description: ‘檢查應用程序運行狀態’, details: ‘驗證應用服務、數據庫連接、外部API等關鍵組件的健康狀態。’ },
{ id: ‘performance-test’, title: ‘性能測試’, icon: Zap, description: ‘驗證系統性能指標’, details: ‘測試應用的響應時間、吞吐量、資源使用率等性能指標。’ },
{ id: ‘user-acceptance’, title: ‘用戶驗收’, icon: Users, description: ‘用戶驗收測試’, details: ‘邀請關鍵用戶進行驗收測試,確保功能滿足業務需求。’ }
]
}
];
const executeStep = async (stepId) => {
setIsProcessing(true);
setCurrentExecutingStep(stepId);
setStepStatuses(prev => ({ …prev, [stepId]: ‘running’ }));
// 模擬處理時間
await new Promise(resolve => setTimeout(resolve, 3000));setStepStatuses(prev => ({ ...prev, [stepId]: 'completed' }));
setCompletedSteps(prev => new Set([...prev, stepId]));
setIsProcessing(false);
setCurrentExecutingStep(null);
};
const getStepStatus = (stepId) => {
if (stepStatuses[stepId] === ‘running’) return ‘running’;
if (completedSteps.has(stepId)) return ‘completed’;
return ‘pending’;
};
const getMainStepStatus = (step) => {
const completedSubSteps = step.subSteps.filter(subStep => completedSteps.has(subStep.id)).length;
if (completedSubSteps === step.subSteps.length) return ‘completed’;
if (completedSubSteps > 0) return ‘running’;
return ‘pending’;
};
const StepIndicator = ({ status, icon: Icon, color, size = 20 }) => {
const statusStyles = {
completed: {
background: ‘#10B981’,
border: ‘2px solid #10B981’,
color: ‘white’
},
running: {
background: color,
border: 2px solid ${color}
,
color: ‘white’,
animation: ‘pulse 2s infinite’
},
pending: {
background: ‘rgba(255, 255, 255, 0.1)’,
border: ‘2px solid rgba(255, 255, 255, 0.3)’,
color: ‘rgba(255, 255, 255, 0.6)’
}
};
return (<div className="step-indicator"style={statusStyles[status]}>{status === 'completed' ? <CheckCircle size={size} /> : <Icon size={size} />}</div>
);
};
const toggleExpanded = (stepId) => {
setExpandedStep(expandedStep === stepId ? null : stepId);
};
return (
DevOps 發布系統
智能化產品發布流水線
<div className="system-status"><div className="status-item"><span className="label">總體進度</span><span className="value">{Math.round((completedSteps.size / mainPipeline.reduce((acc, step) => acc + step.subSteps.length, 0)) * 100)}%</span></div><div className="status-item"><span className="label">已完成步驟</span><span className="value">{completedSteps.size} / {mainPipeline.reduce((acc, step) => acc + step.subSteps.length, 0)}</span></div></div></div></div><div className="system-content"><div className="pipeline-container"><div className="pipeline-header"><h2>產品發布流水線</h2><p>點擊各階段卡片展開詳細步驟,引導您完成完整的產品上線流程</p></div>{/* 主流程總覽 */}<div className="main-pipeline">{mainPipeline.map((step, index) => (<React.Fragment key={step.id}><div className={`pipeline-step ${expandedStep === step.id ? 'expanded' : ''} ${getMainStepStatus(step)}`}onClick={() => toggleExpanded(step.id)}><StepIndicator status={getMainStepStatus(step)}icon={step.icon}color={step.color}size={24}/><div className="step-content"><h3>{step.title}</h3><p>{step.description}</p><div className="step-progress"><div className="progress-bar"><div className="progress-fill"style={{width: `${(step.subSteps.filter(subStep => completedSteps.has(subStep.id)).length / step.subSteps.length) * 100}%`,background: step.color}}/></div><span className="progress-text">{step.subSteps.filter(subStep => completedSteps.has(subStep.id)).length} / {step.subSteps.length}</span></div></div><div className="expand-indicator"><ChevronDown size={20} className={`expand-arrow ${expandedStep === step.id ? 'rotated' : ''}`}/></div>{expandedStep === step.id && (<div className="active-border" style={{ background: step.color }} />)}</div>{index < mainPipeline.length - 1 && (<div className="pipeline-connector"><ChevronRight size={24} /></div>)}</React.Fragment>))}</div>{/* 詳細步驟展開區域 */}{expandedStep && (<div className="detailed-steps-section"><div className="section-header"><h3>{mainPipeline.find(s => s.id === expandedStep)?.title} - 詳細步驟</h3><p>按順序執行以下步驟完成 {mainPipeline.find(s => s.id === expandedStep)?.title}</p></div><div className="substeps-grid">{mainPipeline.find(s => s.id === expandedStep)?.subSteps.map((subStep, index) => (<div key={subStep.id}className={`substep-card ${getStepStatus(subStep.id)} ${currentExecutingStep === subStep.id ? 'executing' : ''}`}><div className="card-header"><StepIndicator status={getStepStatus(subStep.id)}icon={subStep.icon}color={mainPipeline.find(s => s.id === expandedStep)?.color}size={20}/><div className="step-number">步驟 {index + 1}</div></div><div className="card-content"><h4>{subStep.title}</h4><p className="description">{subStep.description}</p><p className="details">{subStep.details}</p></div><div className="card-actions">{getStepStatus(subStep.id) === 'pending' && (<button className="execute-btn"onClick={() => executeStep(subStep.id)}disabled={isProcessing}style={{ background: mainPipeline.find(s => s.id === expandedStep)?.color }}>{currentExecutingStep === subStep.id ? (<><Clock size={16} />執行中...</>) : (<><Play size={16} />開始執行</>)}</button>)}{getStepStatus(subStep.id) === 'completed' && (<div className="completed-badge"><CheckCircle size={16} />已完成</div>)}{getStepStatus(subStep.id) === 'running' && (<div className="running-badge"><Clock size={16} />執行中</div>)}</div></div>))}</div></div>)}</div></div><style jsx>{`.devops-system {min-height: 100vh;background: linear-gradient(135deg, #1e1b4b 0%, #312e81 50%, #1e3a8a 100%);font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;color: white;}.system-header {background: rgba(30, 27, 75, 0.8);backdrop-filter: blur(20px);border-bottom: 1px solid rgba(255, 255, 255, 0.1);padding: 24px 40px;}.header-content {display: flex;justify-content: space-between;align-items: center;max-width: 1400px;margin: 0 auto;}.logo {display: flex;align-items: center;gap: 16px;}.logo h1 {margin: 0;font-size: 28px;font-weight: 700;background: linear-gradient(135deg, #60a5fa, #a78bfa);-webkit-background-clip: text;-webkit-text-fill-color: transparent;}.logo p {margin: 0;color: rgba(255, 255, 255, 0.7);font-size: 14px;}.system-status {display: flex;gap: 32px;}.status-item {text-align: right;}.status-item .label {display: block;font-size: 12px;color: rgba(255, 255, 255, 0.6);margin-bottom: 4px;}.status-item .value {display: block;font-size: 18px;font-weight: 600;color: white;}.system-content {padding: 40px;max-width: 1400px;margin: 0 auto;}.pipeline-container {background: rgba(255, 255, 255, 0.05);backdrop-filter: blur(10px);border-radius: 20px;border: 1px solid rgba(255, 255, 255, 0.1);padding: 40px;}.pipeline-header {text-align: center;margin-bottom: 40px;}.pipeline-header h2 {font-size: 32px;margin: 0 0 12px 0;background: linear-gradient(135deg, #60a5fa, #a78bfa);-webkit-background-clip: text;-webkit-text-fill-color: transparent;}.pipeline-header p {color: rgba(255, 255, 255, 0.7);font-size: 16px;margin: 0;}.main-pipeline {display: flex;align-items: center;gap: 24px;margin-bottom: 40px;overflow-x: auto;padding: 20px 0;}.pipeline-step {background: rgba(255, 255, 255, 0.08);border: 2px solid rgba(255, 255, 255, 0.1);border-radius: 16px;padding: 24px;min-width: 280px;cursor: pointer;transition: all 0.3s ease;position: relative;display: flex;flex-direction: column;gap: 16px;}.pipeline-step:hover {transform: translateY(-4px);box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);background: rgba(255, 255, 255, 0.12);}.pipeline-step.expanded {border-color: rgba(96, 165, 250, 0.6);box-shadow: 0 8px 16px rgba(96, 165, 250, 0.3);}.pipeline-step.completed {border-color: rgba(16, 185, 129, 0.6);}.pipeline-step.running {border-color: rgba(251, 191, 36, 0.6);animation: glow 2s ease-in-out infinite alternate;}@keyframes glow {from { box-shadow: 0 0 10px rgba(251, 191, 36, 0.3); }to { box-shadow: 0 0 20px rgba(251, 191, 36, 0.6); }}.active-border {position: absolute;top: 0;left: 0;right: 0;height: 4px;border-radius: 16px 16px 0 0;}.step-indicator {width: 56px;height: 56px;border-radius: 16px;display: flex;align-items: center;justify-content: center;transition: all 0.3s ease;align-self: flex-start;}@keyframes pulse {0%, 100% { transform: scale(1); opacity: 1; }50% { transform: scale(1.1); opacity: 0.8; }}.step-content {flex: 1;}.step-content h3 {margin: 0 0 8px 0;font-size: 20px;font-weight: 600;}.step-content p {margin: 0 0 16px 0;color: rgba(255, 255, 255, 0.7);font-size: 14px;line-height: 1.5;}.step-progress {display: flex;align-items: center;gap: 12px;}.progress-bar {flex: 1;height: 6px;background: rgba(255, 255, 255, 0.2);border-radius: 3px;overflow: hidden;}.progress-fill {height: 100%;transition: width 0.3s ease;border-radius: 3px;}.progress-text {font-size: 12px;color: rgba(255, 255, 255, 0.6);min-width: 40px;}.expand-indicator {align-self: center;}.expand-arrow {transition: transform 0.3s ease;color: rgba(255, 255, 255, 0.6);}.expand-arrow.rotated {transform: rotate(180deg);}.pipeline-connector {color: rgba(255, 255, 255, 0.4);flex-shrink: 0;}.detailed-steps-section {border-top: 1px solid rgba(255, 255, 255, 0.1);padding-top: 40px;animation: slideIn 0.3s ease-out;}@keyframes slideIn {from {opacity: 0;transform: translateY(20px);}to {opacity: 1;transform: translateY(0);}}.section-header {margin-bottom: 32px;}.section-header h3 {margin: 0 0 8px 0;font-size: 24px;font-weight: 700;color: white;}.section-header p {margin: 0;color: rgba(255, 255, 255, 0.7);font-size: 16px;}.substeps-grid {display: grid;grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));gap: 24px;}.substep-card {background: rgba(255, 255, 255, 0.08);border: 2px solid rgba(255, 255, 255, 0.1);border-radius: 16px;padding: 24px;transition: all 0.3s ease;position: relative;overflow: hidden;}.substep-card:hover {transform: translateY(-2px);box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);}.substep-card.completed {border-color: rgba(16, 185, 129, 0.6);background: rgba(16, 185, 129, 0.1);}.substep-card.running {border-color: rgba(59, 130, 246, 0.6);background: rgba(59, 130, 246, 0.1);}.substep-card.executing {animation: executeGlow 2s ease-in-out infinite;}@keyframes executeGlow {0%, 100% { box-shadow: 0 0 10px rgba(59, 130, 246, 0.3); }50% { box-shadow: 0 0 20px rgba(59, 130, 246, 0.6); }}.card-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 16px;}.card-header .step-indicator {width: 48px;height: 48px;}.step-number {background: rgba(255, 255, 255, 0.1);padding: 6px 12px;border-radius: 8px;font-size: 12px;color: rgba(255, 255, 255, 0.8);font-weight: 500;}.card-content h4 {margin: 0 0 12px 0;font-size: 18px;font-weight: 600;color: white;}.card-content .description {margin: 0 0 12px 0;color: rgba(255, 255, 255, 0.8);font-size: 14px;line-height: 1.5;}.card-content .details {margin: 0 0 20px 0;color: rgba(255, 255, 255, 0.6);font-size: 13px;line-height: 1.6;}.card-actions {margin-top: auto;}.execute-btn {display: flex;align-items: center;gap: 8px;background: #3B82F6;border: none;border-radius: 12px;padding: 12px 20px;color: white;font-weight: 500;cursor: pointer;transition: all 0.2s ease;width: 100%;justify-content: center;}.execute-btn:hover:not(:disabled) {transform: translateY(-2px);box-shadow: 0 8px 16px rgba(59, 130, 246, 0.3);}.execute-btn:disabled {opacity: 0.6;cursor: not-allowed;}.completed-badge {display: flex;align-items: center;gap: 8px;background: rgba(16, 185, 129, 0.2);color: #10B981;padding: 12px 20px;border-radius: 12px;justify-content: center;font-weight: 500;}.running-badge {display: flex;align-items: center;gap: 8px;background: rgba(59, 130, 246, 0.2);color: #3B82F6;padding: 12px 20px;border-radius: 12px;justify-content: center;font-weight: 500;animation: pulse 2s infinite;}@media (max-width: 768px) {.system-header {padding: 20px;}.header-content {flex-direction: column;gap: 20px;text-align: center;}.system-status {justify-content: center;}.system-content {padding: 20px;}.pipeline-container {padding: 24px;}.main-pipeline {flex-direction: column;align-items: stretch;}.pipeline-step {min-width: auto;}.pipeline-connector {transform: rotate(90deg);margin: 12px 0;}.substeps-grid {flex-direction: column;align-items: stretch;}.substep-card {min-width: auto;}.substep-connector {transform: rotate(90deg);margin: 12px 0;align-self: center;}}`}</style>
</div>
);
};
export default DevOpsPipelineSystem;