feat: 初始化DevOps平台前端
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { ReleaseRun } from '../types/devops'
|
||||
import StatusPill from './StatusPill.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
run: ReleaseRun
|
||||
}>()
|
||||
|
||||
const notableSteps = computed(() =>
|
||||
props.run.steps.filter(
|
||||
(step) =>
|
||||
step.status === 'failed' ||
|
||||
step.status === 'skipped' ||
|
||||
step.errorSummary ||
|
||||
step.message ||
|
||||
step.logExcerpt,
|
||||
),
|
||||
)
|
||||
|
||||
function noteLabel(status: string) {
|
||||
return status === 'failed' ? '失败摘要' : '执行提示'
|
||||
}
|
||||
|
||||
function displayText(value?: string | number) {
|
||||
if (value === undefined || value === null || value === '' || value === '-') {
|
||||
return '未返回'
|
||||
}
|
||||
|
||||
return String(value)
|
||||
}
|
||||
|
||||
function finishText() {
|
||||
if (props.run.finishedAt && props.run.finishedAt !== '-') {
|
||||
return props.run.finishedAt
|
||||
}
|
||||
|
||||
if (props.run.status === 'queued' || props.run.status === 'running') {
|
||||
return '未结束'
|
||||
}
|
||||
|
||||
return '未返回'
|
||||
}
|
||||
|
||||
function buildText() {
|
||||
if (props.run.jenkinsBuildNumber !== undefined && props.run.jenkinsBuildNumber !== null) {
|
||||
return `#${props.run.jenkinsBuildNumber}`
|
||||
}
|
||||
|
||||
return '未返回'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="run-execution-details">
|
||||
<dl class="detail-grid">
|
||||
<dt>Run ID</dt>
|
||||
<dd class="mono">{{ displayText(run.id) }}</dd>
|
||||
<dt>项目</dt>
|
||||
<dd>{{ displayText(run.projectName) }}</dd>
|
||||
<dt>环境</dt>
|
||||
<dd>{{ displayText(run.environment) }}</dd>
|
||||
<dt>ref/tag</dt>
|
||||
<dd class="mono">{{ displayText(run.ref) }}</dd>
|
||||
<dt>操作者</dt>
|
||||
<dd>{{ displayText(run.actor) }}</dd>
|
||||
<dt>状态</dt>
|
||||
<dd><StatusPill :state="run.status" /></dd>
|
||||
<dt>开始时间</dt>
|
||||
<dd>{{ displayText(run.startedAt) }}</dd>
|
||||
<dt>结束时间</dt>
|
||||
<dd>{{ finishText() }}</dd>
|
||||
<dt>Jenkins Queue</dt>
|
||||
<dd class="mono">{{ displayText(run.jenkinsQueueId) }}</dd>
|
||||
<dt>Jenkins Build</dt>
|
||||
<dd class="mono">{{ buildText() }}</dd>
|
||||
<dt>Jenkins 状态</dt>
|
||||
<dd>{{ displayText(run.jenkinsSummary) }}</dd>
|
||||
<dt>Build URL</dt>
|
||||
<dd>
|
||||
<a v-if="run.jenkinsBuildUrl" :href="run.jenkinsBuildUrl" target="_blank" rel="noreferrer">
|
||||
{{ run.jenkinsBuildUrl }}
|
||||
</a>
|
||||
<span v-else>未返回</span>
|
||||
</dd>
|
||||
<dt>Commit</dt>
|
||||
<dd>{{ displayText(run.commitSummary) }}</dd>
|
||||
<dt>备注</dt>
|
||||
<dd>{{ displayText(run.note) }}</dd>
|
||||
</dl>
|
||||
|
||||
<div class="failure-block">
|
||||
<div class="failure-header">
|
||||
<strong>执行提示 / 日志摘要</strong>
|
||||
<span>{{ notableSteps.length ? `${notableSteps.length} 个节点` : '未返回异常或提示' }}</span>
|
||||
</div>
|
||||
<div v-if="notableSteps.length" class="failure-list">
|
||||
<article
|
||||
v-for="step in notableSteps"
|
||||
:key="step.id"
|
||||
class="failure-item"
|
||||
:class="{ danger: step.status === 'failed' }"
|
||||
>
|
||||
<div class="failure-title">
|
||||
<strong>{{ step.name }}</strong>
|
||||
<StatusPill :state="step.status" />
|
||||
</div>
|
||||
<p v-if="step.errorSummary">
|
||||
<span>{{ noteLabel(step.status) }}</span>
|
||||
{{ step.errorSummary }}
|
||||
</p>
|
||||
<p v-if="step.message">
|
||||
<span>执行消息</span>
|
||||
{{ step.message }}
|
||||
</p>
|
||||
<pre v-if="step.logExcerpt">{{ step.logExcerpt }}</pre>
|
||||
<p v-if="!step.errorSummary && !step.message && !step.logExcerpt">
|
||||
<span>节点摘要</span>
|
||||
{{ step.summary }}
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
<div v-else class="empty-line">后端尚未返回异常摘要、执行提示或日志摘要。</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.run-execution-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 112px minmax(0, 1fr);
|
||||
gap: 10px 12px;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.detail-grid dt {
|
||||
color: #7b8798;
|
||||
}
|
||||
|
||||
.detail-grid dd {
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
color: #172033;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.detail-grid a {
|
||||
color: #1d5fd0;
|
||||
}
|
||||
|
||||
.failure-block {
|
||||
border-top: 1px solid #edf1f7;
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.failure-header,
|
||||
.failure-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.failure-header strong,
|
||||
.failure-title strong {
|
||||
color: #172033;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.failure-header span {
|
||||
color: #7b8798;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.failure-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.failure-item {
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fbfcff;
|
||||
}
|
||||
|
||||
.failure-item.danger {
|
||||
border-color: #f0d5d5;
|
||||
background: #fffafa;
|
||||
}
|
||||
|
||||
.failure-item p {
|
||||
margin: 8px 0 0;
|
||||
color: #4b5565;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.failure-item p span {
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
color: #62572c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.failure-item.danger p span {
|
||||
color: #9f2f2f;
|
||||
}
|
||||
|
||||
.failure-item pre {
|
||||
max-height: 180px;
|
||||
margin: 10px 0 0;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
border-radius: 6px;
|
||||
background: #172033;
|
||||
color: #eef4ff;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.empty-line {
|
||||
margin-top: 10px;
|
||||
color: #7b8798;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.detail-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 4px 0;
|
||||
}
|
||||
|
||||
.detail-grid dd + dt {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user