VetNet-LCPS / vetcopilot-app /src /utils /exportExcel.ts
thomasm291678
VetCopilot-LCPS: LCPS 2.0 本地推理引擎替换 DeepSeek API
793e953
Raw History Blame Contribute Delete
4.1 kB
/**
* CSV 导出工具
*
* 纯前端生成 CSV 文件并触发浏览器下载,无需后端参与。
* 自动处理中文编码(UTF-8 BOM),确保 Excel 能正确显示中文。
*/
import { formatDateStatic } from './dateUtils'
/**
* 将数据导出为 CSV 文件并触发下载
*
* @param filename - 下载文件名(不含扩展名)
* @param headers - CSV 表头行
* @param rows - CSV 数据行(二维数组)
*/
export function exportToCSV(
filename: string,
headers: string[],
rows: string[][],
): void {
// BOM 确保 Excel 正确识别 UTF-8 中文
const BOM = '\uFEFF'
// 转义单元格:如果包含逗号、引号或换行,用引号包裹
const escapeCell = (cell: string): string => {
const str = String(cell ?? '')
if (str.includes(',') || str.includes('"') || str.includes('\n') || str.includes('\r')) {
return `"${str.replace(/"/g, '""')}"`
}
return str
}
const headerLine = headers.map(escapeCell).join(',')
const dataLines = rows.map(row => row.map(escapeCell).join(','))
const csvContent = BOM + [headerLine, ...dataLines].join('\n')
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `${filename}.csv`
document.body.appendChild(a)
a.click()
a.remove()
URL.revokeObjectURL(url)
}
/** 患者导出所需的简要字段 */
export interface ExportPatient {
animal_name: string
species: string
breed: string
sex: string
age: string
owner_name: string
weight?: string
created_at: string
}
/**
* 导出患者列表为 CSV
*/
export function exportPatientsToCSV(patients: ExportPatient[], locale: string = 'zh'): void {
const headers = ['动物名', '物种', '品种', '性别', '年龄', '主人', '体重(kg)', '创建时间']
const rows = patients.map(p => [
p.animal_name ?? '',
p.species ?? '',
p.breed ?? '',
p.sex ?? '',
p.age ?? '',
p.owner_name ?? '',
p.weight ?? '',
p.created_at ? formatDateStatic(p.created_at, locale) : '',
])
const dateStr = new Date().toISOString().slice(0, 10)
exportToCSV(`患者档案_${dateStr}`, headers, rows)
}
/** 问诊导出所需的简要字段 */
export interface ExportConsultation {
animal_name: string
species: string
breed: string
owner_name: string
status: string
doctor_name: string
serial_number: string
created_at: string
}
/**
* 导出问诊列表为 CSV
*/
export function exportConsultationsToCSV(consultations: ExportConsultation[], locale: string = 'zh'): void {
const statusLabel = (s: string): string => {
const map: Record<string, string> = {
'draft': '草稿',
'final': '终稿',
'recording': '录音中',
'transcribing': '转写中',
'processing': '处理中',
'cancelled': '已取消',
}
return map[s] ?? s
}
const headers = ['动物名', '物种', '品种', '主人', '状态', '医师', '编号', '创建时间']
const rows = consultations.map(c => [
c.animal_name ?? '',
c.species ?? '',
c.breed ?? '',
c.owner_name ?? '',
statusLabel(c.status),
c.doctor_name ?? '',
c.serial_number ?? '',
c.created_at ? formatDateStatic(c.created_at, locale) : '',
])
const dateStr = new Date().toISOString().slice(0, 10)
exportToCSV(`问诊记录_${dateStr}`, headers, rows)
}
/**
* 导出图表数据为 CSV(通用)
*
* 适用于饼图、柱状图等简单 key-value 数据的导出。
* @param filename - 文件名(不含扩展名)
* @param labelHeader - 标签列标题
* @param valueHeader - 数值列标题
* @param data - 键值对数组
*/
export function exportChartDataToCSV(
filename: string,
labelHeader: string,
valueHeader: string,
data: Array<{ name: string; value: number }>,
): void {
const headers = [labelHeader, valueHeader]
const rows = data.map(d => [d.name, String(d.value)])
const dateStr = new Date().toISOString().slice(0, 10)
exportToCSV(`${filename}_${dateStr}`, headers, rows)
}