本流程图是根据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原来的构建流水线图