// 公共展示工具:字段语义逐一对照 web.archived/src/shared/ui/ui.jsx export function dateTime(value?: string | number | null): string { if (!value) return '—'; const date = new Date(value); return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString(); } // 对齐归档版 conflictMessage:优先后端 error 字段,409 冲突时附加提示。 export function conflictMessage(error: any, conflictHint?: string): string { const status = error?.status ?? error?.body?.status; const message = error?.message || error?.body?.error || '请求失败'; if (status === 409 && conflictHint) return `${message}:${conflictHint}`; return message; } export const platforms = [ { value: 'douyin', label: '抖音' }, ]; export function platformLabel(value?: string): string { return platforms.find((item) => item.value === value)?.label || value || '—'; } export const shareJobStatus: Record = { queued: { label: '待解析', tone: 'warning' }, processing: { label: '解析中', tone: 'warning' }, succeeded: { label: '已加入监控', tone: 'success' }, failed: { label: '解析失败', tone: 'error' }, }; // 对齐归档版 extractShareURL / platformForShareURL const shareURLPattern = /https?:\/\/[^\s<>"'`]+/giu; const trailingShareURLPunctuation = /[,。;!?、)》)】\]}>,.?!:;]+$/u; export function platformForShareURL(value: string): string { try { const hostname = new URL(value).hostname.toLowerCase(); if (['www.douyin.com', 'v.douyin.com'].includes(hostname)) return 'douyin'; } catch { return ''; } return ''; } export function extractShareURL(value?: string): string { const candidates = String(value || '') .match(shareURLPattern) ?.map((candidate) => candidate.replace(trailingShareURLPunctuation, '')) .filter(Boolean); return candidates?.find((candidate) => platformForShareURL(candidate)) || candidates?.[0] || ''; } // 账号可恢复性判定(对齐归档版 accountReadiness):授权、绑定、出口健康、账号暂停四层。 export const readinessReasonText: Record = { account_revoked: '授权已撤销', account_paused: '账号已暂停', binding_missing: '未绑定运行环境', network_exit_missing: '未绑定固定出口', network_exit_unhealthy: '固定出口不健康', runtime_stop_pending: '运行环境停止结果待确认', runtime_missing: '运行环境未启动', runtime_active: '仍有活动运行实例', account_conflict: '账号状态已变化,请刷新后重试', environment_unavailable: '环境状态未知', }; export interface AccountReadiness { label: string; reason: string; canResume: boolean; ready: boolean; } export function accountReadiness(account: any, binding: any, bindingError: any = null): AccountReadiness { if (account.authorization_status !== 'authorized') return { label: '授权已撤销', reason: 'account_revoked', canResume: false, ready: false }; if (bindingError) return { label: '环境状态未知', reason: 'environment_unavailable', canResume: false, ready: false }; if (!binding) return { label: '未绑定运行环境', reason: 'binding_missing', canResume: false, ready: false }; let blocked = ''; if (binding.runtime_cleanup_pending) { blocked = 'runtime_stop_pending'; } else if (binding.network_exit_id && binding.network_exit_health !== 'healthy') { blocked = 'network_exit_unhealthy'; } if (blocked) return { label: readinessReasonText[blocked], reason: blocked, canResume: false, ready: false }; if (account.runtime_status === 'paused') { const stopped = !binding.runtime_id; return { label: stopped ? '资源就绪,可恢复' : '等待运行环境停止', reason: stopped ? '' : 'runtime_stop_pending', canResume: stopped, ready: false }; } const schedule = binding.schedule_block_reason || ''; return { label: schedule ? readinessReasonText[schedule] || `不可调度:${schedule}` : '可调度', reason: schedule, canResume: false, ready: !schedule, }; } export function isCollectionAccount(account: any): boolean { return ( account.authorization_status === 'authorized' && ['normal', 'muted'].includes(account.business_status) && account.login_status === 'logged_in' ); }