From 74aa20c14f378e224421e96ef977a78f62c47e82 Mon Sep 17 00:00:00 2001 From: wisonic Date: Fri, 21 Feb 2025 18:42:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=9B=BE=E8=A1=A8):=20=E6=98=8E=E7=BB=86?= =?UTF-8?q?=E8=A1=A8=E6=94=AF=E6=8C=81=E5=AD=97=E6=AE=B5=E5=88=86=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/core-frontend/src/locales/en.ts | 10 + core/core-frontend/src/locales/tw.ts | 9 + core/core-frontend/src/locales/zh-CN.ts | 9 + .../src/models/chart/chart-attr.d.ts | 33 ++ .../table/TableHeaderGroupConfig.vue | 522 ++++++++++++++++++ .../components/table/TableHeaderSelector.vue | 97 +++- .../chart/components/editor/util/chart.ts | 12 +- .../js/panel/charts/table/table-info.ts | 68 ++- .../js/panel/common/common_table.ts | 32 ++ 9 files changed, 783 insertions(+), 9 deletions(-) create mode 100644 core/core-frontend/src/views/chart/components/editor/editor-style/components/table/TableHeaderGroupConfig.vue diff --git a/core/core-frontend/src/locales/en.ts b/core/core-frontend/src/locales/en.ts index 23d988f050..2d2520d1ac 100644 --- a/core/core-frontend/src/locales/en.ts +++ b/core/core-frontend/src/locales/en.ts @@ -1929,6 +1929,16 @@ Scatter chart (bubble) chart: {a} (series name), {b} (data name), {c} (value arr central_point: 'Center point', full_display: 'Full display', show_hover_style: 'Show mouse hover style', + table_header_group: 'Header grouping', + table_header_group_config: 'Header grouping config', + cancel_group: 'Cancel grouping', + cancel_all_group: 'Cancel all grouping', + group_name: 'Group name', + merge_group: 'Merge group', + table_header_group_config_tip: + 'Field additions, deletions, positional changes, and explicit and implicit modifications can cause grouping to become invalid.', + group_name_edit_tip: 'Group names are 1-20 characters in length', + group_name_error_tip: 'Please input valid group name', merge_cells: 'Merge cells', length_limit: 'Length limit', radar_point: 'Enable auxiliary points', diff --git a/core/core-frontend/src/locales/tw.ts b/core/core-frontend/src/locales/tw.ts index 6334b1aaf9..d3dcbb52f2 100644 --- a/core/core-frontend/src/locales/tw.ts +++ b/core/core-frontend/src/locales/tw.ts @@ -1884,6 +1884,15 @@ export default { central_point: '中心點', full_display: '全量顯示', show_hover_style: '顯示滑鼠懸浮樣式', + table_header_group: '表頭分組', + table_header_group_config: '表頭分組設置', + cancel_group: '取消分組', + cancel_all_group: '取消所有分組', + group_name: '分組名稱', + merge_group: '合併分組', + table_header_group_config_tip: '字段的增刪,位置變動,顯隱修改都會導致分組失效', + group_name_edit_tip: '分組名稱長度為 1-20 個字符', + group_name_error_tip: '請輸入正確的分組名稱', merge_cells: '合併儲存格', length_limit: '長度限制', radar_point: '開啟輔助點', diff --git a/core/core-frontend/src/locales/zh-CN.ts b/core/core-frontend/src/locales/zh-CN.ts index 236362c413..0bb6214583 100644 --- a/core/core-frontend/src/locales/zh-CN.ts +++ b/core/core-frontend/src/locales/zh-CN.ts @@ -1894,6 +1894,15 @@ export default { central_point: '中心点', full_display: '全量显示', show_hover_style: '显示鼠标悬浮样式', + table_header_group: '表头分组', + table_header_group_config: '表头分组设置', + cancel_group: '取消分组', + cancel_all_group: '取消所有分组', + group_name: '分组名称', + merge_group: '合并分组', + table_header_group_config_tip: '字段的增删,位置变动,显隐修改都会导致分组失效', + group_name_edit_tip: '分组名称长度为 1-20 个字符', + group_name_error_tip: '请输入正确的分组名称', merge_cells: '合并单元格', length_limit: '长度限制', radar_point: '开启辅助点', diff --git a/core/core-frontend/src/models/chart/chart-attr.d.ts b/core/core-frontend/src/models/chart/chart-attr.d.ts index 064cbeebed..2c916ef832 100644 --- a/core/core-frontend/src/models/chart/chart-attr.d.ts +++ b/core/core-frontend/src/models/chart/chart-attr.d.ts @@ -441,6 +441,32 @@ declare interface ChartTableHeaderAttr { isBolder: boolean isCornerBolder: boolean isColBolder: boolean + /** + * 表头分组开关 + */ + headerGroup: boolean + /** + * 表头分组设置 + */ + headerGroupConfig: { + /** + * 分组结构 + */ + columns: Columns + /** + * 分组名称 + */ + meta: { + /** + * 字段id + */ + field: string + /** + * 名称 + */ + name: string + }[] + } } /** * 单元格属性 @@ -1276,3 +1302,10 @@ declare interface ConversionTagAtt { */ precision: number } + +declare interface ColumnNode { + key: string + children?: Columns +} + +declare type Columns = Array diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/components/table/TableHeaderGroupConfig.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/components/table/TableHeaderGroupConfig.vue new file mode 100644 index 0000000000..b7b4d32fae --- /dev/null +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/components/table/TableHeaderGroupConfig.vue @@ -0,0 +1,522 @@ + + + + + diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/components/table/TableHeaderSelector.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/components/table/TableHeaderSelector.vue index a411edc4da..ebc485e28b 100644 --- a/core/core-frontend/src/views/chart/components/editor/editor-style/components/table/TableHeaderSelector.vue +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/components/table/TableHeaderSelector.vue @@ -4,6 +4,7 @@ import icon_italic_outlined from '@/assets/svg/icon_italic_outlined.svg' import icon_leftAlignment_outlined from '@/assets/svg/icon_left-alignment_outlined.svg' import icon_centerAlignment_outlined from '@/assets/svg/icon_center-alignment_outlined.svg' import icon_rightAlignment_outlined from '@/assets/svg/icon_right-alignment_outlined.svg' +import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg' import { computed, onMounted, PropType, reactive, watch } from 'vue' import { useI18n } from '@/hooks/web/useI18n' import { COLOR_PANEL, DEFAULT_TABLE_HEADER } from '@/views/chart/components/editor/util/chart' @@ -12,6 +13,8 @@ import { cloneDeep, defaultsDeep } from 'lodash-es' import { convertToAlphaColor, isAlphaColor } from '@/views/chart/components/js/util' import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain' import { storeToRefs } from 'pinia' +import TableHeaderGroupConfig from './TableHeaderGroupConfig.vue' + const dvMainStore = dvMainStoreWithOut() const { mobileInPc } = storeToRefs(dvMainStore) const { t } = useI18n() @@ -58,7 +61,8 @@ const fontSizeList = computed(() => { }) const state = reactive({ - tableHeaderForm: {} as ChartTableHeaderAttr + tableHeaderForm: {} as ChartTableHeaderAttr, + showTableHeaderGroupConfig: false }) const emit = defineEmits(['onTableHeaderChange']) @@ -67,6 +71,13 @@ const changeTableHeader = prop => { emit('onTableHeaderChange', state.tableHeaderForm, prop) } +const changeHeaderGroupConfig = (headerGroupConfig: ChartTableHeaderAttr['headerGroupConfig']) => { + state.tableHeaderForm.headerGroupConfig = headerGroupConfig + state.showTableHeaderGroupConfig = false + log(headerGroupConfig) + changeTableHeader('headerGroupConfig') +} + const init = () => { const tableHeader = props.chart?.customAttr?.tableHeader if (tableHeader) { @@ -723,7 +734,67 @@ onMounted(() => { {{ t('chart.table_header_show_vertical_border') }} + + + {{ t('chart.table_header_group') }} + + + +
+ {{ t('chart.table_header_group_config') }} +
+ + {{ t('visualization.already_setting') }} + +
+ + + + + +
+
+
+
+ + + + + diff --git a/core/core-frontend/src/views/chart/components/editor/util/chart.ts b/core/core-frontend/src/views/chart/components/editor/util/chart.ts index e15f7a5a33..a850982f13 100644 --- a/core/core-frontend/src/views/chart/components/editor/util/chart.ts +++ b/core/core-frontend/src/views/chart/components/editor/util/chart.ts @@ -448,7 +448,12 @@ export const DEFAULT_TABLE_HEADER: ChartTableHeaderAttr = { isColItalic: false, isBolder: true, isCornerBolder: true, - isColBolder: true + isColBolder: true, + headerGroup: false, + headerGroupConfig: { + columns: [], + meta: [] + } } export const DEFAULT_TABLE_CELL: ChartTableCellAttr = { tableFontColor: '#000000', @@ -1671,7 +1676,10 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = { maxLines: 3, radarShowPoint: true, radarPointSize: 4, - radarAreaColor: true + radarAreaColor: true, + circleBorderColor: '#fff', + circleBorderWidth: 0, + circlePadding: 0 } export const BASE_VIEW_CONFIG = { 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 27c175c412..f0bd214dab 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 @@ -14,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 { isNumber, merge } from 'lodash-es' +import { isEqual, isNumber, merge } from 'lodash-es' import { copyContent, CustomDataCell, @@ -24,7 +24,10 @@ import { SortTooltip, configSummaryRow, summaryRowStyle, - configEmptyDataStyle + configEmptyDataStyle, + getValidLeafNodes, + getLeafNodes, + getColumns } from '@/views/chart/components/js/panel/common/common_table' const { t } = useI18n() @@ -63,7 +66,8 @@ export class TableInfo extends S2ChartView { 'table-header-selector': [ ...TABLE_EDITOR_PROPERTY_INNER['table-header-selector'], 'tableHeaderSort', - 'showTableHeader' + 'showTableHeader', + 'headerGroup' ], 'basic-style-selector': [ 'tableColumnMode', @@ -103,6 +107,7 @@ export class TableInfo extends S2ChartView { pre[cur.dataeaseName] = cur return pre }, {}) + const drillFieldMap = {} if (chart.drill) { // 下钻过滤字段 const filterFields = chart.drillFilters.map(i => i.fieldId) @@ -111,13 +116,14 @@ export class TableInfo extends S2ChartView { const drillFieldIndex = chart.xAxis.findIndex(ele => ele.id === drillFieldId) // 当前下钻字段 const curDrillFieldId = chart.drillFields[filterFields.length].id - const curDrillField = fields.filter(ele => ele.id === curDrillFieldId) + const curDrillField = fields.find(ele => ele.id === curDrillFieldId) filterFields.push(curDrillFieldId) // 移除下钻字段,把当前下钻字段插入到下钻入口位置 fields = fields.filter(ele => { return !filterFields.includes(ele.id) }) - fields.splice(drillFieldIndex, 0, ...curDrillField) + drillFieldMap[curDrillField.dataeaseName] = chart.drillFields[0].dataeaseName + fields.splice(drillFieldIndex, 0, curDrillField) } fields.forEach(ele => { const f = axisMap[ele.dataeaseName] @@ -146,6 +152,27 @@ export class TableInfo extends S2ChartView { } }) }) + const { basicStyle, tableCell, tableHeader, tooltip } = parseJson(chart.customAttr) + // 表头分组 + const { headerGroup } = tableHeader + if (headerGroup) { + const { headerGroupConfig } = tableHeader + if (headerGroupConfig?.columns?.length) { + const allKeys = columns.map(c => drillFieldMap[c] || c) + const leafNodes = getLeafNodes(headerGroupConfig.columns as ColumnNode[]) + const leafKeys = leafNodes.map(c => c.key) + if (isEqual(leafKeys, allKeys)) { + if (Object.keys(drillFieldMap).length) { + const originField = Object.values(drillFieldMap)[0] + const drillField = Object.keys(drillFieldMap)[0] + const [drillCol] = getColumns([originField], headerGroupConfig.columns as ColumnNode[]) + drillCol.key = drillField + } + columns.splice(0, columns.length, ...headerGroupConfig.columns) + meta.push(...headerGroupConfig.meta) + } + } + } // 空值处理 const newData = this.configEmptyDataStrategy(chart) // data config @@ -157,7 +184,6 @@ export class TableInfo extends S2ChartView { data: newData } - const { basicStyle, tableCell, tableHeader, tooltip } = parseJson(chart.customAttr) // options const s2Options: S2Options = { width: containerDom.getBoundingClientRect().width, @@ -302,6 +328,13 @@ export class TableInfo extends S2ChartView { n.x = p return p + n.width }, 0) + // 处理分组的单元格,宽度为所有叶子节点之和 + ev.colNodes.forEach(n => { + if (n.colIndex === -1) { + n.width = calcTreeWidth(n) + n.x = getStartPosition(n) + } + }) ev.colsHierarchy.width = totalWidth newChart.store.set('lastLayoutResult', undefined) return @@ -334,6 +367,13 @@ export class TableInfo extends S2ChartView { n.x = p return p + n.width }, 0) + // 处理分组的单元格,宽度为所有叶子节点之和 + ev.colNodes.forEach(n => { + if (n.colIndex === -1) { + n.width = calcTreeWidth(n) + n.x = getStartPosition(n) + } + }) if (totalWidth > containerWidth) { ev.colLeafNodes[ev.colLeafNodes.length - 1].width -= totalWidth - containerWidth } @@ -451,3 +491,19 @@ export class TableInfo extends S2ChartView { super('table-info', []) } } + +function calcTreeWidth(node) { + if (!node.children?.length) { + return node.width + } + return node.children.reduce((pre, cur) => { + return pre + calcTreeWidth(cur) + }, 0) +} + +function getStartPosition(node) { + if (!node.children?.length) { + return node.x + } + return getStartPosition(node.children[0]) +} 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 782cc07026..72e30745da 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 @@ -1963,3 +1963,35 @@ export const configEmptyDataStyle = (newChart, basicStyle, newData, container) = } }) } + +export const getLeafNodes = (tree: Array): ColumnNode[] => { + const result: ColumnNode[] = [] + const inorderTraversal = node => { + if (!node.children?.length) { + // 叶子节点,添加到结果数组 + result.push(node) + return + } + // 中序遍历 + for (let i = 0; i < node.children?.length; i++) { + inorderTraversal(node.children[i]) + } + } + + // 遍历树中所有节点 + tree.forEach(node => inorderTraversal(node)) + return result +} + +export const getColumns = (fields, cols: Array) => { + const result = [] + for (let i = 0; i < cols.length; i++) { + if (fields.includes(cols[i].key)) { + result.push(cols[i]) + } + if (cols[i].children?.length) { + result.push(...getColumns(fields, cols[i].children as Array)) + } + } + return result +}