feat: 增加报表 PDF 下载
This commit is contained in:
@@ -64,6 +64,13 @@ export interface PageResult<T> {
|
|||||||
data: T[]
|
data: T[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ReportExportFormat = 'csv' | 'xlsx' | 'pdf'
|
||||||
|
|
||||||
|
export interface DownloadedReport {
|
||||||
|
blob: Blob
|
||||||
|
fileName: string
|
||||||
|
}
|
||||||
|
|
||||||
// ============ 报表生成参数接口 ============
|
// ============ 报表生成参数接口 ============
|
||||||
|
|
||||||
export interface TrafficReportParams {
|
export interface TrafficReportParams {
|
||||||
@@ -239,8 +246,8 @@ export const fetchReportMetricsRegistry = (params: { data_source: string }) =>
|
|||||||
/** 查看报表内容 */
|
/** 查看报表内容 */
|
||||||
export const fetchReportContent = (id: number) => request.get<ApiResponse<Record<string, any>>>(`/DC-Control/v1/reports/${id}/content`)
|
export const fetchReportContent = (id: number) => request.get<ApiResponse<Record<string, any>>>(`/DC-Control/v1/reports/${id}/content`)
|
||||||
|
|
||||||
/** 原始 ArrayBuffer → 下载用 Blob;xlsx 校验 ZIP 魔数 PK */
|
/** 原始 ArrayBuffer → 下载用 Blob;xlsx 校验 ZIP 魔数 PK,PDF 校验 %PDF- */
|
||||||
function exportBufferToBlob(ab: ArrayBuffer, format: 'csv' | 'xlsx', contentType: string | null): Blob {
|
function exportBufferToBlob(ab: ArrayBuffer, format: ReportExportFormat, contentType: string | null): Blob {
|
||||||
const u8 = new Uint8Array(ab)
|
const u8 = new Uint8Array(ab)
|
||||||
if (format === 'xlsx') {
|
if (format === 'xlsx') {
|
||||||
const okZip = u8.length >= 4 && u8[0] === 0x50 && u8[1] === 0x4b
|
const okZip = u8.length >= 4 && u8[0] === 0x50 && u8[1] === 0x4b
|
||||||
@@ -265,16 +272,42 @@ function exportBufferToBlob(ab: ArrayBuffer, format: 'csv' | 'xlsx', contentType
|
|||||||
: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
return new Blob([ab], { type: mime })
|
return new Blob([ab], { type: mime })
|
||||||
}
|
}
|
||||||
|
if (format === 'pdf') {
|
||||||
|
const okPdf =
|
||||||
|
u8.length >= 5 &&
|
||||||
|
u8[0] === 0x25 &&
|
||||||
|
u8[1] === 0x50 &&
|
||||||
|
u8[2] === 0x44 &&
|
||||||
|
u8[3] === 0x46 &&
|
||||||
|
u8[4] === 0x2d
|
||||||
|
if (!okPdf) {
|
||||||
|
throw new Error('导出失败:服务器返回的不是有效的 PDF。')
|
||||||
|
}
|
||||||
|
const mime = contentType && /pdf/i.test(contentType) ? contentType : 'application/pdf'
|
||||||
|
return new Blob([ab], { type: mime })
|
||||||
|
}
|
||||||
const mime =
|
const mime =
|
||||||
contentType && /csv|text|plain/i.test(contentType) ? contentType : 'text/csv;charset=utf-8'
|
contentType && /csv|text|plain/i.test(contentType) ? contentType : 'text/csv;charset=utf-8'
|
||||||
return new Blob([ab], { type: mime })
|
return new Blob([ab], { type: mime })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseContentDispositionFileName(value: string | null): string | undefined {
|
||||||
|
if (!value) return undefined
|
||||||
|
|
||||||
|
const utf8FileName = value.match(/filename\*=UTF-8''([^;]+)/i)
|
||||||
|
if (utf8FileName) return decodeURIComponent(utf8FileName[1])
|
||||||
|
|
||||||
|
return value.match(/filename="([^"]+)"/i)?.[1]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 报表文件导出:使用 fetch + arrayBuffer,绕过 axios 拦截器与 Blob 中间态,
|
* 报表文件导出:使用 fetch + arrayBuffer,绕过 axios 拦截器与 Blob 中间态,
|
||||||
* 避免网络面板里已是合法 xlsx(PK…)但落盘文件损坏的情况。
|
* 避免网络面板里已是合法 xlsx(PK…)但落盘文件损坏的情况。
|
||||||
*/
|
*/
|
||||||
export const exportReport = async (id: number, format: 'csv' | 'xlsx' = 'csv'): Promise<Blob> => {
|
export const exportReport = async (
|
||||||
|
id: number,
|
||||||
|
format: ReportExportFormat = 'csv',
|
||||||
|
): Promise<DownloadedReport> => {
|
||||||
const base = String(import.meta.env.VITE_API_BASE_URL || '').replace(/\/$/, '')
|
const base = String(import.meta.env.VITE_API_BASE_URL || '').replace(/\/$/, '')
|
||||||
const url = `${base}/DC-Control/v1/reports/${id}/export?format=${encodeURIComponent(format)}`
|
const url = `${base}/DC-Control/v1/reports/${id}/export?format=${encodeURIComponent(format)}`
|
||||||
const token = SafeStorage.get(AppStorageKey.TOKEN)
|
const token = SafeStorage.get(AppStorageKey.TOKEN)
|
||||||
@@ -296,7 +329,10 @@ export const exportReport = async (id: number, format: 'csv' | 'xlsx' = 'csv'):
|
|||||||
}
|
}
|
||||||
throw new Error(msg)
|
throw new Error(msg)
|
||||||
}
|
}
|
||||||
return exportBufferToBlob(ab, format, r.headers.get('content-type'))
|
return {
|
||||||
|
blob: exportBufferToBlob(ab, format, r.headers.get('content-type')),
|
||||||
|
fileName: parseContentDispositionFileName(r.headers.get('content-disposition')) || `report_${id}.${format}`,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ 监测指标类接口(旧版兼容) ============
|
// ============ 监测指标类接口(旧版兼容) ============
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
||||||
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
||||||
|
<a-doption @click="handleExport('pdf')">导出 PDF</a-doption>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
||||||
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('pdf', record)">PDF</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
@@ -211,12 +213,13 @@ import {
|
|||||||
fetchReportList,
|
fetchReportList,
|
||||||
generateReport,
|
generateReport,
|
||||||
fetchReportContent,
|
fetchReportContent,
|
||||||
exportReport,
|
|
||||||
ReportType,
|
ReportType,
|
||||||
type ReportRecord,
|
type ReportRecord,
|
||||||
type NetworkDeviceReportParams,
|
type NetworkDeviceReportParams,
|
||||||
} from '@/api/ops/report'
|
} from '@/api/ops/report'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
|
import { downloadReportFile } from '../useReportExport'
|
||||||
|
import type { ReportExportFormat } from '@/api/ops/report'
|
||||||
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
||||||
import { useReportNetworkDevicePickOptions } from '../useReportNetworkDevicePickOptions'
|
import { useReportNetworkDevicePickOptions } from '../useReportNetworkDevicePickOptions'
|
||||||
|
|
||||||
@@ -560,7 +563,7 @@ const handleViewContent = async (record?: ReportRecord) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导出报表
|
// 导出报表
|
||||||
const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
const handleExport = async (format: ReportExportFormat, record?: ReportRecord) => {
|
||||||
const targetRecord = record || selectedRecord.value
|
const targetRecord = record || selectedRecord.value
|
||||||
if (!targetRecord) {
|
if (!targetRecord) {
|
||||||
Message.warning('请选择要导出的报表')
|
Message.warning('请选择要导出的报表')
|
||||||
@@ -575,18 +578,7 @@ const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
|||||||
exporting.value = true
|
exporting.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blob = await exportReport(targetRecord.id, format)
|
await downloadReportFile(targetRecord, format)
|
||||||
|
|
||||||
// 创建下载链接
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = url
|
|
||||||
link.download = `report_${targetRecord.id}.${format}`
|
|
||||||
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
document.body.removeChild(link)
|
|
||||||
window.URL.revokeObjectURL(url)
|
|
||||||
|
|
||||||
Message.success('导出成功')
|
Message.success('导出成功')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
||||||
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
||||||
|
<a-doption @click="handleExport('pdf')">导出 PDF</a-doption>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -74,6 +75,7 @@
|
|||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
||||||
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('pdf', record)">PDF</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
@@ -235,13 +237,14 @@ import {
|
|||||||
fetchReportList,
|
fetchReportList,
|
||||||
generateReport,
|
generateReport,
|
||||||
fetchReportContent,
|
fetchReportContent,
|
||||||
exportReport,
|
|
||||||
exportEvidenceReport,
|
exportEvidenceReport,
|
||||||
ReportType,
|
ReportType,
|
||||||
type ReportRecord,
|
type ReportRecord,
|
||||||
type FaultReportParams,
|
type FaultReportParams,
|
||||||
} from '@/api/ops/report'
|
} from '@/api/ops/report'
|
||||||
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
||||||
|
import { downloadReportFile } from '../useReportExport'
|
||||||
|
import type { ReportExportFormat } from '@/api/ops/report'
|
||||||
import { useFaultReportServiceIdentityOptions } from '../useReportTargetIdentityOptions'
|
import { useFaultReportServiceIdentityOptions } from '../useReportTargetIdentityOptions'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -619,7 +622,7 @@ const handleViewContent = async (record?: ReportRecord) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导出报表
|
// 导出报表
|
||||||
const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
const handleExport = async (format: ReportExportFormat, record?: ReportRecord) => {
|
||||||
const targetRecord = record || selectedRecord.value
|
const targetRecord = record || selectedRecord.value
|
||||||
if (!targetRecord) {
|
if (!targetRecord) {
|
||||||
Message.warning('请选择要导出的报表')
|
Message.warning('请选择要导出的报表')
|
||||||
@@ -634,18 +637,7 @@ const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
|||||||
exporting.value = true
|
exporting.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blob = await exportReport(targetRecord.id, format)
|
await downloadReportFile(targetRecord, format)
|
||||||
|
|
||||||
// 创建下载链接
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = url
|
|
||||||
link.download = `report_${targetRecord.id}.${format}`
|
|
||||||
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
document.body.removeChild(link)
|
|
||||||
window.URL.revokeObjectURL(url)
|
|
||||||
|
|
||||||
Message.success('导出成功')
|
Message.success('导出成功')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
||||||
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
||||||
|
<a-doption @click="handleExport('pdf')">导出 PDF</a-doption>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
||||||
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('pdf', record)">PDF</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
@@ -211,12 +213,13 @@ import {
|
|||||||
fetchReportList,
|
fetchReportList,
|
||||||
generateReport,
|
generateReport,
|
||||||
fetchReportContent,
|
fetchReportContent,
|
||||||
exportReport,
|
|
||||||
ReportType,
|
ReportType,
|
||||||
type ReportRecord,
|
type ReportRecord,
|
||||||
type ServerReportParams,
|
type ServerReportParams,
|
||||||
} from '@/api/ops/report'
|
} from '@/api/ops/report'
|
||||||
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
||||||
|
import { downloadReportFile } from '../useReportExport'
|
||||||
|
import type { ReportExportFormat } from '@/api/ops/report'
|
||||||
import { useReportServerPickOptions } from '../useReportServerPickOptions'
|
import { useReportServerPickOptions } from '../useReportServerPickOptions'
|
||||||
|
|
||||||
const { serverIdentityOptions, serverOptionsLoading, loadServerPickOptions } = useReportServerPickOptions()
|
const { serverIdentityOptions, serverOptionsLoading, loadServerPickOptions } = useReportServerPickOptions()
|
||||||
@@ -548,7 +551,7 @@ const handleViewContent = async (record?: ReportRecord) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导出报表
|
// 导出报表
|
||||||
const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
const handleExport = async (format: ReportExportFormat, record?: ReportRecord) => {
|
||||||
const targetRecord = record || selectedRecord.value
|
const targetRecord = record || selectedRecord.value
|
||||||
if (!targetRecord) {
|
if (!targetRecord) {
|
||||||
Message.warning('请选择要导出的报表')
|
Message.warning('请选择要导出的报表')
|
||||||
@@ -563,18 +566,7 @@ const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
|||||||
exporting.value = true
|
exporting.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blob = await exportReport(targetRecord.id, format)
|
await downloadReportFile(targetRecord, format)
|
||||||
|
|
||||||
// 创建下载链接
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = url
|
|
||||||
link.download = `report_${targetRecord.id}.${format}`
|
|
||||||
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
document.body.removeChild(link)
|
|
||||||
window.URL.revokeObjectURL(url)
|
|
||||||
|
|
||||||
Message.success('导出成功')
|
Message.success('导出成功')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
||||||
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
||||||
|
<a-doption @click="handleExport('pdf')">导出 PDF</a-doption>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
||||||
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('pdf', record)">PDF</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
@@ -248,12 +250,13 @@ import {
|
|||||||
fetchReportList,
|
fetchReportList,
|
||||||
generateReport,
|
generateReport,
|
||||||
fetchReportContent,
|
fetchReportContent,
|
||||||
exportReport,
|
|
||||||
ReportType,
|
ReportType,
|
||||||
type ReportRecord,
|
type ReportRecord,
|
||||||
type StatisticsReportParams,
|
type StatisticsReportParams,
|
||||||
} from '@/api/ops/report'
|
} from '@/api/ops/report'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
|
import { downloadReportFile } from '../useReportExport'
|
||||||
|
import type { ReportExportFormat } from '@/api/ops/report'
|
||||||
import { useReportTargetIdentityOptions } from '../useReportTargetIdentityOptions'
|
import { useReportTargetIdentityOptions } from '../useReportTargetIdentityOptions'
|
||||||
import { useReportMetricRegistryOptions } from '../useReportMetricRegistryOptions'
|
import { useReportMetricRegistryOptions } from '../useReportMetricRegistryOptions'
|
||||||
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
||||||
@@ -634,7 +637,7 @@ const handleViewContent = async (record?: ReportRecord) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导出报表
|
// 导出报表
|
||||||
const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
const handleExport = async (format: ReportExportFormat, record?: ReportRecord) => {
|
||||||
const targetRecord = record || selectedRecord.value
|
const targetRecord = record || selectedRecord.value
|
||||||
if (!targetRecord) {
|
if (!targetRecord) {
|
||||||
Message.warning('请选择要导出的报表')
|
Message.warning('请选择要导出的报表')
|
||||||
@@ -649,18 +652,7 @@ const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
|||||||
exporting.value = true
|
exporting.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blob = await exportReport(targetRecord.id, format)
|
await downloadReportFile(targetRecord, format)
|
||||||
|
|
||||||
// 创建下载链接
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = url
|
|
||||||
link.download = `report_${targetRecord.id}.${format}`
|
|
||||||
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
document.body.removeChild(link)
|
|
||||||
window.URL.revokeObjectURL(url)
|
|
||||||
|
|
||||||
Message.success('导出成功')
|
Message.success('导出成功')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
||||||
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
||||||
|
<a-doption @click="handleExport('pdf')">导出 PDF</a-doption>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
||||||
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('pdf', record)">PDF</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
@@ -238,12 +240,13 @@ import {
|
|||||||
fetchReportList,
|
fetchReportList,
|
||||||
generateReport,
|
generateReport,
|
||||||
fetchReportContent,
|
fetchReportContent,
|
||||||
exportReport,
|
|
||||||
ReportType,
|
ReportType,
|
||||||
type ReportRecord,
|
type ReportRecord,
|
||||||
type TopNReportParams,
|
type TopNReportParams,
|
||||||
} from '@/api/ops/report'
|
} from '@/api/ops/report'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
|
import { downloadReportFile } from '../useReportExport'
|
||||||
|
import type { ReportExportFormat } from '@/api/ops/report'
|
||||||
import { useReportTargetIdentityOptions } from '../useReportTargetIdentityOptions'
|
import { useReportTargetIdentityOptions } from '../useReportTargetIdentityOptions'
|
||||||
import { useReportMetricRegistryOptions } from '../useReportMetricRegistryOptions'
|
import { useReportMetricRegistryOptions } from '../useReportMetricRegistryOptions'
|
||||||
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
||||||
@@ -639,7 +642,7 @@ const handleViewContent = async (record?: ReportRecord) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导出报表
|
// 导出报表
|
||||||
const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
const handleExport = async (format: ReportExportFormat, record?: ReportRecord) => {
|
||||||
const targetRecord = record || selectedRecord.value
|
const targetRecord = record || selectedRecord.value
|
||||||
if (!targetRecord) {
|
if (!targetRecord) {
|
||||||
Message.warning('请选择要导出的报表')
|
Message.warning('请选择要导出的报表')
|
||||||
@@ -654,18 +657,7 @@ const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
|||||||
exporting.value = true
|
exporting.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blob = await exportReport(targetRecord.id, format)
|
await downloadReportFile(targetRecord, format)
|
||||||
|
|
||||||
// 创建下载链接
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = url
|
|
||||||
link.download = `report_${targetRecord.id}.${format}`
|
|
||||||
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
document.body.removeChild(link)
|
|
||||||
window.URL.revokeObjectURL(url)
|
|
||||||
|
|
||||||
Message.success('导出成功')
|
Message.success('导出成功')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
<a-doption @click="handleExport('csv')">导出 CSV</a-doption>
|
||||||
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
<a-doption @click="handleExport('xlsx')">导出 Excel</a-doption>
|
||||||
|
<a-doption @click="handleExport('pdf')">导出 PDF</a-doption>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleViewContent(record)">查看</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('csv', record)">CSV</a-button>
|
||||||
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('xlsx', record)">Excel</a-button>
|
||||||
|
<a-button v-if="record.status === 'success'" type="text" size="small" @click="handleExport('pdf', record)">PDF</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
@@ -323,12 +325,13 @@ import {
|
|||||||
fetchReportList,
|
fetchReportList,
|
||||||
generateReport,
|
generateReport,
|
||||||
fetchReportContent,
|
fetchReportContent,
|
||||||
exportReport,
|
|
||||||
ReportType,
|
ReportType,
|
||||||
type ReportRecord,
|
type ReportRecord,
|
||||||
type TrafficReportParams,
|
type TrafficReportParams,
|
||||||
} from '@/api/ops/report'
|
} from '@/api/ops/report'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
|
import { downloadReportFile } from '../useReportExport'
|
||||||
|
import type { ReportExportFormat } from '@/api/ops/report'
|
||||||
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
import { normalizeReportRows, reportStatusColor, reportStatusLabel } from '../useReportListRow'
|
||||||
import { useReportTopologyOptions } from '../useReportTopologyOptions'
|
import { useReportTopologyOptions } from '../useReportTopologyOptions'
|
||||||
import { useReportTargetIdentityOptions } from '../useReportTargetIdentityOptions'
|
import { useReportTargetIdentityOptions } from '../useReportTargetIdentityOptions'
|
||||||
@@ -815,7 +818,7 @@ const handleViewContent = async (record?: ReportRecord) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导出报表
|
// 导出报表
|
||||||
const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
const handleExport = async (format: ReportExportFormat, record?: ReportRecord) => {
|
||||||
const targetRecord = record || selectedRecord.value
|
const targetRecord = record || selectedRecord.value
|
||||||
if (!targetRecord) {
|
if (!targetRecord) {
|
||||||
Message.warning('请选择要导出的报表')
|
Message.warning('请选择要导出的报表')
|
||||||
@@ -830,18 +833,7 @@ const handleExport = async (format: 'csv' | 'xlsx', record?: ReportRecord) => {
|
|||||||
exporting.value = true
|
exporting.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blob = await exportReport(targetRecord.id, format)
|
await downloadReportFile(targetRecord, format)
|
||||||
|
|
||||||
// 创建下载链接
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = url
|
|
||||||
link.download = `report_${targetRecord.id}.${format}`
|
|
||||||
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
document.body.removeChild(link)
|
|
||||||
window.URL.revokeObjectURL(url)
|
|
||||||
|
|
||||||
Message.success('导出成功')
|
Message.success('导出成功')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
16
src/views/ops/pages/report/useReportExport.ts
Normal file
16
src/views/ops/pages/report/useReportExport.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { exportReport, type ReportExportFormat, type ReportRecord } from '@/api/ops/report'
|
||||||
|
|
||||||
|
export async function downloadReportFile(record: ReportRecord, format: ReportExportFormat): Promise<void> {
|
||||||
|
const { blob, fileName } = await exportReport(record.id, format)
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
try {
|
||||||
|
link.href = url
|
||||||
|
link.download = fileName
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
} finally {
|
||||||
|
link.remove()
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user