自动化流水线

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;

相关推荐
JarvanMo5 小时前
Flutter. FractionallySizedBox
前端
知识分享小能手6 小时前
React学习教程,从入门到精通, React 入门指南:React JSX 语法知识点详解及案例代码(8)
前端·javascript·vue.js·学习·react.js·前端框架·anti-design-vue
卓码软件测评6 小时前
第三方web测评机构:【WEB安全测试中HTTP方法(GET/POST/PUT)的安全风险检测】
前端·网络协议·安全·web安全·http·xss
学习3人组6 小时前
React 组件基础与事件处理
前端·javascript·react.js
漂流瓶jz11 小时前
解锁Babel核心功能:从转义语法到插件开发
前端·javascript·typescript
周小码12 小时前
shadcn-table:构建高性能服务端表格的终极解决方案 | 2025最新实践
前端·react.js
大怪v12 小时前
老乡,别走!Javascript隐藏功能你知道吗?
前端·javascript·代码规范
webYin12 小时前
vue2 打包生成的js文件过大优化
前端·vue.js·webpack
gnip12 小时前
结合Worker通知应用更新
前端·javascript