import type { VehicleListItem } from './types'; export type ExportRow = Record; /** Use complete filtered groups, never the progressively rendered slice. Repeat parent labels for Excel filtering. */ export function statisticsExportRows(labels: string[], groups: { values: string[]; count: number; extra?: ExportRow }[], total: number, unit = '台'): ExportRow[] { return groups.map(group => ({ ...Object.fromEntries(labels.length ? labels.map((label, index) => [label, group.values[index]]) : [['统计范围', '当前筛选结果']]), [`数量(${unit})`]: group.count, ...group.extra, '占比(%)': total ? Number((group.count / total * 100).toFixed(1)) : 0, })); } export function vehicleExportRows(vehicles: VehicleListItem[], inventory = false): ExportRow[] { const status: Record = { Abnormal: '异动', Pending: '待交车', Inventory: '在库', Operating: '运营中' }; const operation: Record = { '1': '租赁', '2': '自营', '3': '可运营', '4': '待运营' }; const hasPending = vehicles.some(v => v.pendingDelivery); const hasMovement = vehicles.some(v => v.abnormalMove); return vehicles.map(v => ({ 车牌: v.plateNumber, VIN: v.vin, 品牌: v.brandLabel, 车型: v.type, 型号: v.model, 省份: v.province, 城市: v.city || v.location, 状态: status[v.status] || v.status, 运营状态: operation[v.operationStatus || ''] || v.operationStatus, ...(inventory ? { 运维负责人: v.opsManagers?.trim() || '-' } : { 客户: v.customerName, 签约公司: v.orgName, 部门: v.departmentName, 业务负责人: v.customerManager }), 所属公司: v.subjectOrg, 合同: v.contractNo, ...(hasPending ? { 待交车合同: v.pendingDelivery?.contractNo, 待交车客户: v.pendingDelivery?.customerName } : {}), ...(hasMovement ? { 异动类型: v.abnormalMove?.type, 审批通过时间: v.abnormalMove?.approvalPassedAt, '已异动时长(天)': v.abnormalMove?.elapsedDays == null ? null : Number(v.abnormalMove.elapsedDays.toFixed(1)), 具体工作: v.abnormalMove?.work, 申请人: v.abnormalMove?.applicant, 预计完成时间: v.abnormalMove?.targetCompletionTime, 实际完成时间: v.abnormalMove?.actualCompletionTime || (v.abnormalMove?.status === '待结束' ? '未完成' : null), } : {}), })); } export async function createAnalysisWorkbook(statistics: ExportRow[], details: ExportRow[]) { const XLSX = await import('xlsx'); const workbook = XLSX.utils.book_new(); for (const [name, rows] of [['统计表', statistics], ['详情表', details]] as const) { const sheet = XLSX.utils.json_to_sheet(rows.map(row => Object.fromEntries(Object.entries(row).map(([key, value]) => [key, value ?? '—'])))); sheet['!cols'] = Object.keys(rows[0] || {}).map(label => ({ wch: /时间|合同|项目|工作/.test(label) ? 28 : 18 })); if (sheet['!ref']) sheet['!autofilter'] = { ref: sheet['!ref'] }; XLSX.utils.book_append_sheet(workbook, sheet, name); } return workbook; } export async function downloadAnalysisWorkbook(statistics: ExportRow[], details: ExportRow[], title: string) { const workbook = await createAnalysisWorkbook(statistics, details); const XLSX = await import('xlsx'); const safeTitle = title.replace(/[\\/:*?"<>|\x00-\x1f]/g, '-').slice(0, 120); XLSX.writeFile(workbook, `${safeTitle}.xlsx`, { compression: true }); }