feat(devops): 扩展流水线任务构建器功能并优化界面布局
- 将Pipeline和PipeTask组件宽度从240px调整为260px - 为PipeTask组件的startTimeMillis和durationMillis属性添加默认值 - 在PipeTaskBuilder中移除预设的节点选项表单并改为动态配置 - 为节点按钮添加触发条件选择功能,支持按状态显示不同按钮 - 优化节点参数配置界面,支持动态添加参数选项输入框 - 新增节点配置预览功能,实时显示JSON格式的配置信息 - 为输入框添加占位符提示文本提升用户体验 - 重构数据绑定逻辑,通过watch监听器实现配置同步
This commit is contained in:
@@ -44,8 +44,7 @@
|
||||
:key="index"
|
||||
v-prevent-re-click
|
||||
type="text"
|
||||
:style="`width: ${item.text && item.text.length >= 4 ? '74px': '26px'
|
||||
};color: ${item.color ? item.color : statusColorMap.running};display: ${item.condition ===
|
||||
:style="`color: ${item.color ? item.color : statusColorMap.running};display: ${item.condition ===
|
||||
'all' ?
|
||||
'inline-block':
|
||||
(item.condition === status ? 'inline-block': 'none')};overflow: hidden;text-overflow: ellipsis;`"
|
||||
@@ -101,14 +100,18 @@ export default {
|
||||
*/
|
||||
startTimeMillis: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false,
|
||||
default: function() {
|
||||
return new Date().getTime()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 持续时间。单位:秒
|
||||
*/
|
||||
durationMillis: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 状态
|
||||
@@ -200,8 +203,8 @@ export default {
|
||||
taskWindowStyle() {
|
||||
const colorHex = this.getColorHexByStatus(this.status)
|
||||
return {
|
||||
width: '240px',
|
||||
maxWidth: '240px',
|
||||
width: '260px',
|
||||
maxWidth: '260px',
|
||||
height: '100px',
|
||||
maxHeight: '100px',
|
||||
border: `1px solid ${colorHex}`,
|
||||
|
||||
@@ -54,35 +54,14 @@
|
||||
/>
|
||||
</div>
|
||||
</CutePage>
|
||||
<CutePage>
|
||||
<el-divider content-position="left">节点选项(预览)</el-divider>
|
||||
<el-form ref="form" :model="parameters" label-width="160px">
|
||||
<el-form-item
|
||||
v-for="(paramCode, index) in Object.keys(parameters)"
|
||||
:key="`paramKey_${index}`"
|
||||
:label="`${parameters[paramCode].name}(${paramCode})`"
|
||||
>
|
||||
<el-select
|
||||
v-model="parameters[paramCode].value"
|
||||
:placeholder="`请选择${parameters[paramCode].name}`"
|
||||
clearable
|
||||
filterable
|
||||
>
|
||||
<el-option v-for="(item, index2) in parameters[paramCode].dataSource" :key="`item_${index2}`" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</CutePage>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<CutePage>
|
||||
<el-divider content-position="left">节点参数设置</el-divider>
|
||||
<el-form label-width="140px">
|
||||
<el-form-item label="节点名称">
|
||||
<el-input v-model="name" />
|
||||
<el-input v-model="name" placeholder="请输入节点中文名称。例如:初始化" />
|
||||
</el-form-item>
|
||||
<el-form-item label="节点编码">
|
||||
<el-input v-model="code" />
|
||||
<el-input v-model="code" placeholder="请输入节点英文名称。例如:initial" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否支持点击">
|
||||
<el-switch v-model="click" />
|
||||
@@ -109,7 +88,22 @@
|
||||
<el-table :data="buttonList">
|
||||
<el-table-column prop="text" label="按钮名称" width="180">
|
||||
<template v-slot="scope">
|
||||
<el-input v-model="scope.row.text" />
|
||||
<el-input v-model="scope.row.text" placeholder="请输入按钮名称" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="text" label="触发条件" width="180">
|
||||
<template v-slot="scope">
|
||||
<el-select
|
||||
v-model="scope.row.condition"
|
||||
placeholder="请选择触发条件"
|
||||
clearable
|
||||
>
|
||||
<el-option label="所有" value="all" />
|
||||
<el-option label="待执行" value="pending" />
|
||||
<el-option label="执行中" value="running" />
|
||||
<el-option label="执行成功" value="success" />
|
||||
<el-option label="执行失败" value="failed" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="按钮类型" width="160">
|
||||
@@ -126,7 +120,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="执行动作">
|
||||
<template v-slot="scope">
|
||||
<el-table v-if="scope.row.type === 'link'" :data="buttonList">
|
||||
<el-table v-if="scope.row.type === 'link'" :data="[scope.row]">
|
||||
<el-table-column prop="blank" label="是否新开窗口" width="100">
|
||||
<template v-slot="innerScope">
|
||||
<el-switch v-model="innerScope.row.blank" />
|
||||
@@ -134,11 +128,11 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="linkUrl" label="打开链接">
|
||||
<template v-slot="innerScope">
|
||||
<el-input v-model="innerScope.row.linkUrl" />
|
||||
<el-input v-model="innerScope.row.linkUrl" placeholder="请输入or黏贴第三方链接" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-table v-if="scope.row.type === 'execute'" :data="buttonList">
|
||||
<el-table v-if="scope.row.type === 'execute'" :data="[scope.row]">
|
||||
<el-table-column prop="method" label="请求类型" width="160">
|
||||
<template v-slot="innerScope">
|
||||
<el-select
|
||||
@@ -153,7 +147,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="requestUrl" label="请求地址">
|
||||
<template v-slot="innerScope">
|
||||
<el-input v-model="innerScope.row.requestUrl" />
|
||||
<el-input v-model="innerScope.row.requestUrl" placeholder="请输入or黏贴请求地址。这里应为系统内部地址,具体参考 src/api 文件夹" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -166,30 +160,33 @@
|
||||
<el-table :data="parameterList">
|
||||
<el-table-column prop="name" label="参数名称">
|
||||
<template v-slot="nodeParamScope">
|
||||
<el-input v-model="nodeParamScope.row.name" />
|
||||
<el-input v-model="nodeParamScope.row.name" placeholder="参数中文名" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="code" label="参数编码">
|
||||
<template v-slot="nodeParamScope">
|
||||
<el-input v-model="nodeParamScope.row.code" />
|
||||
<el-input v-model="nodeParamScope.row.code" placeholder="参数英文名" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="dataSource" label="参数可选项">
|
||||
<template v-slot="nodeParamScope">
|
||||
<el-select
|
||||
v-model="nodeParamScope.row.value"
|
||||
placeholder="请输入参数可选项"
|
||||
clearable
|
||||
filterable
|
||||
allow-create
|
||||
>
|
||||
<el-option
|
||||
<el-input
|
||||
v-for="(item, index) in nodeParamScope.row.dataSource"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-model="item.value"
|
||||
style="margin-bottom: 2px"
|
||||
clearable
|
||||
placeholder="请输入参数选项"
|
||||
@input="(value) => onParameterDataSourceInput(nodeParamScope, index, value)"
|
||||
@clear="onParameterDataSourceClear(nodeParamScope, index)"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button
|
||||
style="width: 100%"
|
||||
type="primary"
|
||||
plain
|
||||
@click="onParameterDataSourceAppendClick(nodeParamScope)"
|
||||
>添加可选项
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -198,6 +195,37 @@
|
||||
</el-form>
|
||||
</CutePage>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<CutePage>
|
||||
<el-divider content-position="left">节点选项(预览)</el-divider>
|
||||
<el-form ref="form" :model="parameters" label-width="160px">
|
||||
<el-form-item
|
||||
v-for="(paramCode, index) in Object.keys(parameters)"
|
||||
:key="`paramKey_${index}`"
|
||||
:label="`${parameters[paramCode].name}(${paramCode})`"
|
||||
>
|
||||
<el-select
|
||||
v-model="parameters[paramCode].value"
|
||||
:placeholder="`请选择${parameters[paramCode].name}`"
|
||||
clearable
|
||||
filterable
|
||||
>
|
||||
<el-option v-for="(item, index2) in parameters[paramCode].dataSource" :key="`item_${index2}`" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</CutePage>
|
||||
<CutePage>
|
||||
<el-divider content-position="left">节点配置(预览)</el-divider>
|
||||
<div style="padding-left: 15px">
|
||||
<el-input
|
||||
v-model="taskConfig"
|
||||
type="textarea"
|
||||
:rows="25"
|
||||
/>
|
||||
</div>
|
||||
</CutePage>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
@@ -211,19 +239,19 @@ export default {
|
||||
components: { CutePage, PipeTask },
|
||||
data() {
|
||||
return {
|
||||
name: '环境准备',
|
||||
code: 'release',
|
||||
name: '',
|
||||
code: '',
|
||||
click: false,
|
||||
retry: false,
|
||||
detailType: null,
|
||||
buttonList: [
|
||||
{
|
||||
condition: 'running',
|
||||
method: 'get',
|
||||
requestUrl: `api/devops/ci/doCheck?id=#pipelineInstanceNodeId`,
|
||||
text: '确认合并',
|
||||
type: 'execute'
|
||||
}
|
||||
// {
|
||||
// condition: 'running',
|
||||
// method: 'get',
|
||||
// requestUrl: `api/devops/ci/doCheck?id=#pipelineInstanceNodeId`,
|
||||
// text: '确认合并',
|
||||
// type: 'execute'
|
||||
// }
|
||||
],
|
||||
hasButtonList: false,
|
||||
parameters: {
|
||||
@@ -237,22 +265,235 @@ export default {
|
||||
}
|
||||
},
|
||||
parameterList: [
|
||||
{
|
||||
name: 'Jdk版本',
|
||||
code: 'jdkVersion',
|
||||
dataSource: [
|
||||
{ label: 'jdk8', value: 'jdk8' },
|
||||
{ label: 'jdk11', value: 'jdk11' },
|
||||
{ label: 'jdk21', value: 'jdk21' }
|
||||
]
|
||||
}
|
||||
]
|
||||
// {
|
||||
// name: 'Jdk版本',
|
||||
// code: 'jdkVersion',
|
||||
// dataSource: [
|
||||
// { label: 'jdk8', value: 'jdk8' },
|
||||
// { label: 'jdk11', value: 'jdk11' },
|
||||
// { label: 'jdk21', value: 'jdk21' }
|
||||
// ]
|
||||
// }
|
||||
],
|
||||
taskConfig: ''
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
mounted() {},
|
||||
methods: {}
|
||||
computed: {
|
||||
},
|
||||
watch: {
|
||||
name: {
|
||||
handler(newVal) {
|
||||
const tempParameters = {}
|
||||
for (const key in this.parameters) {
|
||||
tempParameters[key] = ''
|
||||
}
|
||||
const tempParameterMap = {}
|
||||
for (const key in newVal) {
|
||||
tempParameterMap[key] = newVal[key].dataSource
|
||||
}
|
||||
const config = {
|
||||
name: newVal,
|
||||
code: this.code,
|
||||
click: this.click,
|
||||
retry: this.retry,
|
||||
detailType: this.detailType,
|
||||
buttonList: this.buttonList,
|
||||
parameters: tempParameters
|
||||
}
|
||||
this.taskConfig = JSON.stringify(config, null, 2)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
code: {
|
||||
handler(newVal) {
|
||||
const tempParameters = {}
|
||||
for (const key in this.parameters) {
|
||||
tempParameters[key] = ''
|
||||
}
|
||||
const tempParameterMap = {}
|
||||
for (const key in newVal) {
|
||||
tempParameterMap[key] = newVal[key].dataSource
|
||||
}
|
||||
const config = {
|
||||
name: this.name,
|
||||
code: newVal,
|
||||
click: this.click,
|
||||
retry: this.retry,
|
||||
detailType: this.detailType,
|
||||
buttonList: this.buttonList,
|
||||
parameters: tempParameters,
|
||||
parameterMap: tempParameterMap
|
||||
}
|
||||
this.taskConfig = JSON.stringify(config, null, 2)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
click: {
|
||||
handler(newVal) {
|
||||
const tempParameters = {}
|
||||
for (const key in this.parameters) {
|
||||
tempParameters[key] = ''
|
||||
}
|
||||
const tempParameterMap = {}
|
||||
for (const key in newVal) {
|
||||
tempParameterMap[key] = newVal[key].dataSource
|
||||
}
|
||||
const config = {
|
||||
name: this.name,
|
||||
code: this.code,
|
||||
click: newVal,
|
||||
retry: this.retry,
|
||||
detailType: this.detailType,
|
||||
buttonList: this.buttonList,
|
||||
parameters: tempParameters,
|
||||
parameterMap: tempParameterMap
|
||||
}
|
||||
this.taskConfig = JSON.stringify(config, null, 2)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
retry: {
|
||||
handler(newVal) {
|
||||
const tempParameters = {}
|
||||
for (const key in this.parameters) {
|
||||
tempParameters[key] = ''
|
||||
}
|
||||
const tempParameterMap = {}
|
||||
for (const key in newVal) {
|
||||
tempParameterMap[key] = newVal[key].dataSource
|
||||
}
|
||||
const config = {
|
||||
name: this.name,
|
||||
code: this.code,
|
||||
click: this.click,
|
||||
retry: newVal,
|
||||
detailType: this.detailType,
|
||||
buttonList: this.buttonList,
|
||||
parameters: tempParameters,
|
||||
parameterMap: tempParameterMap
|
||||
}
|
||||
this.taskConfig = JSON.stringify(config, null, 2)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
detailType: {
|
||||
handler(newVal) {
|
||||
const tempParameters = {}
|
||||
for (const key in this.parameters) {
|
||||
tempParameters[key] = ''
|
||||
}
|
||||
const tempParameterMap = {}
|
||||
for (const key in newVal) {
|
||||
tempParameterMap[key] = newVal[key].dataSource
|
||||
}
|
||||
const config = {
|
||||
name: this.name,
|
||||
code: this.code,
|
||||
click: this.click,
|
||||
retry: this.retry,
|
||||
detailType: newVal,
|
||||
buttonList: this.buttonList,
|
||||
parameters: tempParameters,
|
||||
parameterMap: tempParameterMap
|
||||
}
|
||||
this.taskConfig = JSON.stringify(config, null, 2)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
buttonList: {
|
||||
handler(newVal) {
|
||||
const tempParameters = {}
|
||||
for (const key in this.parameters) {
|
||||
tempParameters[key] = ''
|
||||
}
|
||||
const tempParameterMap = {}
|
||||
for (const key in newVal) {
|
||||
tempParameterMap[key] = newVal[key].dataSource
|
||||
}
|
||||
const config = {
|
||||
name: this.name,
|
||||
code: this.code,
|
||||
click: this.click,
|
||||
retry: this.retry,
|
||||
detailType: this.detailType,
|
||||
buttonList: newVal,
|
||||
parameters: tempParameters,
|
||||
parameterMap: tempParameterMap
|
||||
}
|
||||
this.taskConfig = JSON.stringify(config, null, 2)
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
parameters: {
|
||||
handler(newVal) {
|
||||
const tempParameters = {}
|
||||
for (const key in newVal) {
|
||||
tempParameters[key] = ''
|
||||
}
|
||||
const tempParameterMap = {}
|
||||
for (const key in newVal) {
|
||||
tempParameterMap[key] = newVal[key].dataSource
|
||||
}
|
||||
const config = {
|
||||
name: this.name,
|
||||
code: this.code,
|
||||
click: this.click,
|
||||
retry: this.retry,
|
||||
detailType: this.detailType,
|
||||
buttonList: this.buttonList,
|
||||
parameters: tempParameters,
|
||||
parameterMap: tempParameterMap
|
||||
}
|
||||
this.taskConfig = JSON.stringify(config, null, 2)
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
/**
|
||||
* 用于刷新 parameters
|
||||
*/
|
||||
parameterList: {
|
||||
handler: function(newList, oldList) {
|
||||
const newParameters = {}
|
||||
for (const param of newList) {
|
||||
if (param.code) {
|
||||
newParameters[param.code] = {
|
||||
name: param.name,
|
||||
dataSource: param.dataSource || []
|
||||
}
|
||||
}
|
||||
}
|
||||
this.parameters = newParameters
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
onParameterDataSourceInput(nodeParamScope, index, value) {
|
||||
const iii = nodeParamScope.row.dataSource[index]
|
||||
iii.label = value
|
||||
iii.value = value
|
||||
nodeParamScope.row.dataSource[index] = iii
|
||||
},
|
||||
onParameterDataSourceClear(nodeParamScope, index) {
|
||||
nodeParamScope.row.dataSource.splice(index, 1)
|
||||
},
|
||||
onParameterDataSourceAppendClick(nodeParamScope) {
|
||||
// m1.这种写法vue2无法响应式的检测到变化
|
||||
// const ds = nodeParamScope.row.dataSource || []
|
||||
// ds.push({label: '', value: ''})
|
||||
// nodeParamScope.row.dataSource = ds
|
||||
// m2.使用 this.$set(推荐用于 Vue 2)
|
||||
if (!nodeParamScope.row.dataSource) {
|
||||
this.$set(nodeParamScope.row, 'dataSource', [])
|
||||
}
|
||||
nodeParamScope.row.dataSource.push({ label: '', value: '' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -54,8 +54,8 @@ export default {
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.pipeline-card {
|
||||
width: 240px;
|
||||
max-width: 240px;
|
||||
width: 260px;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
.pipeline-link {
|
||||
|
||||
Reference in New Issue
Block a user