154 lines
5.1 KiB
TypeScript
154 lines
5.1 KiB
TypeScript
import dayjs from 'dayjs'
|
|
|
|
import { i18n } from '@/i18n'
|
|
|
|
const generateStatusMap: Record<string, { label: string; color: string }> = {
|
|
draft: { label: 'status.generate.draft', color: 'default' },
|
|
queued: { label: 'status.generate.queued', color: 'processing' },
|
|
generating: { label: 'status.generate.generating', color: 'processing' },
|
|
running: { label: 'status.generate.running', color: 'processing' },
|
|
completed: { label: 'status.generate.completed', color: 'success' },
|
|
partial_success: { label: 'status.generate.partial_success', color: 'warning' },
|
|
partial_completed: { label: 'status.generate.partial_completed', color: 'warning' },
|
|
failed: { label: 'status.generate.failed', color: 'error' },
|
|
}
|
|
|
|
const publishStatusMap: Record<string, { label: string; color: string }> = {
|
|
unpublished: { label: 'status.publish.unpublished', color: 'default' },
|
|
publishing: { label: 'status.publish.publishing', color: 'processing' },
|
|
success: { label: 'status.publish.success', color: 'success' },
|
|
failed: { label: 'status.publish.failed', color: 'error' },
|
|
partial_success: { label: 'status.publish.partial_success', color: 'warning' },
|
|
published: { label: 'status.publish.published', color: 'success' },
|
|
publish_success: { label: 'status.publish.publish_success', color: 'success' },
|
|
publish_failed: { label: 'status.publish.publish_failed', color: 'error' },
|
|
pending_review: { label: 'status.publish.pending_review', color: 'warning' },
|
|
blocked_by_compliance: { label: 'status.publish.blocked_by_compliance', color: 'error' },
|
|
}
|
|
|
|
export function formatDateTime(value?: string | null, pattern = 'YYYY-MM-DD HH:mm'): string {
|
|
if (!value) {
|
|
return '--'
|
|
}
|
|
const parsed = dayjs(value)
|
|
return parsed.isValid() ? parsed.format(pattern) : value
|
|
}
|
|
|
|
export function formatBytes(bytes: number, decimals = 2): string {
|
|
if (bytes === 0) return '0 B'
|
|
|
|
const k = 1024
|
|
const dm = decimals < 0 ? 0 : decimals
|
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
|
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
|
|
}
|
|
|
|
export function formatPercentage(value?: number | null, decimals = 2): string {
|
|
if (typeof value !== 'number' || Number.isNaN(value) || !Number.isFinite(value)) {
|
|
return '--'
|
|
}
|
|
|
|
return value.toFixed(Math.max(0, decimals))
|
|
}
|
|
|
|
export function getGenerateStatusMeta(status?: string | null): { label: string; color: string } {
|
|
if (!status) {
|
|
return { label: '--', color: 'default' }
|
|
}
|
|
const meta = generateStatusMap[status]
|
|
return meta
|
|
? { label: i18n.global.t(meta.label), color: meta.color }
|
|
: { label: status, color: 'default' }
|
|
}
|
|
|
|
export function hasActiveGenerationStatus(status?: string | null): boolean {
|
|
return status === 'queued' || status === 'pending' || status === 'generating' || status === 'running'
|
|
}
|
|
|
|
export function getPublishStatusMeta(status?: string | null): { label: string; color: string } {
|
|
if (!status) {
|
|
return { label: '--', color: 'default' }
|
|
}
|
|
const meta = publishStatusMap[status]
|
|
return meta
|
|
? { label: i18n.global.t(meta.label), color: meta.color }
|
|
: { label: status, color: 'default' }
|
|
}
|
|
|
|
export function getSourceTypeLabel(
|
|
sourceType?: string | null,
|
|
generationMode?: string | null,
|
|
): string {
|
|
if (!sourceType) {
|
|
return '--'
|
|
}
|
|
if (sourceType === 'template') {
|
|
return i18n.global.t('status.sourceType.template')
|
|
}
|
|
if (sourceType === 'free_create') {
|
|
return i18n.global.t('status.sourceType.free_create')
|
|
}
|
|
if (sourceType === 'imitation') {
|
|
return i18n.global.t('status.sourceType.imitation')
|
|
}
|
|
if (sourceType === 'kol') {
|
|
return i18n.global.t('status.sourceType.kol')
|
|
}
|
|
if (sourceType === 'custom_generation') {
|
|
if (generationMode === 'schedule') {
|
|
return i18n.global.t('status.sourceType.schedule_task')
|
|
}
|
|
if (generationMode === 'instant') {
|
|
return i18n.global.t('status.sourceType.instant_task')
|
|
}
|
|
return i18n.global.t('status.sourceType.custom_generation')
|
|
}
|
|
return sourceType
|
|
}
|
|
|
|
export function formatCronExecutionTime(cronExpr?: string | null): string {
|
|
if (!cronExpr) {
|
|
return '--'
|
|
}
|
|
|
|
const fiveFieldMatch = cronExpr.match(/^(\d{1,2})\s+(\d{1,2})\s+\*\s+\*\s+\*$/)
|
|
if (fiveFieldMatch) {
|
|
const [, minute, hour] = fiveFieldMatch
|
|
return `${hour.padStart(2, '0')}:${minute.padStart(2, '0')}`
|
|
}
|
|
|
|
const sixFieldMatch = cronExpr.match(/^(\d{1,2})\s+(\d{1,2})\s+(\d{1,2})\s+\*\s+\*\s+\*$/)
|
|
if (sixFieldMatch) {
|
|
const [, second, minute, hour] = sixFieldMatch
|
|
return `${hour.padStart(2, '0')}:${minute.padStart(2, '0')}:${second.padStart(2, '0')}`
|
|
}
|
|
|
|
return cronExpr
|
|
}
|
|
|
|
export function getTemplateMeta(templateKey: string): {
|
|
eyebrow: string
|
|
helper: string
|
|
accent: string
|
|
action: string
|
|
} {
|
|
const key = [
|
|
'top_x_article',
|
|
'product_review',
|
|
'research_report',
|
|
'brand_search_expansion',
|
|
].includes(templateKey)
|
|
? templateKey
|
|
: 'default'
|
|
|
|
return {
|
|
eyebrow: i18n.global.t(`templateMeta.${key}.eyebrow`),
|
|
helper: i18n.global.t(`templateMeta.${key}.helper`),
|
|
accent: i18n.global.t(`templateMeta.${key}.accent`),
|
|
action: i18n.global.t(`templateMeta.${key}.action`),
|
|
}
|
|
}
|