/** * 查询结果导出工具 * 支持 CSV、JSON、Excel 格式 */ import { escapeHtml } from '@/utils/fileUtils' /** * 导出为 CSV */ export function exportToCSV(data, columns = [], filename = 'query-result.csv') { if (!data || !Array.isArray(data) || data.length === 0) { console.warn('No data to export') return false } try { // 自动检测列名 let headers = columns if (headers.length === 0) { headers = Object.keys(data[0]) } // 构建 CSV 内容 const rows = [] // 表头 rows.push(headers.join(',')) // 数据行 data.forEach(row => { const values = headers.map(header => { const value = row[header] // 处理 null/undefined if (value === null || value === undefined) return '' // 处理包含逗号或引号的值 const strValue = String(value) if (strValue.includes(',') || strValue.includes('"') || strValue.includes('\n')) { return `"${strValue.replace(/"/g, '""')}"` } return strValue }) rows.push(values.join(',')) }) // 添加 BOM 使 Excel 正确识别 UTF-8 const BOM = '\uFEFF' const csvContent = BOM + rows.join('\n') // 创建下载 downloadFile(csvContent, filename, 'text/csv;charset=utf-8') return true } catch (e) { console.error('Failed to export CSV:', e) return false } } /** * 导出为 JSON */ export function exportToJSON(data, filename = 'query-result.json', pretty = true) { if (!data || !Array.isArray(data)) { console.warn('No data to export') return false } try { const jsonContent = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data) downloadFile(jsonContent, filename, 'application/json;charset=utf-8') return true } catch (e) { console.error('Failed to export JSON:', e) return false } } /** * 导出为 Markdown 表格 */ export function exportToMarkdown(data, columns = [], filename = 'query-result.md') { if (!data || !Array.isArray(data) || data.length === 0) { console.warn('No data to export') return false } try { // 自动检测列名 let headers = columns if (headers.length === 0) { headers = Object.keys(data[0]) } // 构建 Markdown 表格 const rows = [] // 表头 rows.push('| ' + headers.join(' | ') + ' |') rows.push('| ' + headers.map(() => '---').join(' | ') + ' |') // 数据行 data.forEach(row => { const values = headers.map(header => { const value = row[header] if (value === null || value === undefined) return '' // 转义管道符 return String(value).replace(/\|/g, '\\|') }) rows.push('| ' + values.join(' | ') + ' |') }) const mdContent = rows.join('\n') downloadFile(mdContent, filename, 'text/markdown;charset=utf-8') return true } catch (e) { console.error('Failed to export Markdown:', e) return false } } /** * 导出为 Excel (HTML 表格格式) */ export function exportToExcel(data, columns = [], filename = 'query-result.xls') { if (!data || !Array.isArray(data) || data.length === 0) { console.warn('No data to export') return false } try { // 自动检测列名 let headers = columns if (headers.length === 0) { headers = Object.keys(data[0]) } // 构建 HTML 表格 let html = '\n' // 表头 html += ' \n \n' headers.forEach(header => { html += ` \n` }) html += ' \n \n' // 表体 html += ' \n' data.forEach(row => { html += ' \n' headers.forEach(header => { const value = row[header] const displayValue = value === null || value === undefined ? '' : String(value) html += ` \n` }) html += ' \n' }) html += ' \n' html += '
${escapeHtml(header)}
${escapeHtml(displayValue)}
' downloadFile(html, filename, 'application/vnd.ms-excel;charset=utf-8') return true } catch (e) { console.error('Failed to export Excel:', e) return false } } /** * 下载文件 */ function downloadFile(content, filename, mimeType) { const blob = new Blob([content], { type: mimeType }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = filename document.body.appendChild(link) link.click() document.body.removeChild(link) URL.revokeObjectURL(url) } /** * 复制到剪贴板 */ export async function copyToClipboard(data, format = 'json') { if (!data) return false try { let content = '' switch (format) { case 'json': content = JSON.stringify(data, null, 2) break case 'csv': if (data.length === 0) return false const headers = Object.keys(data[0]) content = headers.join(',') + '\n' data.forEach(row => { content += headers.map(h => row[h] ?? '').join(',') + '\n' }) break default: content = String(data) } await navigator.clipboard.writeText(content) return true } catch (e) { console.error('Failed to copy to clipboard:', e) return false } }