feat: show admin file detail lifecycle

This commit is contained in:
Lan
2026-06-03 05:28:11 +08:00
parent d7e3aff825
commit f446062272
5 changed files with 400 additions and 2 deletions
+148
View File
@@ -585,6 +585,43 @@
</button>
</div>
<section
class="rounded-lg border px-4 py-4"
:class="getInsightPanelClass(selectedFileDetail.statusInsightSeverity)"
>
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
<div class="min-w-0">
<p class="text-xs font-medium uppercase" :class="[mutedTextClass]">
{{ t('fileManage.statusInsight') }}
</p>
<h4 class="mt-1 text-base font-semibold" :class="[primaryTextClass]">
{{ getInsightStateLabel(selectedFileDetail.statusInsightState) }}
</h4>
<p class="mt-1 text-sm" :class="[mutedTextClass]">
{{ t('fileManage.nextAction') }}:
{{ getInsightActionLabel(selectedFileDetail.statusInsightNextAction) }}
</p>
</div>
<span
class="inline-flex w-fit items-center rounded-full px-2.5 py-1 text-xs font-medium"
:class="getInsightBadgeClass(selectedFileDetail.statusInsightSeverity)"
>
{{ getInsightSeverityLabel(selectedFileDetail.statusInsightSeverity) }}
</span>
</div>
<div v-if="detailInsightReasonLabels.length > 0" class="mt-3 flex flex-wrap gap-2">
<span
v-for="reason in detailInsightReasonLabels"
:key="reason"
class="rounded-full px-2.5 py-1 text-xs font-medium"
:class="isDarkMode ? 'bg-gray-700 text-gray-300' : 'bg-white text-gray-600 shadow-sm'"
>
{{ reason }}
</span>
</div>
</section>
<section class="space-y-3">
<h4 class="text-sm font-semibold" :class="[primaryTextClass]">
{{ t('fileManage.overview') }}
@@ -623,6 +660,52 @@
</div>
</section>
<section v-if="selectedFileDetail.detailTimeline.length > 0" class="space-y-3">
<h4 class="text-sm font-semibold" :class="[primaryTextClass]">
{{ t('fileManage.lifecycle') }}
</h4>
<div
class="rounded-lg border px-4 py-4"
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-white']"
>
<div
v-for="(item, index) in selectedFileDetail.detailTimeline"
:key="`${item.key}-${index}`"
class="grid grid-cols-[auto_minmax(0,1fr)] gap-3"
>
<div class="flex flex-col items-center">
<span
class="mt-1 h-3 w-3 rounded-full"
:class="getTimelineDotClass(item.severity)"
/>
<span
v-if="index < selectedFileDetail.detailTimeline.length - 1"
class="my-1 w-px flex-1"
:class="[isDarkMode ? 'bg-gray-600' : 'bg-gray-200']"
/>
</div>
<div
class="mb-3 rounded-lg border px-3 py-2 last:mb-0"
:class="[
isDarkMode ? 'border-gray-700 bg-gray-800/60' : 'border-gray-100 bg-gray-50'
]"
>
<div class="flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between">
<p class="text-sm font-medium" :class="[primaryTextClass]">
{{ item.displayTitle }}
</p>
<p class="text-xs" :class="[mutedTextClass]">
{{ item.displayMeta }}
</p>
</div>
<p class="mt-1 text-sm" :class="[mutedTextClass]">
{{ item.displayDescription }}
</p>
</div>
</div>
</div>
</section>
<section class="space-y-3">
<h4 class="text-sm font-semibold" :class="[primaryTextClass]">
{{ t('fileManage.storageInfo') }}
@@ -885,6 +968,7 @@ import { computed, onMounted, type Component } from 'vue'
import { useI18n } from 'vue-i18n'
import type {
AdminBatchEditMode,
AdminFileInsightSeverity,
AdminFileSortBy,
AdminFileSortOrder,
AdminFileStatusFilter,
@@ -1106,6 +1190,13 @@ const detailStorageItems = computed<DetailInfoItem[]>(() => {
]
})
const detailInsightReasonLabels = computed(() => {
const file = selectedFileDetail.value
if (!file) return []
return file.statusInsightReasons.map((reason) => t(`fileManage.insightReasons.${reason}`))
})
const openDetailEditModal = () => {
if (!selectedFileDetail.value) return
@@ -1246,6 +1337,63 @@ const getStatusBadgeClass = (file: AdminFileViewItem) => {
return isDarkMode.value ? 'bg-green-900/30 text-green-300' : 'bg-green-100 text-green-700'
}
const getInsightSeverityLabel = (severity: AdminFileInsightSeverity) =>
t(`fileManage.insightSeverity.${severity}`)
const getInsightStateLabel = (state: string) => t(`fileManage.insightStates.${state}`)
const getInsightActionLabel = (action: string) => t(`fileManage.insightActions.${action}`)
const getInsightPanelClass = (severity: AdminFileInsightSeverity) => {
const darkClasses: Record<AdminFileInsightSeverity, string> = {
success: 'border-emerald-500/20 bg-emerald-500/10',
warning: 'border-amber-500/20 bg-amber-500/10',
danger: 'border-red-500/20 bg-red-500/10',
info: 'border-blue-500/20 bg-blue-500/10',
neutral: 'border-gray-700 bg-gray-700/30'
}
const lightClasses: Record<AdminFileInsightSeverity, string> = {
success: 'border-emerald-100 bg-emerald-50',
warning: 'border-amber-100 bg-amber-50',
danger: 'border-red-100 bg-red-50',
info: 'border-blue-100 bg-blue-50',
neutral: 'border-gray-200 bg-gray-50'
}
return isDarkMode.value ? darkClasses[severity] : lightClasses[severity]
}
const getInsightBadgeClass = (severity: AdminFileInsightSeverity) => {
const darkClasses: Record<AdminFileInsightSeverity, string> = {
success: 'bg-emerald-900/40 text-emerald-200',
warning: 'bg-amber-900/40 text-amber-200',
danger: 'bg-red-900/40 text-red-200',
info: 'bg-blue-900/40 text-blue-200',
neutral: 'bg-gray-700 text-gray-300'
}
const lightClasses: Record<AdminFileInsightSeverity, string> = {
success: 'bg-emerald-100 text-emerald-700',
warning: 'bg-amber-100 text-amber-700',
danger: 'bg-red-100 text-red-700',
info: 'bg-blue-100 text-blue-700',
neutral: 'bg-gray-100 text-gray-700'
}
return isDarkMode.value ? darkClasses[severity] : lightClasses[severity]
}
const getTimelineDotClass = (severity: AdminFileInsightSeverity) => {
const classes: Record<AdminFileInsightSeverity, string> = {
success: 'bg-emerald-500',
warning: 'bg-amber-500',
danger: 'bg-red-500',
info: 'bg-blue-500',
neutral: isDarkMode.value ? 'bg-gray-500' : 'bg-gray-300'
}
return classes[severity]
}
onMounted(() => {
void loadFiles()
})