fix(图表): 修复明细表和汇总表开启总计之后序号列计算错误以及图片不显示 (#15454)

This commit is contained in:
wisonic-s
2025-03-21 18:34:38 +08:00
committed by GitHub
parent efbc337e89
commit 46a5c546de
4 changed files with 163 additions and 130 deletions

View File

@@ -209,3 +209,9 @@ declare interface Filter {
datasetTableField: ChartViewField
fieldId: string
}
declare interface PageInfo {
currentPage: number
pageSize: number
total: number
}

View File

@@ -1,6 +1,5 @@
import {
type LayoutResult,
MergedCell,
S2DataConfig,
S2Event,
S2Options,
@@ -15,7 +14,7 @@ import { hexColorToRGBA, isAlphaColor, parseJson } from '../../../util'
import { S2ChartView, S2DrawOptions } from '../../types/impl/s2'
import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common'
import { useI18n } from '@/hooks/web/useI18n'
import { isEqual, isNumber, merge } from 'lodash-es'
import { filter, isEqual, isNumber, merge } from 'lodash-es'
import {
copyContent,
CustomDataCell,
@@ -23,12 +22,13 @@ import {
getRowIndex,
calculateHeaderHeight,
SortTooltip,
configSummaryRow,
summaryRowStyle,
configEmptyDataStyle,
getLeafNodes,
getColumns,
drawImage
drawImage,
getSummaryRow,
SummaryCell
} from '@/views/chart/components/js/panel/common/common_table'
const { t } = useI18n()
@@ -197,37 +197,6 @@ export class TableInfo extends S2ChartView<TableSheet> {
s2Options.frozenColCount = tableCell.tableColumnFreezeHead ?? 0
s2Options.frozenRowCount = tableCell.tableRowFreezeHead ?? 0
}
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
let indexLabel = tableHeader.indexLabel
if (!indexLabel) {
indexLabel = ''
}
s2Options.layoutCoordinate = (_, __, col) => {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel
col.value = indexLabel
}
}
}
s2Options.dataCell = viewMeta => {
const field = fields.filter(f => f.dataeaseName === viewMeta.valueField)?.[0]
if (field?.deType === 7 && chart.showPosition !== 'dialog') {
return new ImageCell(viewMeta, viewMeta?.spreadsheet)
}
if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
if (tableCell.mergeCells) {
viewMeta.fieldValue = getRowIndex(s2Options.mergedCellsInfo, viewMeta)
} else {
viewMeta.fieldValue =
pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1
}
}
// 配置文本自动换行参数
viewMeta.autoWrap = tableCell.mergeCells ? false : basicStyle.autoWrap
viewMeta.maxLines = basicStyle.maxLines
return new CustomDataCell(viewMeta, viewMeta?.spreadsheet)
}
// tooltip
this.configTooltip(chart, s2Options)
// 合并单元格
@@ -256,8 +225,8 @@ export class TableInfo extends S2ChartView<TableSheet> {
return new CustomTableColCell(node, sheet, config)
}
}
// 总计
configSummaryRow(chart, s2Options, newData, tableHeader, basicStyle, basicStyle.showSummary)
// 序列号和总计
this.configSummaryRowAndIndex(chart, pageInfo, s2Options, s2DataConfig)
// 开始渲染
const newChart = new TableSheet(containerDom, s2DataConfig, s2Options)
// 总计紧贴在单元格后面
@@ -475,6 +444,75 @@ export class TableInfo extends S2ChartView<TableSheet> {
return theme
}
protected configSummaryRowAndIndex(
chart: Chart,
pageInfo: PageInfo,
s2Options: S2Options,
s2DataConfig: S2DataConfig
) {
const { tableHeader, basicStyle, tableCell } = parseJson(chart.customAttr)
const fields = chart.data?.fields ?? []
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
let indexLabel = tableHeader.indexLabel
if (!indexLabel) {
indexLabel = ''
}
s2Options.layoutCoordinate = (_, __, col) => {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel
col.value = indexLabel
}
}
}
const { showSummary, summaryLabel } = basicStyle
const data = s2DataConfig.data
const xAxis = chart.xAxis
if (showSummary && data?.length) {
// 设置汇总行高度和表头一致
const heightByField = {}
heightByField[data.length] = tableHeader.tableTitleHeight
s2Options.style.rowCfg = { heightByField }
// 计算汇总加入到数据里,冻结最后一行
s2Options.frozenTrailingRowCount = 1
const axis = filter(xAxis, axis => [2, 3, 4].includes(axis.deType))
const summaryObj = getSummaryRow(data, axis, basicStyle.seriesSummary) as any
data.push(summaryObj)
}
s2Options.dataCell = viewMeta => {
// 总计行处理
if (showSummary && viewMeta.rowIndex === data.length - 1) {
if (viewMeta.colIndex === 0) {
if (tableHeader.showIndex) {
viewMeta.fieldValue = summaryLabel ?? t('chart.total_show')
} else {
// 第一列不是数值类型的,显示总计
if (![2, 3, 4].includes(xAxis?.[0]?.deType)) {
viewMeta.fieldValue = summaryLabel ?? t('chart.total_show')
}
}
}
return new SummaryCell(viewMeta, viewMeta?.spreadsheet)
}
const field = fields.find(f => f.dataeaseName === viewMeta.valueField)
if (field?.deType === 7 && chart.showPosition !== 'dialog') {
return new ImageCell(viewMeta, viewMeta?.spreadsheet)
}
if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
if (tableCell.mergeCells) {
viewMeta.fieldValue = getRowIndex(s2Options.mergedCellsInfo, viewMeta)
} else {
viewMeta.fieldValue =
pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1
}
}
// 配置文本自动换行参数
viewMeta.autoWrap = tableCell.mergeCells ? false : basicStyle.autoWrap
viewMeta.maxLines = basicStyle.maxLines
return new CustomDataCell(viewMeta, viewMeta?.spreadsheet)
}
}
constructor() {
super('table-info', [])
}

View File

@@ -2,9 +2,11 @@ import { useI18n } from '@/hooks/web/useI18n'
import { formatterItem, valueFormatter } from '@/views/chart/components/js/formatter'
import {
configEmptyDataStyle,
configSummaryRow,
copyContent,
CustomDataCell,
getSummaryRow,
SortTooltip,
SummaryCell,
summaryRowStyle
} from '@/views/chart/components/js/panel/common/common_table'
import { S2ChartView, S2DrawOptions } from '@/views/chart/components/js/panel/types/impl/s2'
@@ -16,11 +18,10 @@ import {
S2Options,
ScrollbarPositionType,
TableColCell,
TableDataCell,
TableSheet,
ViewMeta
} from '@antv/s2'
import { cloneDeep, isNumber } from 'lodash-es'
import { isNumber } from 'lodash-es'
import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common'
const { t } = useI18n()
@@ -161,26 +162,6 @@ export class TableNormal extends S2ChartView<TableSheet> {
s2Options.frozenColCount = tableCell.tableColumnFreezeHead ?? 0
s2Options.frozenRowCount = tableCell.tableRowFreezeHead ?? 0
}
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
let indexLabel = tableHeader.indexLabel
if (!indexLabel) {
indexLabel = ''
}
s2Options.layoutCoordinate = (_, __, col) => {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel
col.value = indexLabel
}
}
s2Options.dataCell = viewMeta => {
if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
viewMeta.fieldValue =
pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1
}
return new TableDataCell(viewMeta, viewMeta.spreadsheet)
}
}
// tooltip
this.configTooltip(chart, s2Options)
// 隐藏表头,保留顶部的分割线, 禁用表头横向 resize
@@ -201,9 +182,8 @@ export class TableNormal extends S2ChartView<TableSheet> {
chart.container = container
this.configHeaderInteraction(chart, s2Options)
}
// 总计
configSummaryRow(chart, s2Options, newData, tableHeader, basicStyle, basicStyle.showSummary)
// 配置总计和序号列
this.configSummaryRowAndIndex(chart, pageInfo, s2Options, s2DataConfig)
// 开始渲染
const newChart = new TableSheet(containerDom, s2DataConfig, s2Options)
// 总计紧贴在单元格后面
@@ -302,6 +282,57 @@ export class TableNormal extends S2ChartView<TableSheet> {
return newChart
}
protected configSummaryRowAndIndex(
chart: Chart,
pageInfo: PageInfo,
s2Options: S2Options,
s2DataConfig: S2DataConfig
) {
const { tableHeader, basicStyle } = parseJson(chart.customAttr)
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
let indexLabel = tableHeader.indexLabel
if (!indexLabel) {
indexLabel = ''
}
s2Options.layoutCoordinate = (_, __, col) => {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel
col.value = indexLabel
}
}
}
const { showSummary, summaryLabel } = basicStyle
const data = s2DataConfig.data
const { xAxis, yAxis } = chart
if (showSummary && data?.length) {
// 设置汇总行高度和表头一致
const heightByField = {}
heightByField[data.length] = tableHeader.tableTitleHeight
s2Options.style.rowCfg = { heightByField }
// 计算汇总加入到数据里,冻结最后一行
s2Options.frozenTrailingRowCount = 1
const summaryObj = getSummaryRow(data, yAxis, basicStyle.seriesSummary) as any
data.push(summaryObj)
}
s2Options.dataCell = viewMeta => {
// 总计行处理
if (showSummary && viewMeta.rowIndex === data.length - 1) {
if (viewMeta.colIndex === 0) {
if (tableHeader.showIndex || xAxis?.length) {
viewMeta.fieldValue = summaryLabel ?? t('chart.total_show')
}
}
return new SummaryCell(viewMeta, viewMeta?.spreadsheet)
}
if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
viewMeta.fieldValue = pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1
}
return new CustomDataCell(viewMeta, viewMeta?.spreadsheet)
}
}
constructor() {
super('table-normal', [])
}

View File

@@ -1872,40 +1872,11 @@ const getWrapTextHeight = (wrapText, textStyle, spreadsheet, maxLines) => {
return Math.min(lines, maxLines) * maxHeight
}
/**
* 设置汇总行
* @param chart
* @param s2Options
* @param newData
* @param tableHeader
* @param basicStyle
* @param showSummary
*/
export const configSummaryRow = (
chart,
s2Options,
newData,
tableHeader,
basicStyle,
showSummary
) => {
if (!showSummary || !newData.length) return
// 设置汇总行高度和表头一致
const heightByField = {}
heightByField[newData.length] = tableHeader.tableTitleHeight
s2Options.style.rowCfg = {heightByField}
// 计算汇总加入到数据里,冻结最后一行
s2Options.frozenTrailingRowCount = 1
const xAxis = chart.xAxis
const axis = chart.type === 'table-info'
? filter(chart.xAxis, axis => [2, 3, 4].includes(axis.deType))
: chart.yAxis
const summaryObj = {SUMMARY: true}
export function getSummaryRow(data, axis, sumCon = []) {
const summaryObj = { SUMMARY: true }
for (let i = 0; i < axis.length; i++) {
const a = axis[i].dataeaseName
let savedAxis = find(basicStyle.seriesSummary, s => s.field === a)
let savedAxis = find(sumCon, s => s.field === a)
if (savedAxis) {
if (savedAxis.summary == undefined) {
savedAxis.summary = 'sum'
@@ -1925,58 +1896,45 @@ export const configSummaryRow = (
}
switch (savedAxis.summary) {
case 'sum':
summaryObj[a] = sumBy(newData, d => (parseFloat(d[a]) || 0))
summaryObj[a] = sumBy(data, d => parseFloat(d[a]) || 0)
break
case 'avg':
summaryObj[a] = meanBy(newData, d => (parseFloat(d[a]) || 0))
summaryObj[a] = meanBy(data, d => parseFloat(d[a]) || 0)
break
case 'max':
summaryObj[a] = maxBy(filter(newData, d => parseFloat(d[a]) !== undefined), d => parseFloat(d[a]))[a]
summaryObj[a] = maxBy(
filter(data, d => parseFloat(d[a]) !== undefined),
d => parseFloat(d[a])
)[a]
break
case 'min':
summaryObj[a] = minBy(filter(newData, d => parseFloat(d[a]) !== undefined), d => parseFloat(d[a]))[a]
summaryObj[a] = minBy(
filter(data, d => parseFloat(d[a]) !== undefined),
d => parseFloat(d[a])
)[a]
break
case 'var_pop'://方差
if (newData.length < 2) {
case 'var_pop': //方差
if (data.length < 2) {
continue
} else {
const mean = meanBy(newData, d => (parseFloat(d[a]) || 0)) // 计算均值
const squaredDeviations = map(newData, d => ((parseFloat(d[a]) || 0) - mean) ** 2); // 计算偏差平方
summaryObj[a] = sum(squaredDeviations) / (size(newData) - 1); // 样本方差分母n-1
const mean = meanBy(data, d => parseFloat(d[a]) || 0) // 计算均值
const squaredDeviations = map(data, d => ((parseFloat(d[a]) || 0) - mean) ** 2) // 计算偏差平方
summaryObj[a] = sum(squaredDeviations) / (size(data) - 1) // 样本方差分母n-1
}
break
case 'stddev_pop'://标准差
if (newData.length < 2) {
case 'stddev_pop': //标准差
if (data.length < 2) {
continue
} else {
const mean = meanBy(newData, d => (parseFloat(d[a]) || 0)) // 计算均值
const squaredDeviations = map(newData, d => ((parseFloat(d[a]) || 0) - mean) ** 2); // 计算偏差平方
const sampleVariance = sum(squaredDeviations) / (size(newData) - 1); // 样本方差分母n-1
summaryObj[a] = Math.sqrt(sampleVariance); // 样本标准差
const mean = meanBy(data, d => parseFloat(d[a]) || 0) // 计算均值
const squaredDeviations = map(data, d => ((parseFloat(d[a]) || 0) - mean) ** 2) // 计算偏差平方
const sampleVariance = sum(squaredDeviations) / (size(data) - 1) // 样本方差分母n-1
summaryObj[a] = Math.sqrt(sampleVariance) // 样本标准差
}
break
}
}
newData.push(summaryObj)
s2Options.dataCell = viewMeta => {
// 配置文本自动换行参数
viewMeta.autoWrap = basicStyle.autoWrap
viewMeta.maxLines = basicStyle.maxLines
if (viewMeta.rowIndex !== newData.length - 1) {
return new CustomDataCell(viewMeta, viewMeta.spreadsheet)
}
if (viewMeta.colIndex === 0) {
if (tableHeader.showIndex) {
viewMeta.fieldValue = basicStyle.summaryLabel ?? i18nt('chart.total_show')
} else {
if (xAxis.length) {
viewMeta.fieldValue = basicStyle.summaryLabel ?? i18nt('chart.total_show')
}
}
}
return new SummaryCell(viewMeta, viewMeta.spreadsheet)
}
return summaryObj
}
/**