2026-06-02 08:14:09 +08:00
|
|
|
<template>
|
|
|
|
|
<v-card elevation="0" border rounded="lg">
|
|
|
|
|
<v-card-title class="d-flex flex-wrap align-center ga-2">
|
|
|
|
|
<span>推送历史</span>
|
|
|
|
|
<v-spacer />
|
|
|
|
|
<v-select
|
|
|
|
|
:model-value="statusFilter"
|
|
|
|
|
:items="statusOptions"
|
|
|
|
|
item-title="label"
|
|
|
|
|
item-value="value"
|
|
|
|
|
label="状态"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
density="compact"
|
|
|
|
|
hide-details
|
|
|
|
|
clearable
|
|
|
|
|
style="max-width: 120px"
|
|
|
|
|
@update:model-value="$emit('update:statusFilter', $event); $emit('filter-change')"
|
|
|
|
|
/>
|
|
|
|
|
<v-select
|
|
|
|
|
:model-value="syncModeFilter"
|
|
|
|
|
:items="syncModeOptions"
|
|
|
|
|
item-title="label"
|
|
|
|
|
item-value="value"
|
|
|
|
|
label="模式"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
density="compact"
|
|
|
|
|
hide-details
|
|
|
|
|
clearable
|
|
|
|
|
style="max-width: 120px"
|
|
|
|
|
@update:model-value="$emit('update:syncModeFilter', $event); $emit('filter-change')"
|
|
|
|
|
/>
|
|
|
|
|
<v-select
|
|
|
|
|
:model-value="serverFilter"
|
|
|
|
|
:items="serverOptions"
|
|
|
|
|
item-title="title"
|
|
|
|
|
item-value="value"
|
|
|
|
|
label="服务器"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
density="compact"
|
|
|
|
|
hide-details
|
|
|
|
|
clearable
|
|
|
|
|
style="max-width: 180px"
|
|
|
|
|
@update:model-value="$emit('update:serverFilter', $event); $emit('filter-change')"
|
|
|
|
|
/>
|
|
|
|
|
<v-btn
|
|
|
|
|
size="small"
|
|
|
|
|
variant="tonal"
|
|
|
|
|
prepend-icon="mdi-wrench"
|
|
|
|
|
:loading="reconciling"
|
|
|
|
|
@click="$emit('reconcile')"
|
|
|
|
|
>
|
|
|
|
|
修复卡住记录
|
|
|
|
|
</v-btn>
|
|
|
|
|
</v-card-title>
|
|
|
|
|
<v-data-table-server
|
|
|
|
|
:items="items"
|
|
|
|
|
:headers="headers"
|
|
|
|
|
:items-length="total"
|
|
|
|
|
:loading="loading"
|
|
|
|
|
:page="page"
|
|
|
|
|
:items-per-page="15"
|
|
|
|
|
hover
|
|
|
|
|
density="comfortable"
|
|
|
|
|
@update:page="$emit('update:page', $event); $emit('page-change')"
|
|
|
|
|
>
|
2026-06-12 01:43:51 +08:00
|
|
|
<template #item.server_name="{ item }">
|
|
|
|
|
<span
|
|
|
|
|
class="text-body-2 push-history-server-name"
|
|
|
|
|
:title="item.server_name ?? ''"
|
|
|
|
|
>{{ item.server_name }}</span>
|
|
|
|
|
</template>
|
2026-06-02 08:14:09 +08:00
|
|
|
<template #item.status="{ item }">
|
|
|
|
|
<v-chip :color="logStatusColor(item.status)" size="x-small" variant="tonal" label border="sm">
|
|
|
|
|
{{ logStatusLabel(item.status) }}
|
|
|
|
|
</v-chip>
|
|
|
|
|
</template>
|
|
|
|
|
<template #item.sync_mode="{ item }">
|
|
|
|
|
<span class="text-caption">{{ syncModeLabel(item.sync_mode) }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
<template #item.started_at="{ item }">
|
|
|
|
|
<span class="text-caption text-medium-emphasis">{{ formatLogTime(item.started_at) }}</span>
|
|
|
|
|
</template>
|
2026-06-08 03:15:40 +08:00
|
|
|
<template #item.push_permission="{ item }">
|
|
|
|
|
<span
|
|
|
|
|
class="text-caption"
|
|
|
|
|
:class="item.push_permission ? '' : 'text-medium-emphasis'"
|
|
|
|
|
:title="item.push_permission || '本次推送未配置 rsync 属主/权限修正'"
|
|
|
|
|
>{{ formatPushPermission(item.push_permission) }}</span>
|
|
|
|
|
</template>
|
2026-06-02 08:14:09 +08:00
|
|
|
<template #item.error_message="{ item }">
|
|
|
|
|
<span
|
|
|
|
|
v-if="item.error_message"
|
|
|
|
|
class="text-caption text-error text-truncate d-inline-block"
|
|
|
|
|
style="max-width: 200px"
|
2026-06-02 15:18:01 +08:00
|
|
|
:title="formatPushErrorMessage(item.error_message)"
|
|
|
|
|
>{{ formatPushErrorMessage(item.error_message) }}</span>
|
2026-06-02 08:14:09 +08:00
|
|
|
<span v-else class="text-caption text-medium-emphasis">—</span>
|
|
|
|
|
</template>
|
|
|
|
|
<template #item.actions="{ item }">
|
|
|
|
|
<v-btn
|
|
|
|
|
v-if="item.status === 'failed'"
|
|
|
|
|
size="x-small"
|
|
|
|
|
variant="tonal"
|
|
|
|
|
prepend-icon="mdi-stethoscope"
|
|
|
|
|
@click="$emit('diagnose', item)"
|
|
|
|
|
>
|
|
|
|
|
诊断
|
|
|
|
|
</v-btn>
|
|
|
|
|
</template>
|
|
|
|
|
<template #no-data>
|
|
|
|
|
<div class="text-center text-medium-emphasis py-6">暂无数据</div>
|
|
|
|
|
</template>
|
|
|
|
|
</v-data-table-server>
|
|
|
|
|
</v-card>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-06-02 15:18:01 +08:00
|
|
|
import {
|
|
|
|
|
formatLogTime,
|
|
|
|
|
formatPushErrorMessage,
|
2026-06-08 03:15:40 +08:00
|
|
|
formatPushPermission,
|
2026-06-02 15:18:01 +08:00
|
|
|
logStatusColor,
|
|
|
|
|
logStatusLabel,
|
|
|
|
|
syncModeLabel,
|
|
|
|
|
} from '@/composables/push/labels'
|
2026-06-02 08:14:09 +08:00
|
|
|
import type { PushItem } from '@/types/api'
|
|
|
|
|
|
|
|
|
|
defineProps<{
|
|
|
|
|
items: PushItem[]
|
|
|
|
|
headers: { title: string; key: string; width?: number; sortable?: boolean }[]
|
|
|
|
|
total: number
|
|
|
|
|
loading: boolean
|
|
|
|
|
page: number
|
|
|
|
|
statusFilter: string | null
|
|
|
|
|
syncModeFilter: string | null
|
|
|
|
|
serverFilter: number | null
|
|
|
|
|
statusOptions: { label: string; value: string }[]
|
|
|
|
|
syncModeOptions: { label: string; value: string }[]
|
|
|
|
|
serverOptions: { title: string; value: number }[]
|
|
|
|
|
reconciling: boolean
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
defineEmits<{
|
|
|
|
|
'update:page': [page: number]
|
|
|
|
|
'update:statusFilter': [value: string | null]
|
|
|
|
|
'update:syncModeFilter': [value: string | null]
|
|
|
|
|
'update:serverFilter': [value: number | null]
|
|
|
|
|
'filter-change': []
|
|
|
|
|
'page-change': []
|
|
|
|
|
reconcile: []
|
|
|
|
|
diagnose: [item: PushItem]
|
|
|
|
|
}>()
|
|
|
|
|
</script>
|
2026-06-12 01:43:51 +08:00
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.push-history-server-name {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
max-width: 200px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
vertical-align: bottom;
|
|
|
|
|
}
|
|
|
|
|
</style>
|