Files
DevFlow/src/components/ai/CIStatus.vue
T
lxy 25ec9cf69a 优化: 前端u-agent令牌对齐与组件拆分续批
- 全局样式类(df-sec-h/df-toast族)替换散装实现,主题token去硬编码兜底
- 拆分子组件:ai-chat/queue-panel(队列时钟与编辑随迁)、
  conversation-sidebar/ConversationList、settings/provider-panel
- 父组件死副本清理(队列编辑/时钟/键位表,TS6133清零)并补
  fill-draft父侧转发(重构半成品断链修复)
- 验收:vue-tsc EXIT 0
2026-10-18 23:15:57 +08:00

124 lines
3.2 KiB
Vue

<template>
<div class="ci-status" v-if="statuses.length > 0">
<div class="ci-header">
<span class="ci-title">{{ $t('aiChat.ciTitle') }}</span>
<span class="ci-summary" :class="summaryClass">{{ summaryText }}</span>
</div>
<div class="ci-list">
<a
v-for="check in statuses"
:key="check.name"
:href="check.url || undefined"
class="ci-item"
:class="'ci-item--' + check.status"
target="_blank"
rel="noopener"
>
<span class="ci-item-icon">{{ statusIcon(check.status) }}</span>
<span class="ci-item-name">{{ check.name }}</span>
</a>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { invoke } from '@tauri-apps/api/core'
const { t } = useI18n()
interface CheckStatus {
name: string
status: string
url: string | null
}
const props = defineProps<{
repoUrl: string
commitSha: string
}>()
const statuses = ref<CheckStatus[]>([])
const summaryClass = computed(() => {
const hasFailure = statuses.value.some(s => s.status === 'failure')
const hasPending = statuses.value.some(s => s.status === 'pending')
if (hasFailure) return 'ci-summary--fail'
if (hasPending) return 'ci-summary--pending'
return 'ci-summary--pass'
})
const summaryText = computed(() => {
const pass = statuses.value.filter(s => s.status === 'success').length
const fail = statuses.value.filter(s => s.status === 'failure').length
const pending = statuses.value.filter(s => s.status === 'pending').length
if (fail > 0) return t('aiChat.ciNFailed', { n: fail })
if (pending > 0) return t('aiChat.ciNPending', { n: pending })
return t('aiChat.ciNPassed', { n: pass })
})
function statusIcon(status: string): string {
switch (status) {
case 'success': return '✓'
case 'failure': return '✗'
case 'pending': return '⏳'
default: return '○'
}
}
onMounted(async () => {
try {
statuses.value = await invoke<CheckStatus[]>('get_commit_status', {
repoUrl: props.repoUrl,
commitSha: props.commitSha,
})
} catch {
statuses.value = []
}
})
</script>
<style scoped>
.ci-status {
border: 1px solid var(--df-border);
border-radius: 8px;
padding: 8px 12px;
margin: 8px 0;
background: var(--df-bg-card);
font-size: 12px;
}
.ci-header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-weight: 600;
}
.ci-title { color: var(--df-text); }
.ci-summary--pass { color: var(--df-success); }
.ci-summary--fail { color: var(--df-danger); }
.ci-summary--pending { color: var(--df-warning); }
.ci-list {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.ci-item {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
background: var(--df-bg);
border: 1px solid var(--df-border);
text-decoration: none;
color: var(--df-text);
}
.ci-item--success { border-color: var(--df-success); }
.ci-item--failure { border-color: var(--df-danger); }
.ci-item--pending { border-color: var(--df-warning); }
.ci-item-icon { font-size: 10px; font-weight: 500; }
.ci-item:hover { opacity: 0.8; }
</style>