🐛 修复生命周期取件次数占位符
This commit is contained in:
@@ -83,6 +83,7 @@ const hourInMilliseconds = 60 * 60 * 1000
|
|||||||
const dayInMilliseconds = 24 * 60 * 60 * 1000
|
const dayInMilliseconds = 24 * 60 * 60 * 1000
|
||||||
const adminPageSizeOptions = [5, 10, 20, 50, 100] as const
|
const adminPageSizeOptions = [5, 10, 20, 50, 100] as const
|
||||||
const defaultAdminPageSize = 10
|
const defaultAdminPageSize = 10
|
||||||
|
const legacyUsedCountTimelineKeys = new Set(['legacy-used-count', 'legacy_used_count'])
|
||||||
|
|
||||||
type DetailPolicyActionOption = {
|
type DetailPolicyActionOption = {
|
||||||
action: AdminFilePolicyAction
|
action: AdminFilePolicyAction
|
||||||
@@ -595,45 +596,109 @@ export function useAdminFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalizeTimelineNumber = (value: unknown) => {
|
||||||
|
if (typeof value === 'number' && Number.isFinite(value)) return value
|
||||||
|
if (typeof value === 'string' && value.trim() !== '') {
|
||||||
|
const numberValue = Number(value)
|
||||||
|
if (Number.isFinite(numberValue)) return numberValue
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasNumericTimelineValue = (item: AdminFileDetailTimelineItem) =>
|
||||||
|
normalizeTimelineNumber(item.value) !== null
|
||||||
|
|
||||||
|
const isLegacyUsedCountTimelineValue = (value: unknown) =>
|
||||||
|
typeof value === 'string' && legacyUsedCountTimelineKeys.has(value.trim())
|
||||||
|
|
||||||
|
const normalizeTimelineItem = (
|
||||||
|
item: AdminFileDetailTimelineItem,
|
||||||
|
fallbackUsedCount: number
|
||||||
|
) => {
|
||||||
|
const normalizedItem = { ...item }
|
||||||
|
|
||||||
|
if (legacyUsedCountTimelineKeys.has(normalizedItem.key)) {
|
||||||
|
normalizedItem.key = 'retrieved'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLegacyUsedCountTimelineValue(normalizedItem.value)) {
|
||||||
|
normalizedItem.value = fallbackUsedCount > 0 ? fallbackUsedCount : null
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedItem
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTimelineTranslationKey = (item: AdminFileDetailTimelineItem) => {
|
||||||
|
if (
|
||||||
|
(item.key === 'retrieved' ||
|
||||||
|
item.key === 'retrieved_event' ||
|
||||||
|
legacyUsedCountTimelineKeys.has(item.key)) &&
|
||||||
|
!hasNumericTimelineValue(item) &&
|
||||||
|
!item.detail
|
||||||
|
) {
|
||||||
|
return 'retrieved_event_unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.key
|
||||||
|
}
|
||||||
|
|
||||||
const formatTimelineValue = (item: AdminFileDetailTimelineItem) => {
|
const formatTimelineValue = (item: AdminFileDetailTimelineItem) => {
|
||||||
if (item.key === 'download_limit') {
|
if (item.key === 'download_limit') {
|
||||||
if (item.status === 'unlimited') return t('fileManage.unlimited')
|
if (item.status === 'unlimited') return t('fileManage.unlimited')
|
||||||
if (typeof item.value === 'number') return t('fileManage.remaining', { count: item.value })
|
const numericValue = normalizeTimelineNumber(item.value)
|
||||||
|
if (numericValue !== null) return t('fileManage.remaining', { count: numericValue })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.key === 'retrieved' && typeof item.value === 'number') {
|
if ((item.key === 'retrieved' || item.key === 'retrieved_event') && hasNumericTimelineValue(item)) {
|
||||||
return `${item.value} ${t('common.times')}`
|
return `${normalizeTimelineNumber(item.value)} ${t('common.times')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.key === 'expiration_policy') {
|
if (item.key === 'expiration_policy') {
|
||||||
if (item.status === 'unlimited') return t('send.expiration.units.forever')
|
if (item.status === 'unlimited') return t('send.expiration.units.forever')
|
||||||
if (typeof item.value === 'number') {
|
const numericValue = normalizeTimelineNumber(item.value)
|
||||||
const duration = formatDuration(Math.abs(item.value), (key) => t(key))
|
if (numericValue !== null) {
|
||||||
return item.value <= 0
|
const duration = formatDuration(Math.abs(numericValue), (key) => t(key))
|
||||||
|
return numericValue <= 0
|
||||||
? t('fileManage.timeline.expiration_policy.overdue', { time: duration })
|
? t('fileManage.timeline.expiration_policy.overdue', { time: duration })
|
||||||
: t('fileManage.timeline.expiration_policy.remaining', { time: duration })
|
: t('fileManage.timeline.expiration_policy.remaining', { time: duration })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.detail) return item.detail
|
if (item.detail) return item.detail
|
||||||
if (item.value !== null && item.value !== undefined) return String(item.value)
|
const numericValue = normalizeTimelineNumber(item.value)
|
||||||
|
if (numericValue !== null) return String(numericValue)
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const createTimelineViewItems = (items: AdminFileDetailTimelineItem[] = []) =>
|
const formatTimelineTitleValue = (item: AdminFileDetailTimelineItem, valueText: string) => {
|
||||||
items.map((item) => {
|
if (item.key === 'retrieved_event') {
|
||||||
|
const numericValue = normalizeTimelineNumber(item.value)
|
||||||
|
return numericValue === null ? '' : String(numericValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return valueText
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTimelineViewItems = (
|
||||||
|
items: AdminFileDetailTimelineItem[] = [],
|
||||||
|
fallbackUsedCount = 0
|
||||||
|
) =>
|
||||||
|
items.map((rawItem) => {
|
||||||
|
const item = normalizeTimelineItem(rawItem, fallbackUsedCount)
|
||||||
|
const timelineKey = getTimelineTranslationKey(item)
|
||||||
const timestampText = item.timestamp ? formatTimestamp(item.timestamp) : ''
|
const timestampText = item.timestamp ? formatTimestamp(item.timestamp) : ''
|
||||||
const valueText = formatTimelineValue(item)
|
const valueText = formatTimelineValue(item)
|
||||||
|
const titleValue = formatTimelineTitleValue(item, valueText)
|
||||||
const metaParts = [timestampText, valueText].filter(Boolean)
|
const metaParts = [timestampText, valueText].filter(Boolean)
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
severity: normalizeInsightSeverity(item.severity),
|
severity: normalizeInsightSeverity(item.severity),
|
||||||
displayTitle: t(`fileManage.timeline.${item.key}.title`, {
|
displayTitle: t(`fileManage.timeline.${timelineKey}.title`, {
|
||||||
status: t(`fileManage.timeline.status.${item.status || 'pending'}`),
|
status: t(`fileManage.timeline.status.${item.status || 'pending'}`),
|
||||||
detail: item.detail || '',
|
detail: item.detail || '',
|
||||||
value: valueText || item.value || ''
|
value: titleValue
|
||||||
}),
|
}),
|
||||||
displayDescription: t(`fileManage.timeline.${item.key}.description`, {
|
displayDescription: t(`fileManage.timeline.${timelineKey}.description`, {
|
||||||
status: t(`fileManage.timeline.status.${item.status || 'pending'}`),
|
status: t(`fileManage.timeline.status.${item.status || 'pending'}`),
|
||||||
detail: item.detail || '',
|
detail: item.detail || '',
|
||||||
value: valueText || ''
|
value: valueText || ''
|
||||||
@@ -701,6 +766,14 @@ export function useAdminFiles() {
|
|||||||
(viewItem.isExpiredFile ? 'expired' : isPermanentFile ? 'permanent' : 'available')
|
(viewItem.isExpiredFile ? 'expired' : isPermanentFile ? 'permanent' : 'available')
|
||||||
const statusInsightNextAction =
|
const statusInsightNextAction =
|
||||||
statusInsights?.nextAction ?? statusInsights?.next_action ?? 'monitor'
|
statusInsights?.nextAction ?? statusInsights?.next_action ?? 'monitor'
|
||||||
|
const usedCountValue = normalizeCount(
|
||||||
|
detail.usedCount ??
|
||||||
|
detail.used_count ??
|
||||||
|
statusInsights?.metrics?.usedCount ??
|
||||||
|
statusInsights?.metrics?.used_count ??
|
||||||
|
file.usedCount ??
|
||||||
|
file.used_count
|
||||||
|
)
|
||||||
const metadata = detail.metadata ?? detail.meta
|
const metadata = detail.metadata ?? detail.meta
|
||||||
const metadataNote = detail.note ?? metadata?.note ?? ''
|
const metadataNote = detail.note ?? metadata?.note ?? ''
|
||||||
const metadataTags = normalizeMetadataTags(detail.tags ?? metadata?.tags)
|
const metadataTags = normalizeMetadataTags(detail.tags ?? metadata?.tags)
|
||||||
@@ -742,7 +815,7 @@ export function useAdminFiles() {
|
|||||||
statusInsightNextAction,
|
statusInsightNextAction,
|
||||||
statusInsightReasons: statusInsights?.reasons || [],
|
statusInsightReasons: statusInsights?.reasons || [],
|
||||||
statusInsightMetrics: statusInsights?.metrics,
|
statusInsightMetrics: statusInsights?.metrics,
|
||||||
detailTimeline: createTimelineViewItems(detail.timeline || [])
|
detailTimeline: createTimelineViewItems(detail.timeline || [], usedCountValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -618,6 +618,10 @@ export default {
|
|||||||
retrieved_event: {
|
retrieved_event: {
|
||||||
title: 'Retrieval #{value}',
|
title: 'Retrieval #{value}',
|
||||||
description: 'This retrieval is complete; current status is {status}.'
|
description: 'This retrieval is complete; current status is {status}.'
|
||||||
|
},
|
||||||
|
retrieved_event_unknown: {
|
||||||
|
title: 'Retrieval History',
|
||||||
|
description: 'This retrieval is complete; current status is {status}.'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -615,6 +615,10 @@ export default {
|
|||||||
retrieved_event: {
|
retrieved_event: {
|
||||||
title: '第 {value} 次取件',
|
title: '第 {value} 次取件',
|
||||||
description: '本次取件已完成,当前状态为 {status}。'
|
description: '本次取件已完成,当前状态为 {status}。'
|
||||||
|
},
|
||||||
|
retrieved_event_unknown: {
|
||||||
|
title: '取件记录',
|
||||||
|
description: '本次取件已完成,当前状态为 {status}。'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
Reference in New Issue
Block a user