feat:Roaming CDR自定义导出功能

This commit is contained in:
zhongzm
2025-10-20 17:04:16 +08:00
parent 81f287e9ff
commit 719a08d993
4 changed files with 730 additions and 4 deletions

View File

@@ -22,6 +22,8 @@ import saveAs from 'file-saver';
import { useClipboard } from '@vueuse/core';
import dayjs, { type Dayjs } from 'dayjs';
import { dayjsRanges } from '@/hooks/useRangePicker';
import ExportCustomModal from '@/components/ExportCustomModal/index.vue';
import * as XLSX from 'xlsx';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n();
const ws = new WS();
@@ -397,9 +399,243 @@ function fnExportList() {
});
}
/**自定义导出 - 先获取后端数据来确定可用列 */
function fnExportCustom() {
if (modalState.confirmLoading || tablePagination.total === 0) return;
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
// 先获取后端标准格式的完整数据
const querys = toRaw(queryParams);
querys.pageNum = 1;
querys.pageSize = Math.min(tablePagination.total, 10); // 只获取前10条用于分析列结构
querys.startTime = Number(querys.startTime);
querys.endTime = Number(querys.endTime);
exportSGWCDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
// 解析后端Excel文件获取可用的列
parseExcelColumns(res.data);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.catch(error => {
console.error('Export error:', error);
message.error({
content: t('common.operateError'),
duration: 3,
});
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
}
/**解析Excel获取可用的列信息 */
function parseExcelColumns(excelBlob: Blob) {
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = new Uint8Array(e.target?.result as ArrayBuffer);
const workbook = XLSX.read(data, { type: 'array' });
// 获取第一个工作表
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
// 将工作表转换为JSON格式
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
if (jsonData.length === 0) {
message.error(t('common.noData'));
return;
}
// 获取表头(第一行)
const headers = jsonData[0] as string[];
const dataRows = jsonData.slice(1, 4); // 取前3行作为示例数据
// 构建可用列配置
const availableColumns = headers.map((header, index) => ({
key: `col_${index}`,
title: header,
originalTitle: header,
dataIndex: `col_${index}`,
columnIndex: index,
visible: true
}));
exportAvailableColumns.value = availableColumns;
// 构建示例数据
const sampleData = dataRows.map((row: any) => {
const obj: any = { id: Math.random() };
headers.forEach((header, index) => {
obj[`col_${index}`] = row[index] || '';
});
return obj;
});
exportSampleData.value = sampleData;
// 打开自定义导出对话框
exportCustomVisible.value = true;
} catch (error) {
console.error('Parse Excel error:', error);
message.error(t('common.operateError'));
}
};
reader.readAsArrayBuffer(excelBlob);
}
/**处理自定义导出确认 */
function handleExportCustomConfirm(config: any[]) {
exportCustomConfig.value = config;
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
// 先获取后端标准格式的完整数据
const querys = toRaw(queryParams);
querys.pageNum = 1;
querys.pageSize = tablePagination.total;
querys.startTime = Number(querys.startTime);
querys.endTime = Number(querys.endTime);
exportSGWCDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
// 后端返回标准格式数据后,在前端进行自定义处理
processCustomExport(res.data, config);
message.success({
content: t('common.operateOk'),
duration: 3,
});
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.catch(error => {
console.error('Export error:', error);
message.error({
content: t('common.operateError'),
duration: 3,
});
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
}
/**处理自定义导出 - 基于后端数据在前端自定义处理 */
function processCustomExport(excelBlob: Blob, config: any[]) {
// 读取后端返回的Excel文件
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = new Uint8Array(e.target?.result as ArrayBuffer);
const workbook = XLSX.read(data, { type: 'array' });
// 获取第一个工作表
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
// 将工作表转换为JSON格式
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
if (jsonData.length === 0) {
message.error(t('common.noData'));
return;
}
// 获取表头(第一行)
const originalHeaders = jsonData[0] as string[];
const dataRows = jsonData.slice(1);
// 根据配置处理数据
const processedData = processDataWithConfig(originalHeaders, dataRows, config);
// 生成新的Excel文件
generateCustomExcelFile(processedData);
} catch (error) {
console.error('Process custom export error:', error);
message.error(t('common.operateError'));
}
};
reader.readAsArrayBuffer(excelBlob);
}
/**根据配置处理数据 */
function processDataWithConfig(originalHeaders: string[], dataRows: any[], config: any[]) {
// 获取可见的列配置
const visibleColumns = config.filter(col => col.visible);
// 处理表头
const newHeaders = visibleColumns.map(col => col.title);
// 处理数据行 - 使用columnIndex直接访问
const newDataRows = dataRows.map(row => {
return visibleColumns.map(col => {
// 使用columnIndex字段直接访问对应列的数据
const columnIndex = col.columnIndex;
return columnIndex !== undefined ? (row[columnIndex] || '') : '';
});
});
return {
headers: newHeaders,
data: newDataRows
};
}
/**生成自定义Excel文件 */
function generateCustomExcelFile(processedData: { headers: string[], data: any[] }) {
// 创建工作簿
const wb = XLSX.utils.book_new();
// 准备Excel数据
const excelData = [processedData.headers, ...processedData.data];
// 创建工作表
const ws = XLSX.utils.aoa_to_sheet(excelData);
// 设置列宽
const colWidths = processedData.headers.map(() => ({ wch: 20 }));
ws['!cols'] = colWidths;
// 添加工作表到工作簿
XLSX.utils.book_append_sheet(wb, ws, 'SGWC CDR');
// 生成Excel文件并下载
const excelBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
saveAs(blob, `sgwc_cdr_custom_export_${Date.now()}.xlsx`);
}
/**实时数据开关 */
const realTimeData = ref<boolean>(false);
/**自定义导出配置 */
const exportCustomVisible = ref<boolean>(false);
const exportCustomConfig = ref<any[]>([]);
const exportAvailableColumns = ref<any[]>([]);
const exportSampleData = ref<any[]>([]);
/**
* 实时数据
*/
@@ -619,10 +855,25 @@ onBeforeUnmount(() => {
{{ t('common.deleteText') }}
</a-button>
<a-button type="dashed" @click.prevent="fnExportList()">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="dashed">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
<DownOutlined />
</a-button>
<template #overlay>
<a-menu>
<a-menu-item key="export-default" @click="fnExportList()">
<template #icon><ExportOutlined /></template>
{{ t('common.exportDefault') }}
</a-menu-item>
<a-menu-item key="export-custom" @click="fnExportCustom()">
<template #icon><SettingOutlined /></template>
{{ t('common.exportCustom') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-space>
</template>
@@ -830,6 +1081,14 @@ onBeforeUnmount(() => {
</template>
</a-table>
</a-card>
<!-- 自定义导出配置模态框 -->
<ExportCustomModal
v-model:open="exportCustomVisible"
:original-columns="exportAvailableColumns"
:sample-data="exportSampleData"
@confirm="handleExportCustomConfirm"
/>
</PageContainer>
</template>