- AuditLog +298(筛选/详情/i18n)+ audit 后端 record/mod - AI 命令层:generate_image +81 / fetch_url / fetch_search / skills / tool_registry / tools/file / provider / conversation - 前端组件:AiChat/TopBar/ConversationSidebar/GitChanges/ApprovalPopup/Dashboard/ProjectDetail 等 30+ + composables + i18n - 诊断文档: aichat历史会话实证诊断-2026-08-04 + project_soft_delete 测试
124 lines
3.2 KiB
Vue
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-secondary);
|
|
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>
|