mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 11:58:34 +08:00
82 lines
4.5 KiB
Vue
82 lines
4.5 KiB
Vue
<script setup lang="ts">
|
|
import { RefreshRight } from '@element-plus/icons-vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import type { TeamRun } from '@/api'
|
|
import TeamRunProgress from './TeamRunProgress.vue'
|
|
import TeamRunStatus from './TeamRunStatus.vue'
|
|
|
|
withDefaults(defineProps<{
|
|
runs: TeamRun[]
|
|
loading?: boolean
|
|
error?: string | null
|
|
selectedRunId?: string | null
|
|
hasMore?: boolean
|
|
loadingMore?: boolean
|
|
}>(), { loading: false, error: null, selectedRunId: null, hasMore: false, loadingMore: false })
|
|
|
|
const emit = defineEmits<{
|
|
refresh: []
|
|
'select-run': [run: TeamRun]
|
|
'load-more': []
|
|
}>()
|
|
const { t } = useI18n()
|
|
</script>
|
|
|
|
<template>
|
|
<section class="runs-panel" aria-live="polite">
|
|
<header class="runs-panel__header">
|
|
<h2>{{ t('teamRuns.history') }}</h2>
|
|
<button type="button" class="runs-panel__refresh" :aria-label="t('teamRuns.refresh')" @click="emit('refresh')">
|
|
<el-icon :size="15"><RefreshRight /></el-icon>
|
|
</button>
|
|
</header>
|
|
<div v-if="loading && runs.length === 0" class="runs-panel__state">{{ t('teamRuns.loading') }}</div>
|
|
<div v-else-if="error && runs.length === 0" class="runs-panel__state is-error">
|
|
<span>{{ t('teamRuns.loadError') }}</span>
|
|
<button type="button" @click="emit('refresh')">{{ t('teamRuns.retryLoad') }}</button>
|
|
</div>
|
|
<div v-else-if="runs.length === 0" class="runs-panel__state">{{ t('teamRuns.empty') }}</div>
|
|
<div v-else class="runs-panel__list">
|
|
<button
|
|
v-for="run in runs"
|
|
:key="run.id"
|
|
data-team-run-row
|
|
type="button"
|
|
class="run-row"
|
|
:class="{ 'is-selected': selectedRunId === run.id }"
|
|
@click="emit('select-run', run)"
|
|
>
|
|
<span class="run-row__copy">
|
|
<strong>{{ run.title }}</strong>
|
|
<span>{{ run.objective }}</span>
|
|
<TeamRunStatus :status="run.status" :started-at="run.startedAt" :completed-at="run.completedAt" show-duration />
|
|
</span>
|
|
<TeamRunProgress :progress="run.progress" compact />
|
|
</button>
|
|
</div>
|
|
<p v-if="error && runs.length > 0" class="runs-panel__stale">{{ t('teamRuns.loadError') }}</p>
|
|
<button v-if="hasMore" data-team-runs-load-more type="button" class="runs-panel__more" :disabled="loadingMore" @click="emit('load-more')">
|
|
{{ loadingMore ? t('teamRuns.loadingMore') : t('teamRuns.loadMore') }}
|
|
</button>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.runs-panel { width: 100%; letter-spacing: 0; }
|
|
.runs-panel__header { display: flex; align-items: center; justify-content: space-between; padding-bottom: 10px; border-bottom: 1px solid var(--mc-border-light, #e7ebef); }
|
|
.runs-panel__header h2 { margin: 0; color: var(--mc-text-primary); font-size: 14px; }
|
|
.runs-panel__refresh { display: grid; width: 30px; height: 30px; place-items: center; border: 1px solid var(--mc-border); border-radius: 6px; background: var(--mc-bg-elevated); color: var(--mc-text-secondary); cursor: pointer; }
|
|
.runs-panel__list { display: grid; gap: 6px; padding-top: 10px; }
|
|
.run-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; width: 100%; min-height: 72px; padding: 11px 12px; border: 1px solid var(--mc-border); border-left: 3px solid #718096; border-radius: 8px; background: var(--mc-bg-elevated); color: inherit; cursor: pointer; text-align: left; letter-spacing: 0; }
|
|
.run-row:hover, .run-row.is-selected { border-left-color: #16835b; background: rgba(27, 143, 104, 0.055); }
|
|
.run-row:focus-visible, .runs-panel__refresh:focus-visible { outline: 2px solid #16835b; outline-offset: 2px; }
|
|
.run-row__copy { display: grid; min-width: 0; gap: 4px; }
|
|
.run-row__copy strong { color: var(--mc-text-primary); font-size: 13px; overflow-wrap: anywhere; }
|
|
.run-row__copy > span { display: -webkit-box; overflow: hidden; color: var(--mc-text-secondary); font-size: 12px; line-height: 1.4; -webkit-box-orient: vertical; -webkit-line-clamp: 1; }
|
|
.runs-panel__state { display: grid; min-height: 180px; place-content: center; gap: 8px; color: var(--mc-text-tertiary); font-size: 13px; text-align: center; }
|
|
.runs-panel__state.is-error, .runs-panel__stale { color: #b53535; }
|
|
.runs-panel__state button { border: 0; background: transparent; color: #16795a; cursor: pointer; }
|
|
.runs-panel__stale { margin: 8px 0 0; font-size: 12px; }
|
|
.runs-panel__more { display:block; width:100%; margin-top:10px; min-height:34px; border:1px solid var(--mc-border); border-radius:6px; background:var(--mc-bg-elevated); color:#16795a; cursor:pointer }.runs-panel__more:disabled{cursor:wait;opacity:.65}
|
|
</style>
|