自动化流水线

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;

相关推荐
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60619 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了9 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅10 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment10 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端
爱敲代码的小鱼10 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax