export function createId() { if (typeof crypto !== 'undefined' && crypto.randomUUID) { return crypto.randomUUID() } return `${Date.now()}-${Math.random().toString(16).slice(2)}` } export function formatFileSize(size) { const value = Number(size || 0) if (value < 1024) return `${value}B` if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)}KB` return `${(value / 1024 / 1024).toFixed(1)}MB` } export function formatTestError(error, fallback) { const message = String(error?.message || '').trim() if (/not\s*found/i.test(message)) { return '测试接口暂未加载或规则详情已失效,请刷新规则详情后再试。' } return message || fallback } export function formatTime() { return new Intl.DateTimeFormat('zh-CN', { hour: '2-digit', minute: '2-digit' }).format(new Date()) } export function toAttachmentPayload(file) { const document = file.ocrDocument || {} return { id: file.id, name: file.name, size: file.size, content_type: file.contentType, note: file.error || '', recognition_status: file.status, ocr_text: document.text || '', summary: document.summary || '', document_type: document.document_type || '', document_type_label: document.document_type_label || '', scene_code: document.scene_code || '', scene_label: document.scene_label || '', avg_score: document.avg_score || 0, document_fields: Array.isArray(document.document_fields) ? document.document_fields : [] } } export function normalizeOcrDocuments(payload) { const documents = Array.isArray(payload?.documents) ? payload.documents : [] return documents.map((item) => ({ filename: String(item?.filename || '').trim(), summary: String(item?.summary || '').trim(), text: String(item?.text || '').trim(), avg_score: Number(item?.avg_score || 0), document_type: String(item?.document_type || 'other').trim() || 'other', document_type_label: String(item?.document_type_label || '').trim(), scene_code: String(item?.scene_code || 'other').trim() || 'other', scene_label: String(item?.scene_label || '').trim(), document_fields: Array.isArray(item?.document_fields) ? item.document_fields .map((field) => ({ key: String(field?.key || '').trim(), label: String(field?.label || '').trim(), value: String(field?.value || '').trim() })) .filter((field) => field.key && field.label && field.value) : [], warnings: Array.isArray(item?.warnings) ? item.warnings : [] })) } export function mergeRecognizedDocuments(current, incoming) { const next = [...current] incoming.forEach((document) => { const index = next.findIndex((item) => item.filename === document.filename) if (index >= 0) { next.splice(index, 1, document) } else { next.push(document) } }) return next } export function documentHasMeaningfulText(document) { return Boolean( String(document?.text || document?.summary || '').trim() || (Array.isArray(document?.document_fields) && document.document_fields.length) ) }