From 8d15eebe023c866ef7ab8a53162ac4e25ac1896b Mon Sep 17 00:00:00 2001 From: wisonic Date: Tue, 15 Oct 2024 17:49:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=9B=BE=E8=A1=A8):=20=E9=80=8F=E8=A7=86?= =?UTF-8?q?=E8=A1=A8=E6=A0=91=E5=BD=A2=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=B8=A6=E6=A0=BC=E5=BC=8F=E5=AF=BC=E5=87=BA=20#12320?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualization/ComponentEditBar.vue | 13 +- .../visualization/UserViewEnlarge.vue | 8 +- .../js/panel/common/common_table.ts | 157 +++++++++++++++++- 3 files changed, 153 insertions(+), 25 deletions(-) diff --git a/core/core-frontend/src/components/visualization/ComponentEditBar.vue b/core/core-frontend/src/components/visualization/ComponentEditBar.vue index b4adb45c34..31aecf4bc4 100644 --- a/core/core-frontend/src/components/visualization/ComponentEditBar.vue +++ b/core/core-frontend/src/components/visualization/ComponentEditBar.vue @@ -141,12 +141,9 @@ Excel - Excel(带格式) + Excel(带格式) 图片 @@ -185,10 +182,9 @@ Excel - Excel(带格式) + Excel(带格式) 图片 @@ -442,11 +438,6 @@ const exportAsFormattedExcel = () => { exportPivotExcel(s2Instance, chart) } -const enableFormattedExport = computed(() => { - const chart = dvMainStore.getViewDetails(element.value.id) as ChartObj - const mode = chart?.customAttr?.basicStyle?.tableLayoutMode - return mode === 'grid' -}) const exportAsExcel = () => { const viewDataInfo = dvMainStore.getViewDataDetails(element.value.id) const chartExtRequest = dvMainStore.getLastViewRequestInfo(element.value.id) diff --git a/core/core-frontend/src/components/visualization/UserViewEnlarge.vue b/core/core-frontend/src/components/visualization/UserViewEnlarge.vue index 64cebc6cfe..a30b6c2fb4 100644 --- a/core/core-frontend/src/components/visualization/UserViewEnlarge.vue +++ b/core/core-frontend/src/components/visualization/UserViewEnlarge.vue @@ -69,10 +69,9 @@ icon="Download" size="middle" :loading="exportLoading" - :disabled="!enableFormattedExport" @click="exportAsFormattedExcel" > - 导出Excel(带格式) + 导出Excel(带格式) @@ -348,11 +347,6 @@ const exportAsFormattedExcel = () => { const chart = dvMainStore.getViewDetails(viewInfo.value.id) exportPivotExcel(s2Instance, chart) } -const enableFormattedExport = computed(() => { - const chart = dvMainStore.getViewDetails(viewInfo.value.id) as ChartObj - const mode = chart?.customAttr?.basicStyle?.tableLayoutMode - return mode === 'grid' -}) const exportData = () => { useEmitt().emitter.emit('data-export-center', { activeName: 'IN_PROGRESS' }) } diff --git a/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts b/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts index bd623be139..315cf88099 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts @@ -25,7 +25,7 @@ import { type Meta, S2DataConfig } from '@antv/s2' -import { keys, intersection, filter, cloneDeep, merge, find } from 'lodash-es' +import { keys, intersection, filter, cloneDeep, merge, find, repeat } from 'lodash-es' import { createVNode, render } from 'vue' import TableTooltip from '@/views/chart/components/editor/common/TableTooltip.vue' import Exceljs from 'exceljs' @@ -1031,15 +1031,10 @@ function getTooltipPosition(event) { return result } -export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) { +export async function exportGridPivot(instance: PivotSheet, chart: ChartObj) { const { meta, fields } = instance.dataCfg const rowLength = fields?.rows?.length || 0 const colLength = fields?.columns?.length || 0 - const valueLength = fields?.values?.length || 0 - if (!(rowLength && valueLength)) { - ElMessage.warning('行维度或指标维度为空不可导出!') - return - } const workbook = new Exceljs.Workbook() const worksheet = workbook.addWorksheet(chart.title) const metaMap: Record = meta?.reduce((p, n) => { @@ -1056,11 +1051,20 @@ export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) { if (rowLength >= 2) { worksheet.mergeCells(index + 1, 1, index + 1, rowLength) } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } } + } }) fields?.rows?.forEach((row, index) => { const cell = worksheet.getCell(colLength + 1, index + 1) cell.value = metaMap[row]?.name ?? row cell.alignment = { vertical: 'middle', horizontal: 'center' } + cell.border = { + bottom: { style: 'thick', color: { argb: '00000000' } } + } + if (index === fields.rows.length - 1) { + cell.border.right = { style: 'thick', color: { argb: '00000000' } } + } }) const { layoutResult } = instance.facet // 行头 @@ -1084,6 +1088,9 @@ export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) { if (writeColIndex < maxColIndex) { worksheet.mergeCells(writeRowIndex, writeColIndex, writeRowIndex, maxColIndex) } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } } + } }) const getNodeStartRowIndex = (node: Node) => { @@ -1140,6 +1147,9 @@ export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) { if (writeRowIndex < maxColHeight) { worksheet.mergeCells(writeRowIndex, writeColIndex, maxColHeight, writeColIndex) } + cell.border = { + bottom: { style: 'thick', color: { argb: '00000000' } } + } }) const getNodeStartColIndex = (node: Node) => { if (!node.children?.length) { @@ -1190,3 +1200,136 @@ export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) { }) saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`) } + +export async function exportTreePivot(instance: PivotSheet, chart: ChartObj) { + const { meta, fields } = instance.dataCfg + const colLength = fields?.columns?.length || 0 + const workbook = new Exceljs.Workbook() + const worksheet = workbook.addWorksheet(chart.title) + + const metaMap: Record = meta?.reduce((p, n) => { + if (n.field) { + p[n.field] = n + } + return p + }, {}) + const layoutResult = instance.facet.layoutResult + + // 角头 + fields.columns?.forEach((column, index) => { + const cell = worksheet.getCell(index + 1, 1) + cell.value = metaMap[column]?.name ?? column + cell.alignment = { vertical: 'middle', horizontal: 'center' } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } } + } + }) + const maxColHeight = layoutResult.colsHierarchy.maxLevel + 1 + const rowName = fields?.rows?.map(row => metaMap[row]?.name ?? row).join('/') + const cell = worksheet.getCell(colLength + 1, 1) + cell.value = rowName + cell.alignment = { vertical: 'middle', horizontal: 'center' } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } }, + bottom: { style: 'thick', color: { argb: '00000000' } } + } + //行头 + const { rowLeafNodes } = layoutResult + rowLeafNodes.forEach((node, index) => { + const cell = worksheet.getCell(maxColHeight + index + 1, 1) + cell.value = repeat(' ', node.level) + node.label + cell.alignment = { vertical: 'middle', horizontal: 'left' } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } } + } + }) + // 列头 + const notLeafNodeWidthMap: Record = {} + const { colLeafNodes } = layoutResult + colLeafNodes.forEach(node => { + let curNode = node.parent + while (curNode) { + const width = notLeafNodeWidthMap[curNode.id] ?? 0 + notLeafNodeWidthMap[curNode.id] = width + 1 + curNode = curNode.parent + } + const { colIndex } = node + const writeRowIndex = node.level + 1 + const writeColIndex = colIndex + 1 + 1 + const cell = worksheet.getCell(writeRowIndex, writeColIndex) + let value = node.label + if (node.field === '$$extra$$' && metaMap[value]?.name) { + value = metaMap[value].name + } + cell.value = value + cell.alignment = { vertical: 'middle', horizontal: 'center' } + if (writeRowIndex < maxColHeight) { + worksheet.mergeCells(writeRowIndex, writeColIndex, maxColHeight, writeColIndex) + } + cell.border = { + bottom: { style: 'thick', color: { argb: '00000000' } } + } + }) + const colNodes = layoutResult.colNodes + const getNodeStartIndex = (node: Node) => { + if (!node.children?.length) { + return node.colIndex + 1 + } else { + return getNodeStartIndex(node.children[0]) + } + } + colNodes.forEach(node => { + if (node.isLeaf) { + return + } + const colIndex = getNodeStartIndex(node) + const width = notLeafNodeWidthMap[node.id] + const writeRowIndex = node.level + 1 + const mergeRowCount = node.children[0].level - node.level + const writeColIndex = colIndex + 1 + const cell = worksheet.getCell(writeRowIndex, writeColIndex) + cell.value = node.label + cell.alignment = { vertical: 'middle', horizontal: 'center' } + if (mergeRowCount > 1 || width > 1) { + worksheet.mergeCells( + writeRowIndex, + writeColIndex, + writeRowIndex + mergeRowCount - 1, + writeColIndex + width - 1 + ) + } + }) + // 单元格数据 + for (let rowIndex = 0; rowIndex < rowLeafNodes.length; rowIndex++) { + for (let colIndex = 0; colIndex < colLeafNodes.length; colIndex++) { + const dataCellMeta = layoutResult.getCellMeta(rowIndex, colIndex) + const { fieldValue } = dataCellMeta + if (fieldValue === 0 || fieldValue) { + const meta = metaMap[dataCellMeta.valueField] + const cell = worksheet.getCell(rowIndex + maxColHeight + 1, colIndex + 1 + 1) + const value = meta?.formatter?.(fieldValue) || fieldValue.toString() + cell.alignment = { vertical: 'middle', horizontal: 'center' } + cell.value = value + } + } + } + const buffer = await workbook.xlsx.writeBuffer() + const dataBlob = new Blob([buffer], { + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' + }) + saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`) +} +export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) { + const { fields } = instance.dataCfg + const rowLength = fields?.rows?.length || 0 + const valueLength = fields?.values?.length || 0 + if (!(rowLength && valueLength)) { + ElMessage.warning('行维度或指标维度为空不可导出!') + return + } + if (chart.customAttr.basicStyle.tableLayoutMode !== 'tree') { + exportGridPivot(instance, chart) + } else { + exportTreePivot(instance, chart) + } +}