仿coding流水线流程图

本流程图是根据coding构建流水线改造而来,没有构建流程功能,需要的可根据自己的业务场景自行优化,这只是一个静态css加简单增删操作,业务需要可以自己增加事件。

代码环境vue2+element-ui+scss

vue2项目和scss应该不用我多讲了吧,我相信大家都会!好了,直接上代码!

<template>
  <div class="card">
    <div class="centent">
      <!-- 开始节点 -->
      <div class="stage-group">
        <div class="stage-Node">
          <div class="start">
            <div class="start-icon">
              <i
                class="el-icon-caret-right"
                style="font-size: 18px; color: #fff"
              ></i>
            </div>
            <span class="text">开始</span>
          </div>
        </div>
      </div>
      <!-- 加号 -->
      <div class="flex" v-for="(e, index) in stages" :key="e.id">
        <div
          class="add-sequence-stage"
          v-if="e.branches && e.branches.length > 0"
          @click="addNode(e, index)"
        >
          <i class="el-icon-plus"></i>
        </div>
        <!-- 中间节点 -->
        <div class="stage-group-box">
          <div class="stage-group" v-if="e.branches && e.branches.length > 0">
            <div
              :class="['stage-Node', i === e.branches.length - 1 ? 'last' : '']"
              v-for="(ele, i) in e.branches"
              :key="ele.id"
            >
              <div class="stage-arrow-box">
                <div class="stage-wrapper">
                  <div
                    :class="[
                      'stage-box',
                      'step-box',
                      checkedID === ele.id ? 'checked-stage' : ''
                    ]"
                    @click="checkedFun(ele)"
                  >
                    <div class="stage-index">{{ index + 1 }}-{{ i + 1 }}</div>
                    <div class="stage-name">{{ ele.name }}</div>
                    <div class="stage-del">
                      <div>
                        <div class="stage-icon" style="cursor: pointer">
                          <el-popconfirm
                            title="这是一段内容确定删除吗?"
                            @confirm="delNode(ele, i, e, index)"
                          >
                            <div
                              slot="reference"
                              class="el-icon-circle-close"
                            ></div>
                          </el-popconfirm>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div
                    class="step-wrapper"
                    v-for="(item, l) in ele.steps"
                    :key="item.id"
                  >
                    <div
                      :class="[
                        'step-box',
                        checkedID === item.id ? 'checked-step' : ''
                      ]"
                      @click="checkedFun(item)"
                    >
                      <div class="svg">{{ value }}</div>
                      <div class="step-label">{{ item.name }}</div>
                      <el-popconfirm
                        title="这是一段内容确定删除吗?"
                        @confirm="confirm(item, index, i, l)"
                      >
                        <div
                          slot="reference"
                          class="stage-icon el-icon-circle-close"
                        ></div>
                      </el-popconfirm>
                    </div>
                  </div>
                  <div class="add-step">
                    <i
                      class="el-icon-plus add-icon"
                      @click="addChildNode(ele, index, i)"
                    ></i>
                  </div>
                </div>
              </div>
            </div>
            <div
              v-if="e.branches && e.branches.length > 0"
              class="add-parallel-stage-box"
              @click="addParallelPhase(e, index)"
            >
              + 增加并行阶段
            </div>
          </div>
        </div>
      </div>
      <!-- 增加阶段 -->
      <div class="stage-group add-sequence">
        <div class="new-stage" @click="increasePhase">+ 增加阶段</div>
      </div>
      <!-- 结束 -->
      <div class="stage-end">
        <div class="stage-node">
          <div class="stage-arrow-box">
            <div class="stage-wrapper">
              <div class="stage-box step-box">
                <div class="stage-index">O</div>
                <div class="stage-name">结束</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: '</>',
      stages: [
        {
          branches: [
            {
              name: '检出1',
              id: 0,
              steps: [
                {name: '从代码仓库检出1-1', id: '0-1'},
                {name: '从代码仓库检出1-2', id: '0-2'}
              ]
            },
            {
              name: '检出2',
              id: 1,
              steps: [{name: '从代码仓库检出2', id: '1-1'}]
            }
          ]
        },
        {
          branches: [
            {
              name: '构建镜像并推送到 CODING Docker 制品库',
              id: 3,
              steps: [
                {name: 'sh', id: '3-1'},
                {name: 'useCustomStepPlugin', id: '3-2'}
              ]
            }
          ]
        }
      ],
      checkedID: ''
    }
  },
  methods: {
    addNode(e, index) {
      this.stages.splice(index, 0, {
        branches: [
          {
            name: '构建镜像',
            id: this.rondomPass(12),
            steps: []
          }
        ]
      })
    },
    // 增加阶段
    increasePhase() {
      this.stages.push({
        branches: [
          {
            name: '增加阶段',
            id: 8888
          }
        ]
      })
    },
    // 增加并行阶段
    addParallelPhase(e, index) {
      console.log('e', e, index)
      console.log('rondomPass', this.rondomPass(12))
      this.stages[index].branches.push({
        name: '检出xxxxx',
        id: this.rondomPass(12),
        steps: []
      })
    },
    // 增加子节点 e当前数据  index节点下标 i branches下标
    addChildNode(e, index, i) {
      console.log('ei', e, i)
      console.log('dsdf', this.stages[index].branches[i])
      this.stages[index].branches[i].steps.push({
        name: 'xxxxxx新增子节点',
        id: `${e.id}-${this.stages[index].branches[i].steps.length}`
      })
    },
    // 删除子节点
    confirm(e, index, i, l) {
      this.stages[index].branches[i].steps.splice(l, 1)
    },
    // 删除节点
    delNode(ele, i, e, index) {
      this.stages[index].branches.splice(i, 1)
      this.stages = this.delNodeFun(this.stages, ele.id)
    },
    // 过滤掉空节点
    delNodeFun(data, id) {
      let obj = []
      data.forEach((ele) => {
        if (ele.branches && ele.branches.length > 0) {
          obj.push(ele)
        }
      })
      return obj
    },
    // 选中的节点
    checkedFun(e) {
      this.checkedID = e.id
      console.log('e', this.checkedID, toString(e.id))
    },
    rondomPass(number) {
      let arr = []
      let arr1 = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
      let nonceStr = ''
      for (let i = 0; i < number; i++) {
        let n = Math.floor(Math.random() * 10)
        arr[i] = arr1[n]
        nonceStr += arr1[n]
      }
      return nonceStr
    }
  }
}
</script>

<style lang="scss" scoped>
.card {
  height: 100%;
  background-color: #f5f7fa;
  border: 1px solid #dcdfe6;
  color: #303133;
  -webkit-transition: 0.3s;
  transition-duration: 0.3s;
  transition-timing-function: ease;
  transition-delay: 0s;
  transition-property: all;
  transition: 0.3s;
}
.centent {
  min-height: 500px;
  // display: flex;
  display: -webkit-box;
  overflow-y: auto;
  // 开始
  .stage-group {
    display: flex;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-animation: change-background-1RSfRAHzBv 1s ease-in-out;
    animation: change-background-1RSfRAHzBv 1s ease-in-out;
    position: relative;
    .stage-Node {
      position: relative;
      .start {
        width: 200px;
        height: 40px;
        margin: 10px 45px;
        border-radius: 4px;
        background: #fff;
        -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
        box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
        display: flex;
        background-color: #fff;
        border: 1px solid #dadfe6;
        position: relative;
        z-index: 9;
        .start-icon {
          width: 40px;
          height: 100%;
          background: #3385ff;
          border-radius: 2px 0 0 2px;
          text-align: center;
          line-height: 45px;
        }
        .text {
          margin-left: 10px;
          font-size: 14px;
          line-height: 43px;
          font-weight: 600;
        }
      }
      .start:before {
        content: '';
        border-color: transparent;
      }
    }
  }
  .flex {
    display: flex;
  }
  // 中间节点
  .stage-group {
    display: flex;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-animation: change-background-1RSfRAHzBv 1s ease-in-out;
    animation: change-background-1RSfRAHzBv 1s ease-in-out;
    position: relative;
    .stage-Node {
      position: relative;
      .stage-arrow-box {
        overflow: hidden;
        left: 0;
        top: 0;
        .stage-wrapper {
          margin: 10px 45px;
          .stage-box {
            font-weight: 700;
            -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
            box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
            display: -ms-flexbox;
            display: flex;
            -ms-flex-pack: justify;
            justify-content: space-between;
            -ms-flex-align: center;
            align-items: center;
            margin: 0;
            .stage-index {
              height: 100%;
              width: 40px;
              background: #3385ff;
              color: #fff;
              display: -ms-flexbox;
              display: flex;
              border-radius: 2px 0 0 2px;
              -ms-flex-pack: center;
              justify-content: center;
              -ms-flex-align: center;
              align-items: center;
            }
            .stage-name {
              -ms-flex: 1 1;
              flex: 1 1;
              padding: 10px;
              overflow: hidden;
              white-space: nowrap;
              -o-text-overflow: ellipsis;
              text-overflow: ellipsis;
            }
            .stage-del {
              display: none;
              align-items: center;
              width: 20px;
            }
          }
          .stage-box:hover {
            border-color: #3385ff;
            .stage-del {
              display: flex;
            }
          }
          .checked-stage {
            border-color: #3385ff !important;
            .stage-del {
              display: flex;
            }
          }
          .step-box {
            border-radius: 2px;
            width: 200px;
            height: 40px;
            -ms-flex-pack: center;
            justify-content: center;
            -ms-flex-align: center;
            align-items: center;
            display: -ms-flexbox;
            display: flex;
            background-color: #fff;
            border: 1px solid #dadfe6;
            position: relative;
            z-index: 9;
          }
          .step-wrapper {
            margin: 10px 0;
            position: relative;
            .step-box {
              display: -ms-flexbox;
              display: flex;
              -ms-flex-pack: justify;
              justify-content: space-between;
              -ms-flex-align: center;
              align-items: center;
              margin: 0;
              .svg {
                color: rgb(145, 157, 175);
                width: 20px;
                margin-right: 6px;
                margin-left: 10px;
                display: flex;
              }
              .step-label {
                padding: 10px 10px 10px 4px;
                text-align: left;
                -ms-flex: 1 1;
                flex: 1 1;
                overflow: hidden;
                white-space: nowrap;
                -o-text-overflow: ellipsis;
                text-overflow: ellipsis;
              }
              .stage-icon {
                display: none;
                margin-right: 10px;
                cursor: pointer;
              }
            }
            .step-box:hover {
              border-color: #3385ff;
              .stage-icon {
                display: block;
              }
            }
            .checked-step {
              border-color: #3385ff !important;
              .stage-icon {
                display: block;
              }
            }
          }
          .step-wrapper:before {
            content: '';
            width: 1px;
            height: 10px;
            background: #dadfe6;
            position: absolute;
            top: -11px;
            left: 15px;
          }
          .step-wrapper:after {
            content: '';
            width: 6px;
            height: 6px;
            position: absolute;
            top: -3px;
            left: 12px;
            border: 1px solid #dadfe6;
            border-radius: 50%;
            background-color: #fff;
            z-index: 9;
          }
        }
        .add-step {
          top: -15px;
          left: 90px;
          position: relative;
          display: -ms-flexbox;
          display: flex;
          -ms-flex-pack: center;
          justify-content: center;
          -ms-flex-align: center;
          align-items: center;
          color: #dadfe6;
          background-color: #fff;
          border-radius: 50%;
          width: 19px;
          height: 19px;
          border: 1px solid;
          -webkit-box-shadow: 0 2px 4px 0 rgba(51, 133, 255, 0.15);
          box-shadow: 0 2px 4px 0 rgba(51, 133, 255, 0.15);
          z-index: 9;
          cursor: pointer;
          -webkit-transition: 0.2s ease-in-out;
          -o-transition: ease-in-out 0.2s;
          transition: 0.2s ease-in-out;
          .add-icon {
            color: #3385ff;
          }
        }
        .add-step:hover {
          -webkit-transform: scale(1.2);
          -ms-transform: scale(1.2);
          transform: scale(1.2);
          background-color: #3385ff;
          color: #fff;
          border-color: #3385ff;
          .add-icon {
            color: #fff;
          }
        }
      }
      .stage-arrow-box:before {
        content: '';
        width: 0;
        height: 0;
        border-top: 5px solid transparent;
        border-bottom: 5px solid transparent;
        position: absolute;
        left: 41px;
        top: 25px;
        border-left: 5px solid #3385ff;
      }
    }
  }
  .stage-group:after {
    content: '';
    height: 1px;
    width: 45px;
    background: #3385ff;
    position: absolute;
    right: 0;
    top: 30px;
  }
  .add-sequence-stage {
    position: relative;
    color: #3385ff;
    border-radius: 50%;
    width: 19px;
    height: 19px;
    margin: 20px 0;
    border: 1px solid;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-pack: center;
    justify-content: center;
    -ms-flex-align: center;
    align-items: center;
    background: 50% / cover #fff no-repeat;
    -webkit-box-shadow: 0 2px 4px 0 rgba(51, 133, 255, 0.15);
    box-shadow: 0 2px 4px 0 rgba(51, 133, 255, 0.15);
    z-index: 99;
    cursor: pointer;
    -webkit-transition: 0.2s ease-in-out;
    -o-transition: ease-in-out 0.2s;
    transition: 0.2s ease-in-out;
  }
  .add-sequence-stage:hover {
    -webkit-transform: scale(1.2);
    -ms-transform: scale(1.2);
    transform: scale(1.2);
    background-color: #3385ff;
    color: #fff;
    border-color: #3385ff;
    .add-icon {
      color: #fff;
    }
  }
  .stage-group-box {
    display: flex;
    position: relative;
    .stage-group {
      display: flex;
      -ms-flex-direction: column;
      flex-direction: column;
      -webkit-animation: change-background-1RSfRAHzBv 1s ease-in-out;
      animation: change-background-1RSfRAHzBv 1s ease-in-out;
      position: relative;
      .stage-Node {
        position: relative;
        .start {
          width: 200px;
          height: 40px;
          margin: 10px 45px;
          border-radius: 4px;
          background: #fff;
          -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
          box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
          display: flex;
          background-color: #fff;
          border: 1px solid #dadfe6;
          position: relative;
          z-index: 9;
          .start-icon {
            width: 40px;
            height: 100%;
            background: #176de6;
            border-radius: 2px 0 0 2px;
            text-align: center;
            line-height: 45px;
          }
          .text {
            margin-left: 10px;
            font-size: 14px;
            line-height: 43px;
            font-weight: 600;
          }
        }
        .start:before {
          content: '';
          border-color: transparent;
        }
      }
      .add-parallel-stage-box {
        border: 1px dashed #dadfe6;
        cursor: pointer;
        border-radius: 2px;
        margin: 10px 45px;
        width: 200px;
        height: 40px;
        -ms-flex-pack: center;
        justify-content: center;
        -ms-flex-align: center;
        align-items: center;
        display: -ms-flexbox;
        display: flex;
        background-color: #fff;
        position: relative;
        z-index: 9;
      }
      .add-parallel-stage-box:before {
        content: '';
        width: 0;
        height: 0;
        border-top: 5px solid transparent;
        border-bottom: 5px solid transparent;
        position: absolute;
        left: 0;
        top: 50%;
        -webkit-transform: translate(-100%, -50%);
        -ms-transform: translate(-100%, -50%);
        transform: translate(-100%, -50%);
        border-left: 5px solid #dadfe6;
      }
      .add-parallel-stage-box:hover {
        border: 1px dashed #176de6;
      }
      .stage-Node::before {
        content: '';
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        -webkit-transform: translateY(30px);
        -ms-transform: translateY(30px);
        transform: translateY(30px);
        border: 1px solid #176de6;
        border-top-color: transparent;
        border-radius: 0 0 0px 0px;
      }
      .last::before {
        content: '';
        position: absolute;
        top: -2px;
        right: 0;
        bottom: 0;
        left: 0;
        -webkit-transform: translateY(30px);
        -ms-transform: translateY(30px);
        transform: translateY(30px);
        border: 1px dashed #ccc;
        border-top-color: transparent;
        border-radius: 0 0 8px 8px;
      }
    }
    .stage-group:before {
      content: '';
      height: 1px;
      width: 45px;
      background: #3385ff;
      position: absolute;
      left: 0;
      top: 30px;
    }
  }
  // 新增节点
  .stage-group {
    .new-stage {
      border: 1px dashed #dadfe6;
      cursor: pointer;
      border-radius: 2px;
      margin: 10px 45px;
      width: 200px;
      height: 40px;
      -ms-flex-pack: center;
      justify-content: center;
      -ms-flex-align: center;
      align-items: center;
      display: -ms-flexbox;
      display: flex;
      background-color: #fff;
      position: relative;
      z-index: 9;
    }
    .new-stage:hover {
      border: 1px dashed #176de6;
    }
  }
  .add-sequence:before {
    content: '';
    height: 1px;
    width: 45px;
    background: #3385ff;
    position: absolute;
    left: 0;
    top: 30px;
  }
  // 结束
  .stage-end {
    display: -ms-flexbox;
    display: flex;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-animation: change-background-1RSfRAHzBv 1s ease-in-out;
    animation: change-background-1RSfRAHzBv 1s ease-in-out;
    position: relative;
    .stage-node {
      position: relative;
      .stage-arrow-box {
        overflow: hidden;
        left: 0;
        top: 0;
        .stage-wrapper {
          margin: 10px 45px;
          .stage-box {
            font-weight: 700;
            -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
            box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
            display: -ms-flexbox;
            display: flex;
            -ms-flex-pack: justify;
            justify-content: space-between;
            -ms-flex-align: center;
            align-items: center;
            margin: 0;
            .stage-index {
              height: 100%;
              width: 40px;
              background: #3385ff;
              color: #fff;
              display: -ms-flexbox;
              display: flex;
              border-radius: 2px 0 0 2px;
              -ms-flex-pack: center;
              justify-content: center;
              -ms-flex-align: center;
              align-items: center;
            }
            .stage-name {
              -ms-flex: 1 1;
              flex: 1 1;
              padding: 10px;
              overflow: hidden;
              white-space: nowrap;
              -o-text-overflow: ellipsis;
              text-overflow: ellipsis;
            }
          }
          .stage-box:hover {
            border-color: #3385ff;
          }
          .step-box {
            border-radius: 2px;
            width: 200px;
            height: 40px;
            -ms-flex-pack: center;
            justify-content: center;
            -ms-flex-align: center;
            align-items: center;
            display: -ms-flexbox;
            display: flex;
            background-color: #fff;
            border: 1px solid #dadfe6;
            position: relative;
            z-index: 9;
          }
          .step-wrapper {
            margin: 10px 0;
            position: relative;
            .step-box {
              display: -ms-flexbox;
              display: flex;
              -ms-flex-pack: justify;
              justify-content: space-between;
              -ms-flex-align: center;
              align-items: center;
              margin: 0;
              .svg {
                color: rgb(145, 157, 175);
                width: 20px;
                margin-right: 6px;
                margin-left: 10px;
                display: flex;
              }
              .step-label {
                padding: 10px 10px 10px 4px;
                text-align: left;
                -ms-flex: 1 1;
                flex: 1 1;
                overflow: hidden;
                white-space: nowrap;
                -o-text-overflow: ellipsis;
                text-overflow: ellipsis;
              }
              .stage-icon {
                margin-right: 10px;
                cursor: pointer;
              }
            }
            .step-box:hover {
              border-color: #3385ff;
            }
          }
          .step-wrapper:before {
            content: '';
            width: 1px;
            height: 10px;
            background: #dadfe6;
            position: absolute;
            top: -11px;
            left: 15px;
          }
          .step-wrapper:after {
            content: '';
            width: 6px;
            height: 6px;
            position: absolute;
            top: -3px;
            left: 12px;
            border: 1px solid #dadfe6;
            border-radius: 50%;
            background-color: #fff;
            z-index: 9;
          }
        }
      }
      .stage-arrow-box:before {
        content: '';
        width: 0;
        height: 0;
        border-top: 5px solid transparent;
        border-bottom: 5px solid transparent;
        position: absolute;
        left: 41px;
        top: 25px;
        border-left: 5px solid #3385ff;
      }
    }
  }
  .stage-end:before {
    content: '';
    height: 1px;
    width: 45px;
    background: #3385ff;
    position: absolute;
    left: 0;
    top: 30px;
  }
}
</style>

在vue2项目里面安装了scss就能直接复制使用,其他的需要编辑可自己增加编辑事件!

那么来看下代码展示的实际效果图吧

 下图是coding原来的构建流水线图

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值