From 84f4a7386f7859edf5921d918eb43a2e24370367 Mon Sep 17 00:00:00 2001 From: jianneng-fit2cloud Date: Thu, 4 Sep 2025 13:55:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=9B=BE=E8=A1=A8):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=98=8E=E7=BB=86=E8=A1=A8=E5=9F=BA=E7=A1=80=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E5=8B=BE=E9=80=89=E2=80=9C=E8=87=AA=E5=8A=A8=E6=8D=A2=E8=A1=8C?= =?UTF-8?q?=E2=80=9D=E5=90=8E=EF=BC=8C=E4=BC=9A=E5=87=BA=E7=8E=B0=E8=A1=A8?= =?UTF-8?q?=E5=A4=B4=E9=83=A8=E5=88=86=E9=83=A8=E5=88=86=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E4=B8=8D=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98=20#16804?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/panel/charts/table/table-info.ts | 75 +++++---- .../js/panel/common/common_table.ts | 149 ++++++++++++++++-- 2 files changed, 184 insertions(+), 40 deletions(-) diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-info.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-info.ts index f3c6e89b00..abf5fe1dda 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-info.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-info.ts @@ -16,21 +16,22 @@ import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common' import { useI18n } from '@/hooks/web/useI18n' import { filter, isEqual, isNumber, merge } from 'lodash-es' import { + calcTreeWidth, + calculateGroupHeaderHeight, + calculateHeaderHeight, + configEmptyDataStyle, copyContent, CustomDataCell, CustomTableColCell, - getRowIndex, - calculateHeaderHeight, - SortTooltip, - configEmptyDataStyle, - getLeafNodes, - getColumns, drawImage, + getColumns, + getLeafNodes, + getRowIndex, + getStartPosition, getSummaryRow, + SortTooltip, SummaryCell, - summaryRowStyle, - calcTreeWidth, - getStartPosition + summaryRowStyle } from '@/views/chart/components/js/panel/common/common_table' const { t } = useI18n() @@ -236,28 +237,48 @@ export class TableInfo extends S2ChartView { summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary) // 开启自动换行 if (basicStyle.autoWrap && !tableCell.mergeCells) { - // 调整表头宽度时,计算表头高度 + // 记录调整列宽的信息 + const setResizeColWidthInfo = (info?) => { + newChart.store.set('resizeColWidthInfo', info ? info : undefined) + } + setResizeColWidthInfo() + // 计算分组表头的高度 + newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => { + calculateGroupHeaderHeight(newChart, tableHeader, basicStyle) + setResizeColWidthInfo() + }) + // 调整行高不能小于初始行高 + newChart.on(S2Event.LAYOUT_RESIZE_COL_HEIGHT, info => { + if (info.info.resizedHeight < newChart.options.style.colCfg.height) { + info.style.colCfg.heightByField[info.info.id] = newChart.options.style.colCfg.height + } + }) + // 调整表头单元格宽度时,计算表头高度 newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, info => { + setResizeColWidthInfo(info.info) calculateHeaderHeight(info, newChart, tableHeader, basicStyle, null) }) newChart.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, (ev: LayoutResult) => { - const maxHeight = newChart.store.get('autoCalcHeight') as number - if (maxHeight) { - // 更新列的高度 - ev.colLeafNodes.forEach(n => (n.height = maxHeight)) - ev.colsHierarchy.height = maxHeight - newChart.store.set('autoCalcHeight', undefined) - } else { - if (ev.colLeafNodes?.length) { - const { value, width } = ev.colLeafNodes[0] - calculateHeaderHeight( - { info: { meta: { value }, resizedWidth: width } }, - newChart, - tableHeader, - basicStyle, - ev - ) - } + if (ev.colLeafNodes?.length) { + const { value, width } = ev.colLeafNodes[0] + calculateHeaderHeight( + { info: { meta: { value }, resizedWidth: width } }, + newChart, + tableHeader, + basicStyle, + ev + ) + } + if (tableHeader.headerGroup) { + const groupHeight = ev.colNodes.filter(node => node.colIndex === -1)?.[0]?.height || 0 + ev.colsHierarchy.height = + ev.colsHierarchy.height + ev.colsHierarchy.maxLevel * groupHeight + ev.colLeafNodes.forEach(node => { + if (node.level < ev.colsHierarchy.maxLevel) { + const addHeight = ev.colsHierarchy.maxLevel - node.level + node.height = node.height + addHeight * groupHeight + } + }) } }) } 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 fc85056a20..979e17ef80 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 @@ -59,7 +59,8 @@ import { repeat, sumBy, size, - sum + sum, + isNumber } from 'lodash-es' import { createVNode, render } from 'vue' import TableTooltip from '@/views/chart/components/editor/common/TableTooltip.vue' @@ -69,7 +70,6 @@ import { ElMessage } from 'element-plus-secondary' import { useI18n } from '@/hooks/web/useI18n' import Decimal from 'decimal.js' - const { t: i18nt } = useI18n() export function getCustomTheme(chart: Chart): S2Theme { @@ -1874,10 +1874,15 @@ export async function exportRowQuotaTreePivot(instance: PivotSheet, chart: Chart saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`) } -function extractNumber(formattedValue: string, formatterCfg: BaseFormatter): { - value: number - numFmt: string -} | string { +function extractNumber( + formattedValue: string, + formatterCfg: BaseFormatter +): + | { + value: number + numFmt: string + } + | string { if (!formatterCfg) { return formattedValue } @@ -2255,9 +2260,9 @@ const drawTextShape = (cell, isHeader) => { cell.actualTextWidth = cell.spreadsheet.measureTextWidth(wrapText, textStyle) // 获取文本位置并渲染文本 - const position = cell.getTextPosition() + const { x, y } = cell.getTextAndIconPosition()?.text || cell.getTextPosition() // 绘制文本 - cell.textShape = renderText(cell, [cell.textShape], position.x, position.y, wrapText, textStyle, { + cell.textShape = renderText(cell, [cell.textShape], x, y, wrapText, textStyle, { fontSize: extraStyleFontSize }) @@ -2312,8 +2317,6 @@ export const calculateHeaderHeight = (info, newChart, tableHeader, basicStyle, l ev.colLeafNodes.forEach(n => (n.height = maxHeight)) ev.colsHierarchy.height = maxHeight } - - newChart.store.set('autoCalcHeight', maxHeight) } /** @@ -2450,7 +2453,10 @@ export function getSummaryRow(data, axis, sumCon = []) { }) // 计算方差(平方差的平均值) const variance = squaredDeviations.reduce((acc, val) => acc.plus(val), new Decimal(0)) - summaryObj[a] = variance.dividedBy(data.length - 1).sqrt().toNumber() // 计算总体标准差 + summaryObj[a] = variance + .dividedBy(data.length - 1) + .sqrt() + .toNumber() // 计算总体标准差 } break } @@ -2460,7 +2466,6 @@ export function getSummaryRow(data, axis, sumCon = []) { return summaryObj } - export class SummaryCell extends CustomDataCell { getTextStyle() { const textStyle = cloneDeep(this.theme.colCell.bolderText) @@ -2568,7 +2573,6 @@ export function drawImage() { } } - export function calcTreeWidth(node) { if (!node.children?.length) { return node.width @@ -2615,3 +2619,122 @@ export function summaryRowStyle(newChart, newData, tableCell, tableHeader, showS } }) } + +/** + * 计算分组表头高度 + * @param newChart + * @param tableHeader + * @param basicStyle + */ +export const calculateGroupHeaderHeight = (newChart, tableHeader, basicStyle) => { + let maxGroupHeight = 0 + // 获取分组名字最长的列 + const maxNameMeta = newChart.dataCfg?.meta + ?.filter(item => !item.field.startsWith('f_')) + ?.reduce((max, cur) => (cur.name.length > (max?.name.length ?? 0) ? cur : max), null) + if (maxNameMeta) { + let colWidth = basicStyle.tableColumnWidth + const maxNameColumn = findNodeByKey(newChart.dataCfg.fields.columns, maxNameMeta.field) + const maxNameColumns = [] + if (maxNameColumn) { + maxNameColumns.push(maxNameColumn) + } + const { resizedWidth, meta } = newChart.store.get('resizeColWidthInfo') || { + resizedWidth: 0, + width: 0 + } + const leafKeys = getLeafKeys(maxNameColumns) + if (basicStyle.tableFieldWidth.length > 0) { + colWidth = 0 + const fieldWidth = basicStyle.tableFieldWidth + // fieldWidth中对象的key在leafKeys时,对fieldWidth中对象的width求和 + fieldWidth.forEach(fw => { + // 调整单元格宽度时,排除掉当前调整的列,使用调整后的宽度 + if ( + leafKeys.filter(key => meta?.key !== key).includes(fw.fieldId) && + isNumber(fw.width) && + fw.width > 0 + ) { + colWidth += fw.width + } + }) + } + if (basicStyle.tableColumnMode === 'custom') { + colWidth = basicStyle.tableColumnWidth * (leafKeys.length === 0 ? 1 : leafKeys.length) || 100 + } else { + colWidth = + (newChart.facet?.cfg?.width ? newChart.facet.cfg.width : newChart.options.width) * + (colWidth / 100) + } + // 计算分组表头的高度 + if (colWidth > 0) { + colWidth = colWidth + resizedWidth + const nodeHeight = calculateGroupHeaderMaxTextHeight( + { info: { name: maxNameMeta.name, resizedWidth: colWidth } }, + newChart, + tableHeader, + basicStyle, + null + ) + maxGroupHeight = Math.max(maxGroupHeight, nodeHeight) + } + if (maxGroupHeight > 0) { + newChart.options.style.colCfg.height = maxGroupHeight + } + } +} + +// 获取最里层的叶子节点 +const getLeafKeys = (columns: any[]): string[] => { + const keys: string[] = [] + columns.forEach(col => { + if (col && typeof col === 'object' && Array.isArray(col.children) && col.children.length > 0) { + keys.push(...getLeafKeys(col.children)) + } else if (col && typeof col === 'object' && col.key) { + keys.push(col.key) + } + }) + return keys +} +// 根据 key 查找节点 +const findNodeByKey = (columns: any[], key: string): any | null => { + for (const col of columns) { + if (col.key === key) return col + if (col.children) { + const found = findNodeByKey(col.children, key) + if (found) return found + } + } + return null +} + +/** + * 计算分组表头最大文本高度 + * @param info + * @param newChart + * @param tableHeader + * @param basicStyle + * @param _layoutResult + */ +const calculateGroupHeaderMaxTextHeight = ( + info, + newChart, + tableHeader, + basicStyle, + _layoutResult +) => { + if (tableHeader.showTableHeader === false) return + const maxLines = basicStyle.maxLines ?? 1 + const textStyle = { ...newChart.theme.cornerCell.text, fontSize: tableHeader.tableTitleFontSize } + const sourceText = info.info.name + return ( + getWrapTextHeight( + getWrapText(sourceText, textStyle, info.info.resizedWidth, newChart), + textStyle, + newChart, + maxLines + ) + + textStyle.fontSize + + 10.5 + ) +}