views\chart\components\js\panel

This commit is contained in:
limengnan 2025-07-01 16:21:11 +08:00
parent 9a8a763cf2
commit 40da16013a
51 changed files with 12630 additions and 666 deletions

View File

@ -1,45 +1,98 @@
<script lang="tsx" setup>
import dvUpArrow from '@/assets/svg/dv-up-arrow.svg'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
const { t } = useI18n()
defineProps({
themes: {
type: String,
default: 'light'
}
})
</script>
<template>
<div class="view-panel-Mask">
<Icon class-name="item-icon" name="dv-up-arrow"><dvUpArrow class="svg-icon item-icon" /></Icon>
<div>
<el-button style="opacity: 1 !important" type="warning" size="mini" round>
<span style="font-weight: bold; opacity: 1">{{
t('visualization.template_view_tips')
}}</span>
</el-button>
<div class="view-panel-mask-left"></div>
<div class="view-panel-mask">
<el-popover
:visible="true"
placement="bottom"
popper-class="template-popper-tips"
:width="256"
show-arrow
>
<div class="template-popper-tips-content">
<p class="constant">{{ t('visualization.template_view_tips') }}</p>
</div>
<template #reference>
<div
class="view-panel-mask-inner"
:class="{ 'view-panel-mask-inner-dark': themes === 'dark' }"
></div>
</template>
</el-popover>
</div>
</template>
<style lang="less" scoped>
.view-panel-Mask {
display: flex;
height: calc(100vh - 148px);
width: 100%;
background-color: rgba(92, 94, 97, 0.7);
.view-panel-mask-left {
height: 100%;
width: 240px;
position: absolute;
top: 85px;
left: 0px;
top: 0;
left: 0;
cursor: not-allowed;
background-color: rgba(31, 35, 41);
opacity: 0.4;
z-index: 2;
}
.view-panel-mask {
height: 100%;
width: 178px;
background-color: rgba(31, 35, 41);
opacity: 0.4;
position: absolute;
top: 0;
left: 240px;
z-index: 2;
cursor: not-allowed;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.view-panel-mask-inner {
top: 51px;
left: 6px;
height: 34px;
width: 170px;
background: white;
position: relative;
pointer-events: none;
border-radius: 5px;
}
.item-icon {
position: absolute;
top: 10px;
left: 300px;
width: 40px;
height: 40px;
opacity: 1;
color: #ff8800;
.view-panel-mask-inner-dark {
background: rgba(31, 35, 41);
}
</style>
<style lang="less">
.template-popper-tips {
z-index: 1000 !important;
padding: 24px !important;
box-shadow: none !important;
border: 0 !important;
background: var(--ed-color-primary) !important;
inset: 0 auto auto -5px !important;
.ed-popper__arrow::before {
border: 1px solid var(--ed-color-primary) !important;
background: var(--ed-color-primary) !important;
}
}
.template-popper-tips-content {
color: rgba(255, 255, 255, 1);
.content {
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-weight: 500;
line-height: 22px;
text-align: left;
}
}
</style>

View File

@ -0,0 +1,76 @@
<script lang="ts" setup>
import dvAi from '@/assets/svg/dv-ai.svg'
import { ref } from 'vue'
const visible = ref(true)
</script>
<template>
<el-popover
:visible="visible"
placement="bottom"
popper-class="ai-popper-tips"
:width="288"
show-arrow
>
<div class="ai-popper-tips-content">
<p class="constant">
你好我是 DataEase 智能客服<br />点击一下开启高效解答模式~<br />&nbsp;
</p>
</div>
<template #reference>
<div class="ai-popper-tips-icon">
<el-icon style="margin: 2px" class="ai-icon">
<Icon name="dv-ai"><dvAi class="svg-icon" /></Icon>
</el-icon>
</div>
</template>
</el-popover>
</template>
<style lang="less">
.ai-popper-tips {
z-index: 10001 !important;
padding: 24px !important;
box-shadow: none !important;
border: 0px !important;
background: var(--ed-color-primary) !important;
.ed-popper__arrow::before {
border: 1px solid var(--ed-color-primary) !important;
background: var(--ed-color-primary) !important;
}
}
.ai-popper-tips-content {
color: rgba(255, 255, 255, 1);
.title {
font-family: var(--de-custom_font, 'PingFang');
font-size: 20px;
font-weight: 500;
line-height: 28px;
}
.content {
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-weight: 500;
line-height: 22px;
text-align: left;
}
.bottom {
line-height: 22px;
text-align: right;
button {
border: 0px !important;
border-color: #ffffff !important;
font-weight: 500;
color: rgba(51, 112, 255, 1) !important;
}
}
}
.ai-popper-tips-icon {
margin: 0 8px;
z-index: 10003;
border-radius: 50%;
background: #ffffff;
width: 28px;
height: 28px;
}
</style>

View File

@ -318,6 +318,32 @@ export const DEFAULT_MISC: ChartMiscAttr = {
min: 0,
max: 0,
fieldId: undefined
},
bullet: {
bar: {
ranges: {
fill: ['rgba(0,128,255,0.3)'],
size: 20,
showType: 'dynamic',
fixedRangeNumber: 3,
symbol: 'circle',
symbolSize: 4
},
measures: {
fill: ['rgba(0,128,255,1)'],
size: 15,
symbol: 'circle',
symbolSize: 4
},
target: {
fill: 'rgb(0,0,0)',
size: 20,
showType: 'dynamic',
value: 0,
symbol: 'line',
symbolSize: 4
}
}
}
}
@ -453,7 +479,8 @@ export const DEFAULT_TABLE_HEADER: ChartTableHeaderAttr = {
headerGroupConfig: {
columns: [],
meta: []
}
},
rowHeaderFreeze: true
}
export const DEFAULT_TABLE_CELL: ChartTableCellAttr = {
tableFontColor: '#000000',
@ -552,17 +579,6 @@ export const DEFAULT_TITLE_STYLE_DARK = {
remarkBackgroundColor: '#5A5C62'
}
export const DEFAULT_LEGEND_STYLE: ChartLegendStyle = {
show: true,
hPosition: 'center',
vPosition: 'bottom',
orient: 'horizontal',
icon: 'circle',
color: '#333333',
fontSize: 12,
size: 4
}
export const DEFAULT_LEGEND_STYLE_BASE: ChartLegendStyle = {
show: true,
hPosition: 'center',
@ -571,7 +587,24 @@ export const DEFAULT_LEGEND_STYLE_BASE: ChartLegendStyle = {
icon: 'circle',
color: '#333333',
fontSize: 12,
size: 4
size: 4,
showRange: true,
sort: 'none',
customSort: []
}
export const DEFAULT_LEGEND_STYLE: ChartLegendStyle = {
show: true,
hPosition: 'center',
vPosition: 'bottom',
orient: 'horizontal',
icon: 'circle',
color: '#333333',
fontSize: 12,
size: 4,
showRange: true,
sort: 'none',
customSort: []
}
export const DEFAULT_LEGEND_STYLE_LIGHT: ChartLegendStyle = {
@ -634,6 +667,7 @@ export const DEFAULT_XAXIS_STYLE: ChartAxisStyle = {
},
axisLabelFormatter: {
type: 'auto',
unitLanguage: isEnLocal ? 'en' : 'ch',
unit: 1,
suffix: '',
decimalCount: 2,
@ -680,6 +714,7 @@ export const DEFAULT_YAXIS_STYLE: ChartAxisStyle = {
},
axisLabelFormatter: {
type: 'auto',
unitLanguage: isEnLocal ? 'en' : 'ch',
unit: 1,
suffix: '',
decimalCount: 2,
@ -724,6 +759,7 @@ export const DEFAULT_YAXIS_EXT_STYLE: ChartAxisStyle = {
},
axisLabelFormatter: {
type: 'auto',
unitLanguage: isEnLocal ? 'en' : 'ch',
unit: 1,
suffix: '',
decimalCount: 2,
@ -1165,7 +1201,7 @@ export const CHART_FONT_FAMILY = [
{ name: t('chart.font_family_kai_ti'), value: 'KaiTi' }
]
export const CHART_FONT_FAMILY_MAP:any = {
export const CHART_FONT_FAMILY_MAP = {
'Microsoft YaHei': 'Microsoft YaHei',
SimSun: 'SimSun, "Songti SC", STSong',
SimHei: 'SimHei, Helvetica',
@ -1395,6 +1431,13 @@ export const CHART_TYPE_CONFIGS = [
value: 'stock-line',
title: t('chart.chart_stock_line'),
icon: 'stock-line'
},
{
render: 'antv',
category: 'compare',
value: 'bullet-graph',
title: t('chart.bullet_chart'),
icon: 'bullet-graph'
}
]
},
@ -1651,6 +1694,7 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
zoomButtonColor: '#aaa',
zoomBackground: '#fff',
tableLayoutMode: 'grid',
defaultExpandLevel: 1,
calcTopN: false,
topN: 5,
topNLabel: t('datasource.other'),
@ -1679,7 +1723,9 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
radarAreaColor: true,
circleBorderColor: '#fff',
circleBorderWidth: 0,
circlePadding: 0
circlePadding: 0,
quotaPosition: 'col',
quotaColLabel: t('dataset.value')
}
export const BASE_VIEW_CONFIG = {

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,6 @@ import {
import {
flow,
hexColorToRGBA,
hexToRgba,
parseJson,
setUpGroupSeriesColor,
setUpStackSeriesColor
@ -21,6 +20,7 @@ import {
} from '@/data-visualization/chart/components/js/panel/charts/bar/common'
import {
configPlotTooltipEvent,
configRoundAngle,
getLabel,
getPadding,
getTooltipContainer,
@ -43,7 +43,14 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
...BAR_EDITOR_PROPERTY_INNER,
'basic-style-selector': [...BAR_EDITOR_PROPERTY_INNER['basic-style-selector'], 'seriesColor'],
'label-selector': ['vPosition', 'seriesLabelFormatter', 'showExtremum'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'tooltip-selector': [
'fontSize',
'color',
'backgroundColor',
'seriesTooltipFormatter',
'show',
'carousel'
],
'y-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['y-axis-selector'], 'axisLabelFormatter']
}
protected baseOptions: ColumnOptions = {
@ -69,11 +76,14 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
async drawChart(drawOptions: G2PlotDrawOptions<Column>): Promise<Column> {
const { chart, container, action } = drawOptions
if (!chart?.data?.data?.length) {
chart.container = container
if (!chart?.data?.data?.length) {
clearExtremum(chart)
return
}
const isGroup = 'bar-group' === this.name && chart.xAxisExt?.length > 0
const isStack =
['bar-stack', 'bar-group-stack'].includes(this.name) && chart.extStack?.length > 0
const data = cloneDeep(drawOptions.chart.data?.data)
const initOptions: ColumnOptions = {
...this.baseOptions,
@ -108,7 +118,7 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
const label = {
fields: [],
...tmpOptions.label,
formatter: (data: Datum, _point) => {
formatter: (data: Datum) => {
if (data.EXTREME) {
return ''
}
@ -174,19 +184,9 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
color
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const columnStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
columnStyle
}
...configRoundAngle(chart, 'columnStyle')
}
let columnWidthRatio
const _v = basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio
@ -227,7 +227,10 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
tickCount: axisValue.splitCount
}
}
return { ...tmpOptions, ...axis }
// 根据axis的最小值过滤options中的data数据过滤掉小于最小值的数据
const { data } = options
const newData = data.filter(item => item.value >= axisValue.min)
return { ...tmpOptions, data: newData, ...axis }
}
return tmpOptions
}
@ -276,7 +279,14 @@ export class StackBar extends Bar {
'totalFormatter',
'showStackQuota'
],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show']
'tooltip-selector': [
'fontSize',
'color',
'backgroundColor',
'tooltipFormatter',
'show',
'carousel'
]
}
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
let label = getLabel(chart)
@ -438,6 +448,74 @@ export class GroupBar extends StackBar {
}
}
async drawChart(drawOptions: G2PlotDrawOptions<Column>): Promise<Column> {
const plot = await super.drawChart(drawOptions)
if (!plot) {
return plot
}
const { chart } = drawOptions
const { xAxis, xAxisExt, yAxis } = chart
let innerSort = !!(xAxis.length && xAxisExt.length && yAxis.length)
if (innerSort && yAxis[0].sort === 'none') {
innerSort = false
}
if (innerSort && xAxisExt[0].sort !== 'none') {
const sortPriority = chart.sortPriority ?? []
const yAxisIndex = sortPriority?.findIndex(e => e.id === yAxis[0].id)
const xAxisExtIndex = sortPriority?.findIndex(e => e.id === xAxisExt[0].id)
if (xAxisExtIndex <= yAxisIndex) {
innerSort = false
}
}
if (!innerSort) {
return plot
}
plot.chart.once('beforepaint', () => {
const geo = plot.chart.geometries[0]
const originMapping = geo.beforeMapping.bind(geo)
geo.beforeMapping = originData => {
const values = geo.getXScale().values
const valueMap = values.reduce((p, n) => {
if (!p?.[n]) {
p[n] = {
fieldArr: [],
indexArr: [],
dataArr: []
}
}
originData.forEach((arr, arrIndex) => {
arr.forEach((item, index) => {
if (item._origin.field === n) {
p[n].fieldArr.push(item.field)
p[n].indexArr.push([arrIndex, index])
p[n].dataArr.push(item)
}
})
})
return p
}, {})
values.forEach(v => {
const item = valueMap[v]
item.dataArr.sort((a, b) => {
if (yAxis[0].sort === 'asc') {
return a.value - b.value
}
if (yAxis[0].sort === 'desc') {
return b.value - a.value
}
return 0
})
item.indexArr.forEach((index, i) => {
item.dataArr[i].field = item.fieldArr[i]
originData[index[0]][index[1]] = item.dataArr[i]
})
})
return originMapping(originData)
}
})
return plot
}
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
const tmpLabel = getLabel(chart)
if (!tmpLabel) {
@ -448,7 +526,7 @@ export class GroupBar extends StackBar {
baseOptions.label.style.fill = labelAttr.color
const label = {
...baseOptions.label,
formatter: function (param: Datum, _point) {
formatter: function (param: Datum) {
if (param.EXTREME) {
return ''
}
@ -492,6 +570,7 @@ export class GroupBar extends StackBar {
super(name)
this.baseOptions = {
...this.baseOptions,
marginRatio: 0,
isGroup: true,
isStack: false,
meta: {
@ -606,7 +685,7 @@ export class PercentageStackBar extends GroupStackBar {
propertyInner = {
...this['propertyInner'],
'label-selector': ['color', 'fontSize', 'vPosition', 'reserveDecimalCount'],
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'show']
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'show', 'carousel']
}
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
const baseOptions = super.configLabel(chart, options)

View File

@ -6,14 +6,14 @@ import { cloneDeep, defaultTo, isEmpty, map } from 'lodash-es'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
configRoundAngle,
getPadding,
getTooltipContainer,
getTooltipItemConditionColor,
getYAxis,
getYAxisExt,
setGradientColor,
TOOLTIP_TPL,
addConditionsStyleColorToData
TOOLTIP_TPL
} from '@/data-visualization/chart/components/js/panel/common/common_antv'
import type {
BidirectionalBar as G2BidirectionalBar,
@ -213,19 +213,9 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
...options,
layout: basicStyle.layout
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
barStyle
}
...configRoundAngle(chart, 'barStyle')
}
return options
}

View File

@ -0,0 +1,507 @@
import type {
Bullet as G2Bullet,
BulletOptions as G2BulletOptions
} from '@antv/g2plot/esm/plots/bullet'
import {
G2PlotChartView,
G2PlotDrawOptions
} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot'
import {
BAR_AXIS_TYPE,
BAR_EDITOR_PROPERTY,
BAR_EDITOR_PROPERTY_INNER
} from '@/data-visualization/chart/components/js/panel/charts/bar/common'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { flow, parseJson } from '@/data-visualization/chart/components/js/util'
import { BulletOptions } from '@antv/g2plot'
import { isEmpty } from 'lodash-es'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
getPadding,
getTooltipContainer,
TOOLTIP_TPL
} from '@/data-visualization/chart/components/js/panel/common/common_antv'
import { valueFormatter } from '@/data-visualization/chart/components/js/formatter'
const { t } = useI18n()
/**
*
*/
export class BulletGraph extends G2PlotChartView<G2BulletOptions, G2Bullet> {
constructor() {
super('bullet-graph', [])
}
axis: AxisType[] = [...BAR_AXIS_TYPE, 'yAxisExt', 'extBubble']
axisConfig = {
...this['axisConfig'],
xAxis: { name: `${t('chart.form_type')} / ${t('chart.dimension')}`, type: 'd', limit: 1 },
yAxis: { name: `${t('chart.progress_current')} / ${t('chart.quota')}`, type: 'q', limit: 1 },
yAxisExt: { name: `${t('chart.progress_target')} / ${t('chart.quota')}`, type: 'q', limit: 1 },
extBubble: {
name: `${t('chart.range_bg')} / ${t('chart.quota')}`,
type: 'q',
allowEmpty: true,
limit: 1
}
}
properties: EditorProperty[] = [
...BAR_EDITOR_PROPERTY.filter(
item => !['function-cfg', 'assist-line', 'threshold'].includes(item)
),
'bullet-graph-selector'
]
propertyInner = {
'basic-style-selector': ['radiusColumnBar', 'layout'],
'label-selector': ['hPosition', 'fontSize', 'color', 'labelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'x-axis-selector': [
...BAR_EDITOR_PROPERTY_INNER['x-axis-selector'].filter(item => item != 'position'),
'showLengthLimit'
],
'y-axis-selector': [
...BAR_EDITOR_PROPERTY_INNER['y-axis-selector'].filter(
item => item !== 'axisValue' && item !== 'position'
),
'axisLabelFormatter'
],
'legend-selector': ['showRange', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition']
}
async drawChart(drawOption: G2PlotDrawOptions<G2Bullet>): Promise<G2Bullet> {
const { chart, container, action } = drawOption
if (!chart.data?.data?.length) return
const result = mergeBulletData(chart)
// 处理自定义区间
const { bullet } = parseJson(chart.customAttr).misc
if (bullet.bar.ranges.showType === 'fixed') {
const customRange = bullet.bar.ranges.fixedRange?.map(item => item.fixedRangeValue) || [0]
result.forEach(item => (item.ranges = customRange))
} else {
result.forEach(item => (item.ranges = item.originalRanges))
}
// 处理自定义目标值
if (bullet.bar.target.showType === 'fixed') {
const customTarget = bullet.bar.target.value || 0
result.forEach(item => (item.target = customTarget))
} else {
result.forEach(item => (item.target = item.originalTarget))
}
const initialOptions: BulletOptions = {
appendPadding: getPadding(chart),
data: result.reverse(),
measureField: 'measures',
rangeField: 'ranges',
targetField: 'target',
xField: 'title',
meta: {
title: {
type: 'cat'
}
},
interactions: [
{
type: 'active-region',
cfg: {
start: [{ trigger: 'element:mousemove', action: 'active-region:show' }],
end: [{ trigger: 'element:mouseleave', action: 'active-region:hide' }]
}
}
]
}
const options = this.setupOptions(chart, initialOptions)
let newChart = null
const { Bullet: BulletClass } = await import('@antv/g2plot/esm/plots/bullet')
newChart = new BulletClass(container, options)
newChart.on('element:click', ev => {
const pointData = ev?.data?.data
const dimensionList = options.data.find(item => item.title === pointData.title)?.dimensionList
const actionParams = {
x: ev.x,
y: ev.y,
data: {
data: {
...pointData,
dimensionList
}
}
}
action(actionParams)
})
configPlotTooltipEvent(chart, newChart)
configAxisLabelLengthLimit(chart, newChart, null)
return newChart
}
protected configBasicStyle(chart: Chart, options: BulletOptions): BulletOptions {
const basicStyle = parseJson(chart.customAttr).basicStyle
const { radiusColumnBar, columnBarRightAngleRadius, layout } = basicStyle
let radiusValue = 0
let rangeLength = 1
if (radiusColumnBar === 'roundAngle' || radiusColumnBar === 'topRoundAngle') {
radiusValue = columnBarRightAngleRadius
rangeLength = options.data[0]?.ranges?.length
}
const barRadiusStyle = { radius: Array(2).fill(radiusValue) }
const baseRadius = [...barRadiusStyle.radius, ...barRadiusStyle.radius]
options = {
...options,
bulletStyle: {
range: datum => {
if (!datum.rKey) return { fill: 'rgba(0, 0, 0, 0)' }
if (rangeLength === 1) {
return {
radius:
radiusColumnBar === 'topRoundAngle' ? [...barRadiusStyle.radius, 0, 0] : baseRadius
}
}
if (rangeLength > 1 && datum.rKey === 'ranges_0') {
return {
radius: radiusColumnBar === 'topRoundAngle' ? [] : [0, 0, ...barRadiusStyle.radius]
}
}
if (rangeLength > 1 && datum.rKey === 'ranges_' + (rangeLength - 1)) {
return { radius: [...barRadiusStyle.radius, 0, 0] }
}
},
measure: datum => {
if (datum.measures) {
return {
radius:
radiusColumnBar === 'topRoundAngle' ? [...barRadiusStyle.radius, 0, 0] : baseRadius
}
} else {
return undefined
}
},
target: datum => (datum.tKey === 'target' ? { lineWidth: 2 } : undefined)
}
}
if (layout === 'vertical') options = { ...options, layout: 'vertical' }
return options
}
protected configMisc(chart: Chart, options: BulletOptions): BulletOptions {
const { bullet } = parseJson(chart.customAttr).misc
const isDynamic = bullet.bar.ranges.showType === 'dynamic'
// 动态背景按大小升序
const rangeColor = isDynamic
? bullet.bar.ranges.fill
: bullet.bar.ranges.fixedRange
?.sort((a, b) => (a.fixedRangeValue ?? 0) - (b.fixedRangeValue ?? 0))
.map(item => item.fill) || []
return {
...options,
color: {
measure: [].concat(bullet.bar.measures.fill),
range: [].concat(rangeColor),
target: [].concat(bullet.bar.target.fill)
},
size: {
measure: bullet.bar.measures.size,
range: bullet.bar.ranges.size,
target: bullet.bar.target.size
}
}
}
protected configXAxis(chart: Chart, options: BulletOptions): BulletOptions {
const tmpOptions = super.configXAxis(chart, options)
if (!tmpOptions.xAxis || !tmpOptions.xAxis.label) return tmpOptions
const { layout, xAxis } = tmpOptions
const position = xAxis.position
const style: any = { ...xAxis.label.style }
if (layout === 'vertical') {
style.textAlign = 'center'
style.textBaseline = position === 'bottom' ? 'top' : 'bottom'
} else {
style.textAlign = position === 'bottom' ? 'end' : 'start'
style.textBaseline = 'middle'
}
xAxis.label.style = style
return tmpOptions
}
protected configYAxis(chart: Chart, options: BulletOptions): BulletOptions {
const tmpOptions = super.configYAxis(chart, options)
if (!tmpOptions.yAxis || !tmpOptions.yAxis.label) return tmpOptions
const yAxis = parseJson(chart.customStyle).yAxis
tmpOptions.yAxis.label.formatter = value => valueFormatter(value, yAxis.axisLabelFormatter)
const { layout, yAxis: yAxisConfig } = tmpOptions
const position = yAxisConfig.position
const style: any = { ...yAxisConfig.label.style }
if (layout === 'vertical') {
style.textAlign = position === 'left' ? 'end' : 'start'
style.textBaseline = 'middle'
} else {
style.textAlign = 'center'
style.textBaseline = position === 'left' ? 'top' : 'bottom'
}
yAxisConfig.label.style = style
return tmpOptions
}
protected configLabel(chart: Chart, options: BulletOptions): BulletOptions {
const tmpOptions = super.configLabel(chart, options)
if (!tmpOptions.label) return tmpOptions
const labelAttr = parseJson(chart.customAttr).label
const label: any = {
...tmpOptions.label,
formatter: param =>
param.mKey === 'measures'
? valueFormatter(param.measures, labelAttr.labelFormatter)
: undefined
}
return { ...tmpOptions, label: { measure: label } }
}
protected configLegend(chart: Chart, options: BulletOptions): BulletOptions {
const baseLegend = super.configLegend(chart, options).legend
if (!baseLegend) return options
const { bullet } = parseJson(chart.customAttr).misc
const customStyleLegend = parseJson(chart.customStyle).legend
const items = []
const createLegendItem = (value, name, symbol, fill, size = 4) => ({
value,
name,
marker: { symbol, style: { fill, stroke: value === 'measure' ? '' : fill, r: size } }
})
if (customStyleLegend.showRange) {
if (bullet.bar.ranges.showType === 'dynamic') {
if (chart.extBubble.length) {
const rangeName = chart.extBubble[0]?.chartShowName || bullet.bar.ranges.name
items.push(
createLegendItem(
'dynamic',
rangeName || chart.extBubble[0]?.name,
bullet.bar.ranges.symbol,
[].concat(bullet.bar.ranges.fill)[0],
bullet.bar.ranges.symbolSize
)
)
}
} else {
bullet.bar.ranges.fixedRange?.forEach(item => {
items.push(
createLegendItem(
item.name,
item.name,
bullet.bar.ranges.symbol,
item.fill,
bullet.bar.ranges.symbolSize
)
)
})
}
}
const targetName = chart.yAxisExt[0]?.chartShowName || bullet.bar.target.name
items.push(
createLegendItem(
'target',
targetName || chart.yAxisExt[0]?.name,
'line',
[].concat(bullet.bar.target.fill)[0],
bullet.bar.ranges.symbolSize
)
)
const measureName = chart.yAxis[0]?.chartShowName || bullet.bar.measures.name
items.push(
createLegendItem(
'measure',
measureName || chart.yAxis[0]?.name,
'square',
[].concat(bullet.bar.measures.fill)[0],
bullet.bar.ranges.symbolSize
)
)
return {
...options,
legend: { custom: true, position: baseLegend.position, layout: baseLegend.layout, items }
}
}
protected configTooltip(chart: Chart, options: BulletOptions): BulletOptions {
const customAttr: DeepPartial<ChartAttr> = parseJson(chart.customAttr)
const tooltipAttr = customAttr.tooltip
const { bullet } = parseJson(chart.customAttr).misc
if (!tooltipAttr.show) return { ...options, tooltip: false }
const formatterMap = tooltipAttr.seriesTooltipFormatter
?.filter(i => i.show)
.reduce((pre, next, index) => {
const keys = ['measures', 'target', 'ranges']
if (keys[index]) pre[keys[index]] = next
return pre
}, {}) as Record<string, SeriesFormatter>
const tooltip = {
shared: true,
showMarkers: true,
customItems(originalItems) {
if (!tooltipAttr.seriesTooltipFormatter?.length) return originalItems
const result = []
const data = options.data.find(item => item.title === originalItems[0].title)
Object.keys(formatterMap).forEach(key => {
if (key === '记录数*') return
const formatter = formatterMap[key]
if (formatter) {
if (key !== 'ranges') {
let value = 0
if (chart.yAxis[0].id === chart.yAxisExt[0].id) {
value = valueFormatter(parseFloat(data['target'] as string), formatter.formatterCfg)
} else {
value = valueFormatter(parseFloat(data[key] as string), formatter.formatterCfg)
}
const name = isEmpty(formatter.chartShowName)
? formatter.name
: formatter.chartShowName
result.push({ ...originalItems[0], color: bullet.bar[key].fill, name, value })
} else {
const ranges = data.ranges
const isDynamic = bullet.bar.ranges.showType === 'dynamic'
ranges.forEach((range, index) => {
const value = valueFormatter(
parseFloat(isDynamic ? data.minRanges[0] : (range as string)),
formatter.formatterCfg
)
let name = ''
let color: string | string[]
if (bullet.bar.ranges.showType === 'dynamic') {
name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName
color = bullet.bar[key].fill
} else {
const customRange = bullet.bar.ranges.fixedRange[index].name
name = customRange
? customRange
: isEmpty(formatter.chartShowName)
? formatter.name
: formatter.chartShowName
color = bullet.bar[key].fixedRange[index].fill
}
result.push({ ...originalItems[0], color, name, value })
})
}
}
})
const dynamicTooltipValue = chart.data.data.find(
d => d.field === originalItems[0]['title']
)?.dynamicTooltipValue
if (dynamicTooltipValue.length > 0) {
dynamicTooltipValue.forEach(dy => {
const q = tooltipAttr.seriesTooltipFormatter.filter(i => i.id === dy.fieldId)
if (q && q.length > 0) {
const value = valueFormatter(parseFloat(dy.value as string), q[0].formatterCfg)
const name = isEmpty(q[0].chartShowName) ? q[0].name : q[0].chartShowName
result.push({ color: 'grey', name, value })
}
})
}
return result
},
container: getTooltipContainer(`tooltip-${chart.id}`),
itemTpl: TOOLTIP_TPL,
enterable: true
}
return { ...options, tooltip }
}
setupDefaultOptions(chart: ChartObj): ChartObj {
chart.customAttr.label.position = 'middle'
chart.customStyle.yAxis.splitLine.show = false
return super.setupDefaultOptions(chart)
}
protected setupOptions(chart: Chart, options: BulletOptions): BulletOptions {
return flow(
this.configTheme,
this.configBasicStyle,
this.configMisc,
this.configXAxis,
this.configYAxis,
this.configLabel,
this.configLegend,
this.configTooltip
)(chart, options, {}, this)
}
}
/**
*
* @param chart
*/
function mergeBulletData(chart): any[] {
// 先根据维度分组,再根据指标字段组装成子弹图的格式
const groupedData = chart.data.data.reduce((acc, item) => {
const field = item.field
if (!acc[field]) {
acc[field] = []
}
acc[field].push(item)
return acc
}, {})
const result = []
// 组装子弹图数据,每个维度对应一个子弹图
Object.keys(groupedData).forEach(field => {
const items = groupedData[field]
// 初始化子弹图条目结构
const entry = {
title: field,
ranges: [],
measures: [],
target: [],
dimensionList: items[0].dimensionList,
quotaList: []
}
// 防止指标相同时无数据有可能会导致数据不一致
items.forEach(item => {
const quotaId = item.quotaList[0]?.id
const v = item.value || 0
if (quotaId === chart.yAxis[0]?.id) {
entry.measures.push(v)
}
if (quotaId === chart.yAxisExt[0]?.id) {
entry.target.push(v)
}
if (quotaId === chart.extBubble[0]?.id) {
entry.ranges.push(v)
}
entry.quotaList.push(item.quotaList[0])
})
// 对数据进行累加
const ranges = chart.extBubble[0]?.id
? [].concat(entry.ranges?.reduce((acc, curr) => acc + curr, 0))
: []
const target = [].concat(entry.target?.reduce((acc, curr) => acc + curr, 0))
const measures = [].concat(entry.measures?.reduce((acc, curr) => acc + curr, 0))
const bulletData = {
...entry,
measures: measures,
target: target,
ranges: ranges,
quotaList: [...entry.quotaList],
minRanges: ranges,
originalRanges: ranges,
originalTarget: target
}
result.push(bulletData)
})
return result
}

View File

@ -6,6 +6,7 @@ import type { Bar, BarOptions } from '@antv/g2plot/esm/plots/bar'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
configRoundAngle,
getPadding,
getTooltipContainer,
setGradientColor,
@ -101,6 +102,17 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
const newChart = new Bar(container, options)
newChart.on('interval:click', action)
if (options.label) {
newChart.on('label:click', e => {
action({
x: e.x,
y: e.y,
data: {
data: e.target.attrs.data
}
})
})
}
configPlotTooltipEvent(chart, newChart)
configAxisLabelLengthLimit(chart, newChart)
return newChart
@ -135,7 +147,10 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
tickCount: axisValue.splitCount
}
}
return { ...tmpOptions, ...axis }
// 根据axis的最小值过滤options中的data数据过滤掉小于最小值的数据
const { data } = options
const newData = data.filter(item => item.value >= axisValue.min)
return { ...tmpOptions, data: newData, ...axis }
}
return tmpOptions
}
@ -157,19 +172,9 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
color
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
barStyle
}
...configRoundAngle(chart, 'barStyle')
}
let barWidthRatio
@ -234,6 +239,7 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
attrs: {
x: 0,
y: 0,
data,
text: value,
textAlign: 'start',
textBaseline: 'top',
@ -316,8 +322,24 @@ export class HorizontalStackBar extends HorizontalBar {
baseOptions.label.style.fill = labelAttr.color
const label = {
...baseOptions.label,
formatter: function (param: Datum) {
return valueFormatter(param.value, labelAttr.labelFormatter)
formatter: function (data: Datum) {
const value = valueFormatter(data.value, labelAttr.labelFormatter)
const group = new Group({})
group.addShape({
type: 'text',
attrs: {
x: 0,
y: 0,
data,
text: value,
textAlign: 'start',
textBaseline: 'top',
fontSize: labelAttr.fontSize,
fontFamily: chart.fontFamily,
fill: labelAttr.color
}
})
return group
}
}
return {
@ -435,11 +457,29 @@ export class HorizontalPercentageStackBar extends HorizontalStackBar {
const l = parseJson(customAttr).label
const label = {
...baseOptions.label,
formatter: function (param: Datum) {
if (!param.value) {
return '0%'
formatter: function (data: Datum) {
let value = data.value
if (value) {
value = (Math.round(value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%'
} else {
value = '0%'
}
return (Math.round(param.value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%'
const group = new Group({})
group.addShape({
type: 'text',
attrs: {
x: 0,
y: 0,
data,
text: value,
textAlign: 'start',
textBaseline: 'top',
fontSize: l.fontSize,
fontFamily: chart.fontFamily,
fill: l.color
}
})
return group
}
}
return {

View File

@ -3,8 +3,8 @@ import { flow, hexColorToRGBA, parseJson } from '../../../util'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
configRoundAngle,
getTooltipContainer,
getTooltipItemConditionColor,
setGradientColor,
TOOLTIP_TPL
} from '../../common/common_antv'
@ -66,7 +66,7 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
'fontSize',
'axisForm',
'axisLabel',
'position',
// 'position',
'showLengthLimit'
],
'function-cfg': ['emptyDataStrategy'],
@ -166,6 +166,7 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
}
})
if (basicStyle.gradient) {
// eslint-disable-next-line
color1 = color1.map((ele, _index) => {
return setGradientColor(ele, true, 0)
})
@ -184,19 +185,9 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
}
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
barStyle
}
...configRoundAngle(chart, 'barStyle')
}
let barWidthRatio
@ -297,12 +288,31 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
if (!baseOption.yAxis) {
return baseOption
}
if (baseOption.yAxis.position === 'left') {
baseOption.yAxis.position = 'bottom'
const yAxis = parseJson(chart.customStyle).yAxis
if (yAxis.axisLabel.show) {
const rotate = yAxis.axisLabel.rotate
let textAlign = 'end'
let textBaseline = 'middle'
if (Math.abs(rotate) > 75) {
textAlign = 'center'
}
if (rotate > 75) {
textBaseline = 'top'
}
if (rotate < -75) {
textBaseline = 'bottom'
}
baseOption.yAxis.label.style.textBaseline = textBaseline
baseOption.yAxis.label.style.textAlign = textAlign
}
/*if (baseOption.yAxis.position === 'left') {
baseOption.yAxis.position = 'bottom'
}
if (baseOption.yAxis.position === 'right') {
baseOption.yAxis.position = 'top'
}
}*/
return baseOption
}
setupDefaultOptions(chart: ChartObj): ChartObj {

View File

@ -6,6 +6,7 @@ import type { Bar, BarOptions } from '@antv/g2plot/esm/plots/bar'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
configRoundAngle,
getPadding,
getTooltipContainer,
setGradientColor,
@ -22,6 +23,7 @@ import {
import { Datum } from '@antv/g2plot/esm/types/common'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart'
import { Group } from '@antv/g-canvas'
const { t } = useI18n()
const DEFAULT_DATA = []
@ -170,6 +172,17 @@ export class RangeBar extends G2PlotChartView<BarOptions, Bar> {
const newChart = new BarClass(container, options)
newChart.on('interval:click', action)
if (options.label) {
newChart.on('label:click', e => {
action({
x: e.x,
y: e.y,
data: {
data: e.target.attrs.data
}
})
})
}
configPlotTooltipEvent(chart, newChart)
configAxisLabelLengthLimit(chart, newChart)
return newChart
@ -309,19 +322,10 @@ export class RangeBar extends G2PlotChartView<BarOptions, Bar> {
}
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
barStyle
}
...configRoundAngle(chart, 'barStyle')
}
let barWidthRatio
const _v = basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio
@ -391,7 +395,22 @@ export class RangeBar extends G2PlotChartView<BarOptions, Bar> {
valueFormatter(param.values[1], labelAttr.labelFormatter)
}
}
return res
const group = new Group({})
group.addShape({
type: 'text',
attrs: {
x: 0,
y: 0,
data: param,
text: res,
textAlign: 'start',
textBaseline: 'top',
fontSize: labelAttr.fontSize,
fontFamily: chart.fontFamily,
fill: labelAttr.color
}
})
return group
}
}
return {

View File

@ -72,7 +72,8 @@ export class Waterfall extends G2PlotChartView<WaterfallOptions, G2Waterfall> {
'axisForm',
'axisLabel',
'axisLabelFormatter',
'showLengthLimit'
'showLengthLimit',
'axisLine'
],
threshold: ['lineThreshold']
}

View File

@ -46,7 +46,8 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
'label-selector': ['seriesLabelVPosition', 'seriesLabelFormatter', 'showExtremum'],
'tooltip-selector': [
...LINE_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axis: AxisType[] = [...LINE_AXIS_TYPE]
@ -103,8 +104,8 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
async drawChart(drawOptions: G2PlotDrawOptions<G2Area>): Promise<G2Area> {
const { chart, container, action } = drawOptions
if (!chart.data?.data?.length) {
chart.container = container
if (!chart.data?.data?.length) {
clearExtremum(chart)
return
}
@ -147,7 +148,7 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
fields: [],
...tmpOptions.label,
layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout,
formatter: (data: Datum, _point) => {
formatter: (data: Datum) => {
if (data.EXTREME) {
return ''
}
@ -305,7 +306,7 @@ export class StackArea extends Area {
propertyInner = {
...this['propertyInner'],
'label-selector': ['vPosition', 'fontSize', 'color', 'labelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter', 'show']
'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter', 'show', 'carousel']
}
axisConfig = {
...this['axisConfig'],

View File

@ -0,0 +1,673 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
exports.__esModule = true;
exports.StockLine = void 0;
var g2plot_1 = require("@/views/chart/components/js/panel/types/impl/g2plot");
var util_1 = require("@/views/chart/components/js/util");
var common_1 = require("@/views/chart/components/js/panel/charts/line/common");
var useI18n_1 = require("@/hooks/web/useI18n");
var formatter_1 = require("@/views/chart/components/js/formatter");
var t = useI18n_1.useI18n().t;
var DEFAULT_DATA = [];
/**
* K线图
*/
var StockLine = /** @class */ (function (_super) {
__extends(StockLine, _super);
function StockLine(name) {
if (name === void 0) { name = 'stock-line'; }
var _this = _super.call(this, name, DEFAULT_DATA) || this;
_this.properties = [
'background-overall-component',
'border-style',
'basic-style-selector',
'legend-selector',
'x-axis-selector',
'y-axis-selector',
'title-selector',
'tooltip-selector',
'function-cfg',
'jump-set',
'linkage'
];
_this.propertyInner = __assign(__assign({}, common_1.LINE_EDITOR_PROPERTY_INNER), { 'function-cfg': ['emptyDataStrategy'], 'y-axis-selector': [
'name',
'color',
'fontSize',
'position',
'axisLabel',
'axisLine',
'splitLine',
'axisLabelFormatter'
], 'legend-selector': ['fontSize', 'color', 'show'] });
_this.axis = ['xAxis', 'yAxis', 'filter', 'extLabel', 'extTooltip'];
_this.axisConfig = {
xAxis: {
name: t('common.component.date') + " / " + t('chart.dimension'),
limit: 1,
type: 'd'
},
yAxis: {
name: t('chart.k_line_yaxis_tip') + " / " + t('chart.quota'),
limit: 4,
type: 'q'
}
};
/**
* 计算收盘价平均值
* @param data
* @param dayCount
* @param chart
*/
_this.calculateMovingAverage = function (data, dayCount, chart) {
var _a, _b;
var xAxis = chart.xAxis;
var yAxis = chart.yAxis;
// 时间字段
var xAxisgisbiName = xAxis[0].gisbiName;
// 收盘价字段
var yAxisgisbiName = yAxis[1].gisbiName;
var result = [];
for (var i = 0; i < data.length; i++) {
if (i < dayCount) {
result.push((_a = {},
_a[xAxisgisbiName] = data[i][xAxisgisbiName],
_a.value = null,
_a));
}
else {
var sum = data
.slice(i - dayCount + 1, i + 1)
.reduce(function (sum, item) { return sum + item[yAxisgisbiName]; }, 0);
result.push((_b = {},
_b[xAxisgisbiName] = data[i][xAxisgisbiName],
_b.value = parseFloat((sum / dayCount).toFixed(3)),
_b));
}
}
return result;
};
/**
* 获取数据集合中对象属性值的最大最小值
* @param data
*/
_this.calculateMinMax = function (data) {
return data.reduce(function (acc, current) {
// 获取 current 对象的所有属性值
var values = Object.values(current);
// 过滤出数字值
var numericValues = values.filter(function (value) { return typeof value === 'number'; });
// 找到 current 对象的数字属性值中的最大值和最小值
// 如果存在数字值,则计算当前对象的最大值和最小值
if (numericValues.length > 0) {
var currentMax = Math.max.apply(Math, numericValues);
var currentMin = Math.min.apply(Math, numericValues);
// 更新全局最大值和最小值
acc.maxValue = Math.max(acc.maxValue, currentMax);
acc.minValue = Math.min(acc.minValue, currentMin);
}
return acc;
}, { maxValue: Number.NEGATIVE_INFINITY, minValue: Number.POSITIVE_INFINITY });
};
/**
* 注册图表事件
* @param data
* @param plot
* @param averagesLineData
*/
_this.registerEvent = function (data, plot, averagesLineData) {
// 监听图例点击事件,显示隐藏
var risingVisible = true;
plot.on('legend-item:click', function (evt) {
var value = evt.target.get('delegateObject').item.value;
if (value === 'k') {
risingVisible = !risingVisible;
plot.chart.geometries.forEach(function (geom) {
if (geom.type === 'schema') {
geom.changeVisible(risingVisible);
}
});
}
else {
var lines = plot.chart.geometries.filter(function (item) { return item.type === 'line'; });
var points = plot.chart.geometries.filter(function (item) { return item.type === 'point'; });
var lineIndex = 0;
for (var _i = 0, _a = averagesLineData.keys(); _i < _a.length; _i++) {
var key = _a[_i];
lineIndex++;
if (key === value) {
lines[lineIndex - 1].changeVisible(!lines[lineIndex - 1].visible);
points[lineIndex - 1].changeVisible(!points[lineIndex - 1].visible);
}
}
}
});
// 监听图表渲染事件
plot.on('afterrender', function (e) {
var _a, _b;
var first = false;
if (plot.chart.options.slider.start === 0.5 && plot.chart.options.slider.end === 1) {
first = true;
}
if ((_b = (_a = e.view) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.scales) {
var startIndex = Math.floor(0.5 * data.length);
var endIndex = Math.ceil(1 * data.length);
var filteredData = data.slice(startIndex, endIndex);
var _c = _this.calculateMinMax(first ? filteredData : e.view.filteredData), maxValue_1 = _c.maxValue, minValue_1 = _c.minValue;
var a_1 = e.view.options.scales;
Object.keys(a_1).forEach(function (item) {
if (a_1[item].max) {
a_1[item].max = maxValue_1;
}
if (a_1[item].min) {
a_1[item].min = minValue_1;
}
});
}
});
// 监听图例组点击事件,设置缩放
plot.on('legend-item-group:click', function (e) {
var _a, _b;
if ((_b = (_a = e.view) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.scales) {
var _c = _this.calculateMinMax(e.view.filteredData), maxValue_2 = _c.maxValue, minValue_2 = _c.minValue;
var a_2 = e.view.options.scales;
Object.keys(a_2).forEach(function (item) {
if (a_2[item].max) {
a_2[item].max = maxValue_2;
}
if (a_2[item].min) {
a_2[item].min = minValue_2;
}
});
}
});
// 监听滑块事件,设置缩放
plot.on('slider:valuechanged', function (e) {
var start = e.gEvent.currentTarget.cfg.component.cfg.start;
var end = e.gEvent.currentTarget.cfg.component.cfg.end;
plot.chart.options.slider.start = start;
plot.chart.options.slider.end = end;
var startIndex = Math.floor(start * data.length);
var endIndex = Math.ceil(end * data.length);
var filteredData = data.slice(startIndex, endIndex);
var _a = _this.calculateMinMax(filteredData), maxValue = _a.maxValue, minValue = _a.minValue;
var a = e.view.options.scales;
Object.keys(a).forEach(function (item) {
if (a[item].max) {
a[item].max = maxValue;
}
if (a[item].min) {
a[item].min = minValue;
}
});
});
};
return _this;
}
StockLine.prototype.drawChart = function (drawOptions) {
var _a, _b, _c, _d, _e, _f;
return __awaiter(this, void 0, Promise, function () {
var chart, action, container, xAxis, yAxis, basicStyle, colors, alpha, data, xAxisgisbiName, averages, legendItems, averagesLineData, averageLines, index, start, end, startIndex, endIndex, filteredData, _g, maxValue, minValue, _i, _h, key, axis, dateFormat, dateSplit, option, MixClass, plot;
var _j;
var _this = this;
return __generator(this, function (_k) {
switch (_k.label) {
case 0:
chart = drawOptions.chart, action = drawOptions.action, container = drawOptions.container;
if (!((_b = (_a = chart.data) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.length)) {
return [2 /*return*/];
}
xAxis = chart.xAxis;
yAxis = chart.yAxis;
if (yAxis.length != 4) {
return [2 /*return*/];
}
basicStyle = util_1.parseJson(chart.customAttr).basicStyle;
colors = [];
alpha = basicStyle.alpha;
basicStyle.colors.forEach(function (ele) {
colors.push(util_1.hexColorToRGBA(ele, alpha));
});
data = util_1.parseJson((_c = chart.data) === null || _c === void 0 ? void 0 : _c.tableRow);
xAxisgisbiName = xAxis[0].gisbiName;
averages = [5, 10, 20, 60, 120, 180];
legendItems = [
{
name: '日K',
value: 'k',
marker: {
symbol: function (x, y, r) {
var width = r * 1;
var height = r;
return [
// 矩形框
['M', x - width - 1 / 2, y - height / 2],
['L', x + width + 1 / 2, y - height / 2],
['L', x + width + 1 / 2, y + height / 2],
['L', x - width - 1 / 2, y + height / 2],
['Z'],
// 中线
['M', x, y + 10 / 2],
['L', x, y - 10 / 2]
];
},
style: { fill: 'red', stroke: 'red', lineWidth: 2 }
}
}
];
averagesLineData = new Map();
averages.forEach(function (item) {
averagesLineData.set('ma' + item, _this.calculateMovingAverage(data, item, chart));
});
// 将均线数据设置到主数据中
data.forEach(function (item) {
var _a;
var date = item[xAxisgisbiName];
for (var _i = 0, averagesLineData_1 = averagesLineData; _i < averagesLineData_1.length; _i++) {
var _b = averagesLineData_1[_i], key = _b[0], value = _b[1];
item[key] = (_a = value.find(function (m) { return m[xAxisgisbiName] === date; })) === null || _a === void 0 ? void 0 : _a.value;
}
});
averageLines = [];
index = 0;
start = 0.5;
end = 1;
startIndex = Math.floor(start * data.length);
endIndex = Math.ceil(end * data.length);
filteredData = data.slice(startIndex, endIndex);
_g = this.calculateMinMax(filteredData), maxValue = _g.maxValue, minValue = _g.minValue;
for (_i = 0, _h = averagesLineData.keys(); _i < _h.length; _i++) {
key = _h[_i];
index++;
averageLines.push({
type: 'line',
top: true,
options: {
smooth: false,
xField: xAxisgisbiName,
yField: key,
color: colors[index - 1],
xAxis: null,
yAxis: {
label: false,
min: minValue,
max: maxValue,
grid: null,
line: null
},
lineStyle: {
lineWidth: 2
}
}
});
legendItems.push({
name: key.toUpperCase(),
value: key,
marker: { symbol: 'hyphen', style: { stroke: colors[index - 1], lineWidth: 2 } }
});
}
axis = (_d = chart.xAxis) !== null && _d !== void 0 ? _d : [];
dateSplit = ((_e = axis[0]) === null || _e === void 0 ? void 0 : _e.datePattern) === 'date_split' ? '/' : '-';
switch ((_f = axis[0]) === null || _f === void 0 ? void 0 : _f.dateStyle) {
case 'y':
dateFormat = 'YYYY';
break;
case 'y_M':
dateFormat = 'YYYY' + dateSplit + 'MM';
break;
case 'y_M_d':
dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD';
break;
// case 'H_m_s':
// dateFormat = 'HH:mm:ss'
// break
case 'y_M_d_H':
dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH';
break;
case 'y_M_d_H_m':
dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH:mm';
break;
case 'y_M_d_H_m_s':
dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH:mm:ss';
break;
default:
dateFormat = 'YYYY-MM-dd HH:mm:ss';
}
option = this.setupOptions(chart, {
data: data,
slider: {
start: 0.5,
end: 1,
textStyle: {
fontFamily: chart.fontFamily
}
},
plots: __spreadArrays([
{
type: 'stock',
top: true,
options: {
meta: (_j = {},
_j[xAxisgisbiName] = {
mask: dateFormat
},
_j),
stockStyle: {
stroke: 'black',
lineWidth: 0.5
},
yAxis: {
label: {},
position: 'left',
min: minValue,
max: maxValue
},
xField: xAxisgisbiName,
yField: [
yAxis[0].gisbiName,
yAxis[1].gisbiName,
yAxis[2].gisbiName,
yAxis[3].gisbiName
],
legend: {
position: 'top',
custom: true,
items: legendItems
}
}
}
], averageLines)
});
return [4 /*yield*/, Promise.resolve().then(function () { return require('@antv/g2plot/esm/plots/mix'); })];
case 1:
MixClass = (_k.sent()).Mix;
plot = new MixClass(container, option);
this.registerEvent(data, plot, averagesLineData);
plot.on('schema:click', function (evt) {
var _a;
var selectSchema = evt.data.data[xAxisgisbiName];
var paramData = util_1.parseJson((_a = chart.data) === null || _a === void 0 ? void 0 : _a.data);
var selectData = paramData.filter(function (item) { return item.field === selectSchema; });
var quotaList = [];
selectData.forEach(function (item) {
quotaList.push(__assign(__assign({}, item.quotaList[0]), { value: item.value }));
});
if (selectData.length) {
var param = {
x: evt.x,
y: evt.y,
data: {
data: __assign(__assign({}, evt.data.data), { value: quotaList[0].value, name: selectSchema, dimensionList: selectData[0].dimensionList, quotaList: quotaList })
}
};
action(param);
}
});
return [2 /*return*/, plot];
}
});
});
};
StockLine.prototype.configBasicStyle = function (chart, options) {
// size
var customAttr = util_1.parseJson(chart.customAttr);
var s = JSON.parse(JSON.stringify(customAttr.basicStyle));
var smooth = s.lineSmooth;
var point = {
size: s.lineSymbolSize,
shape: s.lineSymbol
};
var lineStyle = {
lineWidth: s.lineWidth
};
var plots = [];
options.plots.forEach(function (item) {
if (item.type === 'stock') {
plots.push(__assign({}, item));
}
if (item.type === 'line') {
plots.push(__assign(__assign({}, item), { options: __assign(__assign({}, item.options), { smooth: smooth, point: point, lineStyle: lineStyle }) }));
}
});
return __assign(__assign({}, options), { plots: plots });
};
StockLine.prototype.configTooltip = function (chart, options) {
var tooltipAttr = util_1.parseJson(chart.customAttr).tooltip;
var newPlots = [];
var linePlotList = options.plots.filter(function (item) { return item.type === 'line'; });
linePlotList.forEach(function (item) {
newPlots.push(item);
});
var stockPlot = options.plots.filter(function (item) { return item.type === 'stock'; })[0];
if (!tooltipAttr.show) {
var stockOption_1 = __assign(__assign({}, stockPlot.options), { tooltip: {
showContent: false
} });
newPlots.push(__assign(__assign({}, stockPlot), { options: stockOption_1 }));
return __assign(__assign({}, options), { plots: newPlots });
}
var showFiled = chart.data.fields;
var customTooltipItems = function (originalItems) {
var _a;
var formattedItems = originalItems.map(function (item) {
var fieldObj = showFiled.find(function (q) { return q.gisbiName === item.name; });
var displayName = (fieldObj === null || fieldObj === void 0 ? void 0 : fieldObj.chartShowName) || (fieldObj === null || fieldObj === void 0 ? void 0 : fieldObj.name) || item.name;
var formattedName = displayName.startsWith('ma') ? displayName.toUpperCase() : displayName;
tooltipAttr.tooltipFormatter.decimalCount = 3;
var formattedValue = formatter_1.valueFormatter(item.value, tooltipAttr.tooltipFormatter);
return __assign(__assign({}, item), { name: formattedName, value: formattedValue, color: item.color });
});
var hasKLine = formattedItems.some(function (item) { return !item.name.startsWith('MA'); });
var kLines = formattedItems.filter(function (item) { return !item.name.startsWith('MA'); });
return hasKLine
? __spreadArrays([
{ name: '日K', value: '', marker: true, color: (_a = kLines[0]) === null || _a === void 0 ? void 0 : _a.color }
], kLines, formattedItems.filter(function (item) { return item.name.startsWith('MA'); })) : formattedItems;
};
var formatTooltipItem = function (item) {
var size = item.name.startsWith('MA') || !item.value ? 10 : 5;
var markerMarginRight = item.name.startsWith('MA') || !item.value ? 5 : 9;
var markerMarginLeft = item.name.startsWith('MA') || !item.value ? 0 : 2;
return "\n <li style=\"display: flex; align-items: center; margin-bottom: 10px;\">\n <div>\n <span\n style=\"\n background-color: " + item.color + ";\n width: " + size + "px;\n height: " + size + "px;\n border-radius: 50%;\n display: inline-block;\n margin-right: " + markerMarginRight + "px;\n margin-left: " + markerMarginLeft + "px;\n \"></span>\n </div>\n <div style=\"display: flex; justify-content: space-between; width: 100%;\">\n <span style=\"margin-right: 15px;\">" + item.name + "</span>\n <span>" + (item.name.startsWith('MA') && item.value === '0' ? '-' : item.value) + "</span>\n </div>\n </li>\n ";
};
var generateCustomTooltipContent = function (title, items) {
return "\n <div style=\"padding: 10px 0;\">\n <div style=\"margin-bottom: 10px;\">" + title + "</div>\n <ul style=\"list-style: none; padding: 0;\">\n " + items.map(formatTooltipItem).join('') + "\n </ul>\n </div>\n ";
};
var stockOption = __assign(__assign({}, stockPlot.options), { tooltip: {
showMarkers: true,
showCrosshairs: true,
showNil: true,
crosshairs: {
follow: true,
text: function (axisType, value, data) {
if (axisType === 'y') {
return { content: value ? value.toFixed(0) : value };
}
return { content: data[0].title, position: 'end' };
}
},
showContent: true,
customItems: customTooltipItems,
customContent: generateCustomTooltipContent
} });
newPlots.push(__assign(__assign({}, stockPlot), { options: stockOption }));
return __assign(__assign({}, options), { plots: newPlots });
};
StockLine.prototype.configXAxis = function (chart, options) {
var xAxisOptions = _super.prototype.configXAxis.call(this, chart, options);
if (!xAxisOptions) {
return options;
}
var newPlots = [];
var linePlotList = options.plots.filter(function (item) { return item.type === 'line'; });
var stockPlot = options.plots.filter(function (item) { return item.type === 'stock'; })[0];
var newStockPlot = __assign(__assign({}, stockPlot), { options: __assign(__assign({}, stockPlot.options), { xAxis: xAxisOptions['xAxis']
? __assign(__assign({}, stockPlot.options['xAxis']), xAxisOptions['xAxis']) : {
label: false,
line: null
} }) });
newPlots.push(newStockPlot);
linePlotList.forEach(function (item) {
newPlots.push(item);
});
return __assign(__assign({}, options), { plots: newPlots });
};
StockLine.prototype.configYAxis = function (chart, options) {
var yAxisOptions = _super.prototype.configYAxis.call(this, chart, options);
if (!yAxisOptions) {
return options;
}
var yAxis = util_1.parseJson(chart.customStyle).yAxis;
var newPlots = [];
var linePlotList = options.plots.filter(function (item) { return item.type === 'line'; });
var stockPlot = options.plots.filter(function (item) { return item.type === 'stock'; })[0];
var label = false;
if (yAxisOptions['yAxis'].label) {
label = __assign(__assign({}, yAxisOptions['yAxis'].label), { formatter: function (value) {
return formatter_1.valueFormatter(value, yAxis.axisLabelFormatter);
} });
}
var newStockPlot = __assign(__assign({}, stockPlot), { options: __assign(__assign({}, stockPlot.options), { yAxis: label
? __assign(__assign(__assign({}, stockPlot.options['yAxis']), yAxisOptions['yAxis']), { label: label }) : __assign(__assign({}, yAxisOptions['yAxis']), { label: label, grid: null, line: null }) }) });
newPlots.push(newStockPlot);
linePlotList.forEach(function (item) {
newPlots.push(item);
});
return __assign(__assign({}, options), { plots: newPlots });
};
StockLine.prototype.customConfigEmptyDataStrategy = function (chart, options) {
var data = options.data;
if (!(data === null || data === void 0 ? void 0 : data.length)) {
return options;
}
var strategy = util_1.parseJson(chart.senior).functionCfg.emptyDataStrategy;
if (strategy === 'ignoreData') {
var _loop_1 = function (i) {
var item = data[i];
Object.keys(item).forEach(function (key) {
if (key.startsWith('f_') && item[key] === null) {
data.splice(i, 1);
}
});
};
for (var i = data.length - 1; i >= 0; i--) {
_loop_1(i);
}
}
var updateValues = function (strategy, data) {
data.forEach(function (obj) {
Object.keys(obj).forEach(function (key) {
if (key.startsWith('f_') && obj[key] === null) {
obj[key] = strategy === 'breakLine' ? null : 0;
}
});
});
};
if (strategy === 'breakLine' || strategy === 'setZero') {
updateValues(strategy, data);
}
return options;
};
StockLine.prototype.configLegend = function (chart, options) {
var legend = {};
var customStyle;
var stockPlot = options.plots.filter(function (item) { return item.type === 'stock'; })[0];
if (chart.customStyle) {
customStyle = util_1.parseJson(chart.customStyle);
// legend
if (customStyle.legend) {
var l = JSON.parse(JSON.stringify(customStyle.legend));
if (l.show) {
legend = __assign(__assign({}, stockPlot.options.legend), { itemName: {
style: {
fill: l.color,
fontSize: l.fontSize
}
} });
}
else {
legend = false;
}
}
}
var newPlots = [];
var stockOption = __assign(__assign({}, stockPlot.options), { legend: legend });
var linePlotList = options.plots.filter(function (item) { return item.type === 'line'; });
linePlotList.forEach(function (item) {
newPlots.push(item);
});
newPlots.push(__assign(__assign({}, stockPlot), { options: stockOption }));
return __assign(__assign({}, options), { plots: newPlots });
};
StockLine.prototype.setupOptions = function (chart, options) {
return util_1.flow(this.configTheme, this.configBasicStyle, this.configXAxis, this.configYAxis, this.configTooltip, this.configLegend, this.customConfigEmptyDataStrategy)(chart, options);
};
return StockLine;
}(g2plot_1.G2PlotChartView));
exports.StockLine = StockLine;

View File

@ -10,10 +10,12 @@ import {
TOOLTIP_TPL
} from '../../common/common_antv'
import {
convertToAlphaColor,
flow,
getLineConditions,
getLineLabelColorByCondition,
hexColorToRGBA,
isAlphaColor,
parseJson,
setUpGroupSeriesColor
} from '@/data-visualization/chart/components/js/util'
@ -43,8 +45,10 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
'label-selector': ['seriesLabelVPosition', 'seriesLabelFormatter', 'showExtremum'],
'tooltip-selector': [
...LINE_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
]
'seriesTooltipFormatter',
'carousel'
],
'legend-selector': [...LINE_EDITOR_PROPERTY_INNER['legend-selector'], 'legendSort']
}
axis: AxisType[] = [...LINE_AXIS_TYPE, 'xAxisExt']
axisConfig = {
@ -66,8 +70,8 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
}
async drawChart(drawOptions: G2PlotDrawOptions<G2Line>): Promise<G2Line> {
const { chart, action, container } = drawOptions
if (!chart.data?.data?.length) {
chart.container = container
if (!chart.data?.data?.length) {
clearExtremum(chart)
return
}
@ -146,7 +150,7 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
fields: [],
...tmpOptions.label,
layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout,
formatter: (data: Datum, _point) => {
formatter: (data: Datum) => {
if (data.EXTREME) {
return ''
}
@ -321,17 +325,30 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
if (sort?.length) {
// 用值域限定排序,有可能出现新数据但是未出现在图表上,所以这边要遍历一下子维度,加到后面,让新数据显示出来
const data = optionTmp.data
data?.forEach(d => {
const cat = d['category']
if (cat && !sort.includes(cat)) {
sort.push(cat)
const cats =
data?.reduce((p, n) => {
const cat = n['category']
if (cat && !p.includes(cat)) {
p.push(cat)
}
})
return p
}, []) || []
const values = sort.reduce((p, n) => {
if (cats.includes(n)) {
const index = cats.indexOf(n)
if (index !== -1) {
cats.splice(index, 1)
}
p.push(n)
}
return p
}, [])
cats.length > 0 && values.push(...cats)
optionTmp.meta = {
...optionTmp.meta,
category: {
type: 'cat',
values: sort
values
}
}
}
@ -351,6 +368,56 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
fill: style.stroke
}
}
const { sort, customSort, icon } = customStyle.legend
if (sort && sort !== 'none' && chart.xAxisExt.length) {
const customAttr = parseJson(chart.customAttr)
const { basicStyle } = customAttr
const seriesMap =
basicStyle.seriesColor?.reduce((p, n) => {
p[n.id] = n
return p
}, {}) || {}
const dupCheck = new Set()
const items = optionTmp.data?.reduce((arr, item) => {
if (!dupCheck.has(item.category)) {
const fill =
seriesMap[item.category]?.color ??
optionTmp.color[dupCheck.size % optionTmp.color.length]
dupCheck.add(item.category)
arr.push({
name: item.category,
value: item.category,
marker: {
symbol: icon,
style: {
r: size,
fill: isAlphaColor(fill) ? fill : convertToAlphaColor(fill, basicStyle.alpha)
}
}
})
}
return arr
}, [])
if (sort !== 'custom') {
items.sort((a, b) => {
return sort !== 'desc' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name)
})
} else {
const tmp = []
;(customSort || []).forEach(item => {
const index = items.findIndex(i => i.name === item)
if (index !== -1) {
tmp.push(items[index])
items.splice(index, 1)
}
})
items.unshift(...tmp)
}
optionTmp.legend.items = items
if (xAxisExt?.customSort?.length > 0) {
delete optionTmp.meta?.category.values
}
}
return optionTmp
}
protected setupOptions(chart: Chart, options: LineOptions): LineOptions {

View File

@ -68,22 +68,22 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
const xAxis = chart.xAxis
const yAxis = chart.yAxis
// 时间字段
const xAxisDataeaseName = xAxis[0].dataeaseName
const xAxisgisbiName = xAxis[0].gisbiName
// 收盘价字段
const yAxisDataeaseName = yAxis[1].dataeaseName
const yAxisgisbiName = yAxis[1].gisbiName
const result = []
for (let i = 0; i < data.length; i++) {
if (i < dayCount) {
result.push({
[xAxisDataeaseName]: data[i][xAxisDataeaseName],
[xAxisgisbiName]: data[i][xAxisgisbiName],
value: null
})
} else {
const sum = data
.slice(i - dayCount + 1, i + 1)
.reduce((sum, item) => sum + item[yAxisDataeaseName], 0)
.reduce((sum, item) => sum + item[yAxisgisbiName], 0)
result.push({
[xAxisDataeaseName]: data[i][xAxisDataeaseName],
[xAxisgisbiName]: data[i][xAxisgisbiName],
value: parseFloat((sum / dayCount).toFixed(3))
})
}
@ -228,7 +228,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
const data = parseJson(chart.data?.tableRow)
// 时间字段
const xAxisDataeaseName = xAxis[0].dataeaseName
const xAxisgisbiName = xAxis[0].gisbiName
const averages = [5, 10, 20, 60, 120, 180]
const legendItems: any[] = [
{
@ -262,9 +262,9 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
// 将均线数据设置到主数据中
data.forEach((item: any) => {
const date = item[xAxisDataeaseName]
const date = item[xAxisgisbiName]
for (const [key, value] of averagesLineData) {
item[key] = value.find(m => m[xAxisDataeaseName] === date)?.value
item[key] = value.find(m => m[xAxisgisbiName] === date)?.value
}
})
@ -283,7 +283,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
top: true,
options: {
smooth: false,
xField: xAxisDataeaseName,
xField: xAxisgisbiName,
yField: key,
color: colors[index - 1],
xAxis: null,
@ -349,7 +349,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
options: {
meta: {
[xAxisDataeaseName]: {
[xAxisgisbiName]: {
mask: dateFormat
}
},
@ -363,12 +363,12 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
min: minValue,
max: maxValue
},
xField: xAxisDataeaseName,
xField: xAxisgisbiName,
yField: [
yAxis[0].dataeaseName,
yAxis[1].dataeaseName,
yAxis[2].dataeaseName,
yAxis[3].dataeaseName
yAxis[0].gisbiName,
yAxis[1].gisbiName,
yAxis[2].gisbiName,
yAxis[3].gisbiName
],
legend: {
position: 'top',
@ -384,7 +384,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
const plot = new MixClass(container, option)
this.registerEvent(data, plot, averagesLineData)
plot.on('schema:click', evt => {
const selectSchema = evt.data.data[xAxisDataeaseName]
const selectSchema = evt.data.data[xAxisgisbiName]
const paramData = parseJson(chart.data?.data)
const selectData = paramData.filter(item => item.field === selectSchema)
const quotaList = []
@ -440,7 +440,6 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
protected configTooltip(chart: Chart, options: MixOptions): MixOptions {
const tooltipAttr = parseJson(chart.customAttr).tooltip
const xAxis = chart.xAxis
const newPlots = []
const linePlotList = options.plots.filter(item => item.type === 'line')
linePlotList.forEach(item => {
@ -464,7 +463,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
const showFiled = chart.data.fields
const customTooltipItems = originalItems => {
const formattedItems = originalItems.map(item => {
const fieldObj = showFiled.find(q => q.dataeaseName === item.name)
const fieldObj = showFiled.find(q => q.gisbiName === item.name)
const displayName = fieldObj?.chartShowName || fieldObj?.name || item.name
const formattedName = displayName.startsWith('ma') ? displayName.toUpperCase() : displayName
tooltipAttr.tooltipFormatter.decimalCount = 3

View File

@ -74,7 +74,7 @@ export class Liquid extends G2PlotChartView<LiquidOptions, G2Liquid> {
})
// 处理空数据, 只要有一个指标是空数据,就不显示图表
const hasNoneData = chart.data?.series.some(s => !s.data?.[0])
this.configEmptyDataStyle(newChart, hasNoneData ? [] : [1], container)
this.configEmptyDataStyle(hasNoneData ? [] : [1], container, newChart)
if (hasNoneData) {
return
}

View File

@ -402,7 +402,8 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
content.push(name)
}
if (label.showQuota) {
areaMap[name] && content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter))
;(areaMap[name] || areaMap[name] === 0) &&
content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter))
}
item.properties['_DE_LABEL_'] = content.join('\n\n')
}

View File

@ -51,6 +51,29 @@ export const MAP_AXIS_TYPE: AxisType[] = [
'extTooltip'
]
export const gaodeMapStyleOptions = [
{ name: t('chart.map_style_normal'), value: 'normal' },
{ name: t('chart.map_style_darkblue'), value: 'darkblue' },
{ name: t('chart.map_style_light'), value: 'light' },
{ name: t('chart.map_style_dark'), value: 'dark' },
{ name: t('chart.map_style_fresh'), value: 'fresh' },
{ name: t('chart.map_style_grey'), value: 'grey' },
{ name: t('chart.map_style_blue'), value: 'blue' },
{ name: t('chart.map_style_translate'), value: 'Satellite' },
{ name: t('commons.custom'), value: 'custom' }
]
export const tdtMapStyleOptions = [
{ name: t('chart.map_style_normal'), value: 'normal' },
{ name: t('chart.map_style_dark'), value: 'black' },
{ name: t('chart.map_style_darkblue'), value: 'indigo' }
]
export const qqMapStyleOptions = [
{ name: t('chart.map_style_normal'), value: 'normal' },
{ name: t('commons.custom'), value: 'custom' }
]
export declare type MapMouseEvent = MouseEvent & {
feature: GeoJSON.Feature
}

View File

@ -0,0 +1,391 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
exports.__esModule = true;
exports.FlowMap = void 0;
var useI18n_1 = require("@/hooks/web/useI18n");
var l7_1 = require("@/views/chart/components/js/panel/types/impl/l7");
var common_1 = require("@/views/chart/components/js/panel/charts/map/common");
var util_1 = require("@/views/chart/components/js/util");
var utils_1 = require("@/utils/utils");
var l7_layers_1 = require("@antv/l7-layers");
var l7_layers_2 = require("@antv/l7-layers");
var common_antv_1 = require("@/views/chart/components/js/panel/common/common_antv");
var t = useI18n_1.useI18n().t;
/**
* 流向地图
*/
var FlowMap = /** @class */ (function (_super) {
__extends(FlowMap, _super);
function FlowMap() {
var _this = _super.call(this, 'flow-map', []) || this;
_this.properties = [
'background-overall-component',
'border-style',
'basic-style-selector',
'title-selector',
'flow-map-line-selector',
'flow-map-point-selector',
'bubble-animate'
];
_this.propertyInner = __assign(__assign({}, common_1.MAP_EDITOR_PROPERTY_INNER), { 'basic-style-selector': [
'mapBaseStyle',
'mapLineStyle',
'zoom',
'showLabel',
'autoFit',
'mapCenter',
'zoomLevel'
] });
_this.axis = ['xAxis', 'xAxisExt', 'filter', 'flowMapStartName', 'flowMapEndName', 'yAxis'];
_this.axisConfig = {
xAxis: {
name: t('chart.start_coordinates') + " / " + t('chart.dimension'),
type: 'd',
limit: 2
},
xAxisExt: {
name: t('chart.end_coordinates') + " / " + t('chart.dimension'),
type: 'd',
limit: 2
},
flowMapStartName: {
name: t('chart.start_name') + " / " + t('chart.dimension'),
type: 'd',
limit: 1,
allowEmpty: true
},
flowMapEndName: {
name: t('chart.end_name') + " / " + t('chart.dimension'),
type: 'd',
limit: 1,
allowEmpty: true
},
yAxis: {
name: t('chart.flow_map_line_width') + " / " + t('chart.quota'),
type: 'q',
limit: 1,
tooltip: t('chart.flow_map_line_width_tip'),
allowEmpty: true
}
};
_this.lineConfig = function (chart, xAxis, xAxisExt, basicStyle, misc) {
var _a;
var flowLineStyle = {
type: misc.flowMapConfig.lineConfig.mapLineType,
size: misc.flowMapConfig.lineConfig.mapLineType === 'line'
? misc.flowMapConfig.lineConfig.mapLineWidth / 2
: misc.flowMapConfig.lineConfig.mapLineWidth,
animate: misc.flowMapConfig.lineConfig.mapLineAnimate,
animateDuration: misc.flowMapConfig.lineConfig.mapLineAnimateDuration,
gradient: misc.flowMapConfig.lineConfig.mapLineGradient,
sourceColor: misc.flowMapConfig.lineConfig.mapLineSourceColor,
targetColor: misc.flowMapConfig.lineConfig.mapLineTargetColor,
alpha: misc.flowMapConfig.lineConfig.alpha
};
var colorsWithAlpha = basicStyle.colors.map(function (color) {
return util_1.hexColorToRGBA(color, misc.flowMapConfig.lineConfig.alpha);
});
flowLineStyle.sourceColor = colorsWithAlpha[0];
flowLineStyle.targetColor = colorsWithAlpha[1];
// 线条粗细
var lineWidthField = null;
var yAxis = utils_1.deepCopy(chart.yAxis);
if (yAxis.length > 0) {
lineWidthField = yAxis[0].gisbiName;
}
// 线条颜色
var lineColorField = null;
var yAxisExt = utils_1.deepCopy(chart.yAxisExt);
if (yAxisExt.length > 0) {
lineColorField = yAxisExt[0].gisbiName;
}
var asteriskField = '*';
var data = [];
(_a = chart.data) === null || _a === void 0 ? void 0 : _a.tableRow.forEach(function (item) {
var newKey = 'f_record';
var newObj = Object.keys(item).reduce(function (acc, key) {
if (key === asteriskField) {
acc[newKey] = item[key];
}
else {
acc[key] = item[key];
}
return acc;
}, {});
data.push(newObj);
});
var config = new l7_layers_1.LineLayer({
name: 'line',
blend: 'normal',
autoFit: !(basicStyle.autoFit === false)
})
.source(data, {
parser: {
type: 'json',
x: xAxis[0].gisbiName,
y: xAxis[1].gisbiName,
x1: xAxisExt[0].gisbiName,
y1: xAxisExt[1].gisbiName
}
})
.size(flowLineStyle.size)
.shape(flowLineStyle.type)
.animate({
enable: flowLineStyle.animate,
duration: flowLineStyle.animateDuration,
interval: 1,
trailLength: 1
});
if (lineWidthField) {
config.size(lineWidthField === asteriskField ? 'f_record' : lineWidthField, [1, 10]);
}
if (lineColorField) {
config.style({
opacity: flowLineStyle.alpha / 100
});
config.color(lineColorField);
}
else {
if (flowLineStyle.gradient) {
config.style({
sourceColor: flowLineStyle.sourceColor,
targetColor: flowLineStyle.targetColor,
opacity: flowLineStyle.alpha / 100
});
}
else {
config
.style({
opacity: flowLineStyle.alpha / 100
})
.color(flowLineStyle.sourceColor);
}
}
return config;
};
_this.startAndEndNameConfig = function (chart, xAxis, xAxisExt, misc, configList) {
var _a, _b;
var flowMapStartName = utils_1.deepCopy(chart.flowMapStartName);
var flowMapEndName = utils_1.deepCopy(chart.flowMapEndName);
var textColor = misc.flowMapConfig.pointConfig.text.color;
var textFontSize = misc.flowMapConfig.pointConfig.text.fontSize;
var has = new Map();
if ((flowMapStartName === null || flowMapStartName === void 0 ? void 0 : flowMapStartName.length) > 0) {
var startTextLayer = new l7_layers_2.PointLayer()
.source((_a = chart.data) === null || _a === void 0 ? void 0 : _a.tableRow, {
parser: {
type: 'json',
x: xAxis[0].gisbiName,
y: xAxis[1].gisbiName
}
})
.shape(flowMapStartName[0].gisbiName, function (args) {
if (has.has('from-' + args)) {
return '';
}
has.set('from-' + args, args);
return args;
})
.size(textFontSize)
.color(textColor)
.style({
textAnchor: 'top',
textOffset: [0, 0],
spacing: 2,
padding: [1, 1],
textAllowOverlap: true,
fontFamily: chart.fontFamily ? chart.fontFamily : undefined
});
configList.push(startTextLayer);
}
if ((flowMapEndName === null || flowMapEndName === void 0 ? void 0 : flowMapEndName.length) > 0) {
var endTextLayer = new l7_layers_2.PointLayer()
.source((_b = chart.data) === null || _b === void 0 ? void 0 : _b.tableRow, {
parser: {
type: 'json',
x: xAxisExt[0].gisbiName,
y: xAxisExt[1].gisbiName
}
})
.shape(flowMapEndName[0].gisbiName, function (args) {
if (has.has('from-' + args) || has.has('to-' + args)) {
return '';
}
has.set('to-' + args, args);
return args;
})
.size(textFontSize)
.color(textColor)
.style({
textAnchor: 'top',
textOffset: [0, 0],
spacing: 2,
padding: [1, 1],
textAllowOverlap: true,
fontFamily: chart.fontFamily ? chart.fontFamily : undefined
});
configList.push(endTextLayer);
}
};
_this.pointConfig = function (chart, xAxis, xAxisExt, misc, configList) {
var _a, _b;
var color = misc.flowMapConfig.pointConfig.point.color;
var size = misc.flowMapConfig.pointConfig.point.size;
var bubbleCfg = util_1.parseJson(chart.senior).bubbleCfg;
var fromDefaultPointLayer = new l7_layers_2.PointLayer({ zIndex: -1 })
.source((_a = chart.data) === null || _a === void 0 ? void 0 : _a.tableRow, {
parser: {
type: 'json',
x: xAxis[0].gisbiName,
y: xAxis[1].gisbiName
}
})
.shape('circle')
.size(size)
.color(color)
.style({
blur: 0.6
});
var toDefaultPointLayer = new l7_layers_2.PointLayer({ zIndex: -1 })
.source((_b = chart.data) === null || _b === void 0 ? void 0 : _b.tableRow, {
parser: {
type: 'json',
x: xAxisExt[0].gisbiName,
y: xAxisExt[1].gisbiName
}
})
.shape('circle')
.size(size)
.color(color)
.style({
blur: 0.6
});
if (bubbleCfg && bubbleCfg.enable) {
var animate = {
enable: true,
speed: bubbleCfg.speed,
rings: bubbleCfg.rings
};
fromDefaultPointLayer.size(size * 2);
fromDefaultPointLayer.animate(animate);
toDefaultPointLayer.size(size * 2);
toDefaultPointLayer.animate(animate);
}
configList.push(fromDefaultPointLayer);
configList.push(toDefaultPointLayer);
};
return _this;
}
FlowMap.prototype.drawChart = function (drawOption) {
var _a;
return __awaiter(this, void 0, void 0, function () {
var chart, container, containerDom, rect, xAxis, xAxisExt, _b, basicStyle, misc, mapKey, mapStyle, chartObj, scene, center, configList, i;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
chart = drawOption.chart, container = drawOption.container;
containerDom = document.getElementById(container);
rect = containerDom === null || containerDom === void 0 ? void 0 : containerDom.getBoundingClientRect();
if ((rect === null || rect === void 0 ? void 0 : rect.height) <= 0) {
return [2 /*return*/, new l7_1.L7Wrapper((_a = drawOption.chartObj) === null || _a === void 0 ? void 0 : _a.getScene(), [])];
}
xAxis = utils_1.deepCopy(chart.xAxis);
xAxisExt = utils_1.deepCopy(chart.xAxisExt);
_b = utils_1.deepCopy(util_1.parseJson(chart.customAttr)), basicStyle = _b.basicStyle, misc = _b.misc;
return [4 /*yield*/, this.getMapKey()];
case 1:
mapKey = _c.sent();
mapStyle = common_antv_1.getMapStyle(mapKey, basicStyle);
chartObj = drawOption.chartObj;
scene = chartObj === null || chartObj === void 0 ? void 0 : chartObj.getScene();
center = common_antv_1.getMapCenter(basicStyle);
return [4 /*yield*/, common_antv_1.getMapScene(chart, scene, container, mapKey, basicStyle, misc, mapStyle, center)];
case 2:
scene = _c.sent();
this.configZoomButton(chart, scene, mapKey);
if ((xAxis === null || xAxis === void 0 ? void 0 : xAxis.length) < 2 || (xAxisExt === null || xAxisExt === void 0 ? void 0 : xAxisExt.length) < 2) {
return [2 /*return*/, new l7_1.L7Wrapper(scene, undefined)];
}
configList = [];
configList.push(this.lineConfig(chart, xAxis, xAxisExt, basicStyle, misc));
this.startAndEndNameConfig(chart, xAxis, xAxisExt, misc, configList);
this.pointConfig(chart, xAxis, xAxisExt, misc, configList);
configList[0].once('inited', function () {
common_antv_1.mapRendered(container);
});
for (i = 0; i < configList.length; i++) {
configList[i].on('inited', function () {
common_antv_1.qqMapRendered(scene);
});
}
return [2 /*return*/, new l7_1.L7Wrapper(scene, configList)];
}
});
});
};
FlowMap.prototype.setupDefaultOptions = function (chart) {
chart.customAttr.misc.flowMapConfig.lineConfig.mapLineAnimate = true;
return chart;
};
return FlowMap;
}(l7_1.L7ChartView));
exports.FlowMap = FlowMap;

View File

@ -0,0 +1,632 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
exports.__esModule = true;
exports.SymbolicMap = void 0;
var useI18n_1 = require("@/hooks/web/useI18n");
var l7_1 = require("@/views/chart/components/js/panel/types/impl/l7");
var common_1 = require("@/views/chart/components/js/panel/charts/map/common");
var util_1 = require("@/views/chart/components/js/util");
var utils_1 = require("@/utils/utils");
var l7_layers_1 = require("@antv/l7-layers");
var l7_2 = require("@antv/l7");
var common_antv_1 = require("@/views/chart/components/js/panel/common/common_antv");
var tooltip_carousel_1 = require("@/views/chart/components/js/panel/charts/map/tooltip-carousel");
var lodash_es_1 = require("lodash-es");
var t = useI18n_1.useI18n().t;
/**
* 符号地图
*/
var SymbolicMap = /** @class */ (function (_super) {
__extends(SymbolicMap, _super);
function SymbolicMap() {
var _this = _super.call(this, 'symbolic-map', []) || this;
_this.properties = [
'background-overall-component',
'border-style',
'basic-style-selector',
'symbolic-style-selector',
'title-selector',
'label-selector',
'tooltip-selector',
'threshold'
];
_this.propertyInner = __assign(__assign({}, common_1.MAP_EDITOR_PROPERTY_INNER), { 'basic-style-selector': [
'colors',
'alpha',
'mapBaseStyle',
'zoom',
'showLabel',
'autoFit',
'mapCenter',
'zoomLevel'
], 'symbolic-style-selector': ['symbolicMapStyle'], 'label-selector': ['color', 'fontSize', 'showFields', 'customContent'], 'tooltip-selector': [
'color',
'fontSize',
'showFields',
'customContent',
'show',
'backgroundColor',
'carousel'
], threshold: ['lineThreshold'] });
_this.axis = ['xAxis', 'xAxisExt', 'extBubble', 'filter', 'extLabel', 'extTooltip'];
_this.axisConfig = {
xAxis: {
name: t('chart.symbolic_map_coordinates') + " / " + t('chart.dimension'),
type: 'd',
limit: 2
},
xAxisExt: {
name: t('chart.color') + " / " + t('chart.dimension'),
type: 'd',
limit: 1,
allowEmpty: true
},
extBubble: {
name: t('chart.bubble_size') + " / " + t('chart.quota'),
type: 'q',
limit: 1,
tooltip: t('chart.symbolic_map_bubble_size_tip'),
allowEmpty: true
}
};
/**
* 构建符号图层
* @param chart
*/
_this.buildSymbolicLayer = function (chart, scene) { return __awaiter(_this, void 0, void 0, function () {
var basicStyle, xAxis, xAxisExt, extBubble, _a, mapSymbolOpacity, mapSymbolSize, mapSymbol, mapSymbolStrokeWidth, colors, alpha, mapSymbolSizeMin, mapSymbolSizeMax, colorsWithAlpha, colorIndex, colorAssignments, sizeKey, threshold, conditions, extBubbleIds, baseColor, baseColorList, data, pointLayer, parser, index, color, fillRegex, svgStr, doc, svgEle, parser, color, fillRegex, svgStr, doc, svgEle;
var _b, _c, _d;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
basicStyle = util_1.parseJson(chart.customAttr).basicStyle;
xAxis = utils_1.deepCopy(chart.xAxis);
xAxisExt = utils_1.deepCopy(chart.xAxisExt);
extBubble = utils_1.deepCopy(chart.extBubble);
_a = utils_1.deepCopy(basicStyle), mapSymbolOpacity = _a.mapSymbolOpacity, mapSymbolSize = _a.mapSymbolSize, mapSymbol = _a.mapSymbol, mapSymbolStrokeWidth = _a.mapSymbolStrokeWidth, colors = _a.colors, alpha = _a.alpha, mapSymbolSizeMin = _a.mapSymbolSizeMin, mapSymbolSizeMax = _a.mapSymbolSizeMax;
colorsWithAlpha = colors.map(function (color) { return util_1.hexColorToRGBA(color, alpha); });
colorIndex = 0;
colorAssignments = new Map();
sizeKey = extBubble.length > 0 ? extBubble[0].gisbiName : '';
threshold = util_1.parseJson(chart.senior).threshold;
conditions = [];
if (threshold.enable) {
conditions = (_b = threshold.lineThreshold) !== null && _b !== void 0 ? _b : [];
}
extBubbleIds = chart.extBubble.map(function (i) { return i.id; });
conditions = lodash_es_1.filter(conditions, function (c) { return extBubbleIds.includes(c.fieldId); });
baseColor = colorsWithAlpha[0];
baseColorList = [];
data = ((_c = chart.data) === null || _c === void 0 ? void 0 : _c.tableRow) ? chart.data.tableRow.map(function (item, index) {
var _a, _b;
item['_index'] = '_index' + index;
// 颜色标识
var identifier = item[(_a = xAxisExt[0]) === null || _a === void 0 ? void 0 : _a.gisbiName];
// 检查该标识是否已有颜色分配,如果没有则分配
var color = colorAssignments.get(identifier);
if (!color) {
color = colorsWithAlpha[colorIndex++ % colorsWithAlpha.length];
// 记录分配的颜色
colorAssignments.set(identifier, color);
}
baseColorList[index] = color;
if (conditions.length > 0) {
for (var i = 0; i < conditions.length; i++) {
var c = conditions[i];
var value = item[c.field.gisbiName];
for (var _i = 0, _c = c.conditions; _i < _c.length; _i++) {
var t_1 = _c[_i];
var v = t_1.value;
//保存一下颜色到map
var _color = util_1.getColorFormAlphaColor(t_1.color);
if (t_1.term === 'between') {
var start = parseFloat(t_1.min);
var end = parseFloat(t_1.max);
if (start <= value && value <= end) {
color = util_1.hexColorToRGBA(_color, alpha);
baseColorList[index] = color;
}
}
else if ('lt' === t_1.term) {
if (value < v) {
color = util_1.hexColorToRGBA(_color, alpha);
baseColorList[index] = color;
}
}
else if ('le' === t_1.term) {
if (value <= v) {
color = util_1.hexColorToRGBA(_color, alpha);
baseColorList[index] = color;
}
}
else if ('gt' === t_1.term) {
if (value > v) {
color = util_1.hexColorToRGBA(_color, alpha);
baseColorList[index] = color;
}
}
else if ('ge' === t_1.term) {
if (value >= v) {
color = util_1.hexColorToRGBA(_color, alpha);
baseColorList[index] = color;
}
}
else if ('eq' === t_1.term) {
if (value === v) {
color = util_1.hexColorToRGBA(_color, alpha);
baseColorList[index] = color;
}
}
else if ('not_eq' === t_1.term) {
if (value !== v) {
color = util_1.hexColorToRGBA(_color, alpha);
baseColorList[index] = color;
}
}
}
}
}
return __assign(__assign({}, item), { color: color, size: (_b = parseInt(item[sizeKey])) !== null && _b !== void 0 ? _b : mapSymbolSize, name: identifier });
})
: [];
pointLayer = new l7_layers_1.PointLayer({ autoFit: !(basicStyle.autoFit === false) })
.source(data, {
parser: {
type: 'json',
x: xAxis[0].gisbiName,
y: xAxis[1].gisbiName
}
})
.active(true);
if (!((_d = xAxisExt[0]) === null || _d === void 0 ? void 0 : _d.gisbiName)) return [3 /*break*/, 10];
if (!(basicStyle.mapSymbol === 'custom' && basicStyle.customIcon)) return [3 /*break*/, 8];
if (!basicStyle.customIcon.startsWith('data')) return [3 /*break*/, 2];
scene.removeImage('customIcon');
return [4 /*yield*/, scene.addImage('customIcon', basicStyle.customIcon)];
case 1:
_e.sent();
pointLayer.shape('customIcon');
return [3 /*break*/, 7];
case 2:
parser = new DOMParser();
index = 0;
_e.label = 3;
case 3:
if (!(index < Math.min(baseColorList.length, colorIndex + 1))) return [3 /*break*/, 6];
color = baseColorList[index];
fillRegex = /(fill="[^"]*")/g;
svgStr = basicStyle.customIcon.replace(fillRegex, '');
doc = parser.parseFromString(svgStr, 'image/svg+xml');
svgEle = doc.documentElement;
svgEle.setAttribute('fill', color);
scene.removeImage("icon-" + color);
return [4 /*yield*/, scene.addImage("icon-" + color, util_1.svgStrToUrl(svgEle.outerHTML))];
case 4:
_e.sent();
_e.label = 5;
case 5:
index++;
return [3 /*break*/, 3];
case 6:
pointLayer.shape('color', function (c) {
return "icon-" + c;
});
_e.label = 7;
case 7: return [3 /*break*/, 9];
case 8:
pointLayer.shape(mapSymbol).color('_index', baseColorList);
pointLayer.style({
stroke: {
field: 'color'
},
strokeWidth: mapSymbolStrokeWidth,
opacity: mapSymbolOpacity / 10
});
_e.label = 9;
case 9: return [3 /*break*/, 16];
case 10:
if (!(basicStyle.mapSymbol === 'custom' && basicStyle.customIcon)) return [3 /*break*/, 15];
scene.removeImage('customIcon');
if (!basicStyle.customIcon.startsWith('data')) return [3 /*break*/, 12];
return [4 /*yield*/, scene.addImage('customIcon', basicStyle.customIcon)];
case 11:
_e.sent();
pointLayer.shape('customIcon');
return [3 /*break*/, 14];
case 12:
parser = new DOMParser();
color = baseColor;
fillRegex = /(fill="[^"]*")/g;
svgStr = basicStyle.customIcon.replace(fillRegex, '');
doc = parser.parseFromString(svgStr, 'image/svg+xml');
svgEle = doc.documentElement;
svgEle.setAttribute('fill', color);
return [4 /*yield*/, scene.addImage("customIcon", util_1.svgStrToUrl(svgEle.outerHTML))];
case 13:
_e.sent();
pointLayer.shape('customIcon');
_e.label = 14;
case 14: return [3 /*break*/, 16];
case 15:
pointLayer
.shape(mapSymbol)
.color('_index', baseColorList)
.style({
stroke: {
field: 'color'
},
strokeWidth: mapSymbolStrokeWidth,
opacity: mapSymbolOpacity / 10
});
_e.label = 16;
case 16:
if (sizeKey) {
pointLayer.size('size', [mapSymbolSizeMin, mapSymbolSizeMax]);
}
else {
pointLayer.size(mapSymbolSize);
}
return [2 /*return*/, pointLayer];
}
});
}); };
/**
* 合并详情到 map
* @param details
* @returns {Map<string, any>}
*/
_this.mergeDetailsToMap = function (details) {
var resultMap = new Map();
details.forEach(function (item) {
Object.entries(item).forEach(function (_a) {
var key = _a[0], value = _a[1];
if (resultMap.has(key)) {
var existingValue = resultMap.get(key);
if (existingValue !== value) {
resultMap.set(key, existingValue + ", " + value);
}
}
else {
resultMap.set(key, value);
}
});
});
return resultMap;
};
/**
* 清除 popup
* @param container
*/
_this.clearPopup = function (container) {
var containerElement = document.getElementById(container);
containerElement === null || containerElement === void 0 ? void 0 : containerElement.querySelectorAll('.l7-popup').forEach(function (element) { return element.remove(); });
};
/**
* 构建 tooltip
* @param chart
* @param pointLayer
*/
_this.buildTooltip = function (chart, container, pointLayer, scene) {
var _a, _b;
var customAttr = chart.customAttr ? util_1.parseJson(chart.customAttr) : null;
_this.clearPopup(container);
if ((_a = customAttr === null || customAttr === void 0 ? void 0 : customAttr.tooltip) === null || _a === void 0 ? void 0 : _a.show) {
var tooltip_1 = utils_1.deepCopy(customAttr).tooltip;
var showFields_1 = tooltip_1.showFields || [];
if (!tooltip_1.showFields || tooltip_1.showFields.length === 0) {
showFields_1 = __spreadArrays(chart.xAxisExt.map(function (i) { return i.gisbiName + "@" + i.name; }), chart.xAxis.map(function (i) { return i.gisbiName + "@" + i.name; }));
}
// 修改背景色
var styleId = 'tooltip-' + container;
var styleElement = document.getElementById(styleId);
if (styleElement) {
styleElement.remove();
(_b = styleElement.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(styleElement);
}
var style = document.createElement('style');
style.id = styleId;
style.innerHTML = "\n #" + container + " .l7-popup-content {\n background-color: " + tooltip_1.backgroundColor + " !important;\n padding: 6px 10px 6px;\n line-height: 1.6;\n border-top-left-radius: 3px;\n }\n #" + container + " .l7-popup-tip {\n border-top-color: " + tooltip_1.backgroundColor + " !important;\n }\n ";
document.head.appendChild(style);
var htmlPrefix_1 = "<div style='font-size:" + tooltip_1.fontSize + "px;color:" + tooltip_1.color + ";font-family: " + chart.fontFamily + "'>";
var htmlSuffix_1 = '</div>';
var containerElement_1 = document.getElementById(container);
if (containerElement_1) {
containerElement_1.addEventListener('mousemove', function (event) {
var rect = containerElement_1.getBoundingClientRect();
var mouseX = event.clientX - rect.left;
var mouseY = event.clientY - rect.top;
var tooltipElement = containerElement_1.getElementsByClassName('l7-popup');
for (var i = 0; i < (tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.length); i++) {
var element = tooltipElement[i];
element.firstElementChild.style.display = 'none';
element.style.transform = 'translate(15px, 12px)';
var isNearRightEdge = containerElement_1.clientWidth - mouseX <= element.clientWidth + 10;
var isNearBottomEdge = containerElement_1.clientHeight - mouseY <= element.clientHeight;
var transform = '';
if (isNearRightEdge) {
transform += 'translateX(-120%) translateY(15%) ';
}
if (isNearBottomEdge) {
transform += 'translateX(15%) translateY(-80%) ';
}
if (transform) {
element.style.transform = transform.trim();
}
}
});
}
pointLayer.on('touchend', function (e) {
var _a;
if (e.lngLat) {
var fieldData = __assign(__assign({}, e.feature), Object.fromEntries(_this.mergeDetailsToMap((_a = e.feature.details) !== null && _a !== void 0 ? _a : [])));
var content = _this.buildTooltipContent(tooltip_1, fieldData, showFields_1);
var popup = new l7_2.Popup({
lngLat: e.lngLat,
title: '',
closeButton: false,
closeOnClick: true,
html: "" + htmlPrefix_1 + content + htmlSuffix_1
});
scene.addPopup(popup);
}
});
return new l7_2.LayerPopup({
anchor: 'top-left',
className: 'l7-popup-' + container,
items: [
{
layer: pointLayer,
customContent: function (item) {
var fieldData = __assign(__assign({}, item), Object.fromEntries(_this.mergeDetailsToMap(item.details)));
var content = _this.buildTooltipContent(tooltip_1, fieldData, showFields_1);
return "" + htmlPrefix_1 + content + htmlSuffix_1;
}
}
],
trigger: 'hover'
});
}
return undefined;
};
/**
* 构建 tooltip 内容
* @param tooltip
* @param fieldData
* @param showFields
* @returns {string}
*/
_this.buildTooltipContent = function (tooltip, fieldData, showFields) {
var content = "";
if (tooltip.customContent) {
content = tooltip.customContent;
showFields.forEach(function (field) {
content = content.replace("${" + field.split('@')[1] + "}", fieldData[field.split('@')[0]]);
});
}
else {
showFields.forEach(function (field) {
content += "<span style=\"margin-bottom: 4px\">" + field.split('@')[1] + ": " + fieldData[field.split('@')[0]] + "</span><br>";
});
}
return content.replace(/\n/g, '<br>');
};
/**
* 构建 label
* @param chart
* @param configList
*/
_this.buildLabel = function (chart, configList) {
var _a, _b;
var xAxis = utils_1.deepCopy(chart.xAxis);
var customAttr = chart.customAttr ? util_1.parseJson(chart.customAttr) : null;
if ((_a = customAttr === null || customAttr === void 0 ? void 0 : customAttr.label) === null || _a === void 0 ? void 0 : _a.show) {
var label_1 = customAttr.label;
var data = ((_b = chart.data) === null || _b === void 0 ? void 0 : _b.tableRow) || [];
var showFields_2 = label_1.showFields || [];
if (!label_1.showFields || label_1.showFields.length === 0) {
showFields_2 = __spreadArrays(chart.xAxisExt.map(function (i) { return i.gisbiName + "@" + i.name; }), chart.xAxis.map(function (i) { return i.gisbiName + "@" + i.name; }));
}
data.forEach(function (item) {
var fieldData = __assign(__assign({}, item), Object.fromEntries(_this.mergeDetailsToMap(item.details)));
var content = label_1.customContent || '';
if (content) {
showFields_2.forEach(function (field) {
var _a = field.split('@'), fieldKey = _a[0], fieldName = _a[1];
content = content.replace("${" + fieldName + "}", fieldData[fieldKey]);
});
}
else {
content = showFields_2.map(function (field) { return fieldData[field.split('@')[0]]; }).join(',');
}
content = content.replace(/\n/g, '');
item.textLayerContent = content;
});
configList.push(new l7_layers_1.PointLayer()
.source(data, {
parser: {
type: 'json',
x: xAxis[0].gisbiName,
y: xAxis[1].gisbiName
}
})
.shape('textLayerContent', 'text')
.color(label_1.color)
.size(label_1.fontSize)
.style({
textAllowOverlap: label_1.fullDisplay,
textAnchor: 'center',
textOffset: [0, 0],
fontFamily: chart.fontFamily ? chart.fontFamily : undefined
}));
}
};
return _this;
}
SymbolicMap.prototype.drawChart = function (drawOption) {
var _a, _b, _c, _d, _e, _f, _g, _h;
return __awaiter(this, void 0, void 0, function () {
var chart, container, action, containerDom, rect, xAxis, basicStyle, miscStyle, mapKey, mapStyle, center, lng, lat, chartObj, scene, configList, symbolicLayer, tooltipLayer;
return __generator(this, function (_j) {
switch (_j.label) {
case 0:
chart = drawOption.chart, container = drawOption.container, action = drawOption.action;
containerDom = document.getElementById(container);
rect = containerDom === null || containerDom === void 0 ? void 0 : containerDom.getBoundingClientRect();
if ((rect === null || rect === void 0 ? void 0 : rect.height) <= 0) {
return [2 /*return*/, new l7_1.L7Wrapper((_a = drawOption.chartObj) === null || _a === void 0 ? void 0 : _a.getScene(), [])];
}
xAxis = utils_1.deepCopy(chart.xAxis);
if (chart.customAttr) {
basicStyle = util_1.parseJson(chart.customAttr).basicStyle;
miscStyle = util_1.parseJson(chart.customAttr).misc;
}
return [4 /*yield*/, this.getMapKey()];
case 1:
mapKey = _j.sent();
mapStyle = common_antv_1.getMapStyle(mapKey, basicStyle);
center = common_antv_1.getMapCenter(basicStyle);
// 联动时,聚焦到数据点,多个取第一个
if (((_c = (_b = chart.chartExtRequest) === null || _b === void 0 ? void 0 : _b.linkageFilters) === null || _c === void 0 ? void 0 : _c.length) &&
(xAxis === null || xAxis === void 0 ? void 0 : xAxis.length) === 2 && ((_d = chart.data) === null || _d === void 0 ? void 0 : _d.tableRow.length)) {
lng = (_f = (_e = chart.data) === null || _e === void 0 ? void 0 : _e.tableRow) === null || _f === void 0 ? void 0 : _f[0][chart.xAxis[0].gisbiName];
lat = (_h = (_g = chart.data) === null || _g === void 0 ? void 0 : _g.tableRow) === null || _h === void 0 ? void 0 : _h[0][chart.xAxis[1].gisbiName];
center = [lng, lat];
}
chartObj = drawOption.chartObj;
scene = chartObj === null || chartObj === void 0 ? void 0 : chartObj.getScene();
return [4 /*yield*/, common_antv_1.getMapScene(chart, scene, container, mapKey, basicStyle, miscStyle, mapStyle, center)];
case 2:
scene = _j.sent();
this.configZoomButton(chart, scene, mapKey);
if ((xAxis === null || xAxis === void 0 ? void 0 : xAxis.length) < 2) {
return [2 /*return*/, new l7_1.L7Wrapper(scene, undefined)];
}
configList = [];
return [4 /*yield*/, this.buildSymbolicLayer(chart, scene)];
case 3:
symbolicLayer = _j.sent();
configList.push(symbolicLayer);
tooltipLayer = this.buildTooltip(chart, container, symbolicLayer, scene);
if (tooltipLayer) {
scene.addPopup(tooltipLayer);
}
this.buildLabel(chart, configList);
symbolicLayer.once('inited', function () {
common_antv_1.mapRendered(container);
});
symbolicLayer.on('inited', function () {
chart.container = container;
tooltip_carousel_1.configCarouselTooltip(chart, symbolicLayer, symbolicLayer.sourceOption.data, scene);
common_antv_1.qqMapRendered(scene);
});
symbolicLayer.on('click', function (ev) {
var data = ev.feature;
var dimensionList = [];
var quotaList = [];
chart.data.fields.forEach(function (item, index) {
Object.keys(data).forEach(function (key) {
if (key.startsWith('f_') && item.gisbiName === key) {
if (index === 0) {
dimensionList.push({
id: item.id,
gisbiName: item.gisbiName,
value: data[key]
});
}
else {
quotaList.push({
id: item.id,
gisbiName: item.gisbiName,
value: data[key]
});
}
}
});
});
action({
x: ev.x,
y: ev.y,
data: {
data: __assign(__assign({}, data), { value: quotaList[0].value, name: dimensionList[0].id, dimensionList: dimensionList, quotaList: quotaList })
}
});
});
return [2 /*return*/, new l7_1.L7Wrapper(scene, configList)];
}
});
});
};
SymbolicMap.prototype.setupDefaultOptions = function (chart) {
chart.customAttr.label = __assign(__assign({}, chart.customAttr.label), { show: false });
chart.customAttr.basicStyle = __assign(__assign({}, chart.customAttr.basicStyle), { mapSymbolOpacity: 5, mapStyle: 'normal' });
return chart;
};
return SymbolicMap;
}(l7_1.L7ChartView));
exports.SymbolicMap = SymbolicMap;

View File

@ -0,0 +1,558 @@
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
exports.__esModule = true;
exports.CarouselManager = exports.carouselManagerInstances = exports.configCarouselTooltip = void 0;
var l7_1 = require("@antv/l7");
var isEmpty_1 = require("lodash-es/isEmpty");
var formatter_1 = require("@/views/chart/components/js/formatter");
var util_1 = require("@/views/chart/components/js/util");
var utils_1 = require("@/utils/utils");
exports.configCarouselTooltip = function (chart, view, data, scene, customSubArea, drawOption) {
var _a;
if (['bubble-map', 'map'].includes(chart.type)) {
data = (_a = view.source.data.dataArray) === null || _a === void 0 ? void 0 : _a.filter(function (i) { var _a; return ((_a = i.dimensionList) === null || _a === void 0 ? void 0 : _a.length) > 0; }).reduce(function (acc, current) {
var existingItem = acc.find(function (obj) {
var _a;
if ((_a = drawOption === null || drawOption === void 0 ? void 0 : drawOption.areaId) === null || _a === void 0 ? void 0 : _a.startsWith('custom_')) {
return obj.areaName === current.areaName;
}
else {
return obj.name === current.name || (obj.adcode && obj.adcode === current.adcode);
}
});
if (!existingItem) {
acc.push(current);
}
return acc;
}, []);
}
if (exports.carouselManagerInstances[chart.container]) {
var instances = exports.carouselManagerInstances[chart.container];
instances.update(scene, chart, view, data, customSubArea, drawOption);
}
else {
new CarouselManager(scene, chart, view, data, customSubArea, drawOption);
}
};
exports.carouselManagerInstances = {};
/**
* 轮播管理类
*/
var CarouselManager = /** @class */ (function () {
function CarouselManager(scene, chart, view, data, customSubArea, drawOption) {
var _this = this;
/**
* 停留时长定时器
* @private
*/
this.popupTimeoutId = null;
/**
* 轮播间隔定时器
* @private
*/
this.popupIntervalId = null;
/**
* 是否暂停轮播
* @private
*/
this.isPaused = false;
/**
* 当前显示的数据索引
* @private
*/
this.currentIndex = 0;
this.handleVisibilityChange = function () {
if (document.hidden) {
_this.clearPreviousInstance(_this.chart.container);
}
else {
_this.startCarouselPopups();
}
};
/**
* 鼠标移入暂停轮播
*/
this.pauseCarouselPopups = function () {
var _a;
if (_this.popup) {
(_a = _this.popup) === null || _a === void 0 ? void 0 : _a.remove();
}
_this.removeStyle();
_this.isPaused = true;
_this.clearExistingTimers();
};
/**
* 鼠标移出开始轮播
*/
this.resumeCarouselPopups = function () {
if (_this.isPaused) {
_this.isPaused = false;
_this.startCarouselPopups();
}
};
/**
* 清除定时器
* @private
*/
this.clearExistingTimers = function () {
if (_this.popupTimeoutId !== null) {
clearTimeout(_this.popupTimeoutId);
_this.popupTimeoutId = 0;
}
if (_this.popupIntervalId !== null) {
clearInterval(_this.popupIntervalId);
_this.popupIntervalId = 0;
}
};
// 绑定事件处理函数
this.onMouseEnterHandler = this.pauseCarouselPopups.bind(this);
this.onMouseLeaveHandler = this.resumeCarouselPopups.bind(this);
this.onVisibilityChangeHandler = this.handleVisibilityChange.bind(this);
this.clearExistingTimers = this.clearExistingTimers.bind(this);
this.init(scene, chart, view, data, customSubArea, drawOption);
}
/**
* 更新轮播弹窗对象内容
* @param scene
* @param chart
* @param view
* @param data
* @param customSubArea
*/
CarouselManager.prototype.update = function (scene, chart, view, data, customSubArea, drawOption) {
this.init(scene, chart, view, data, customSubArea, drawOption);
};
/**
* 初始化轮播弹窗
* @param scene
* @param chart
* @param view
* @param data
* @private
*/
CarouselManager.prototype.init = function (scene, chart, view, data, customSubArea, drawOption) {
var _this = this;
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
this.view = view;
this.chart = chart;
this.scene = scene;
this.data = data;
this.popup = null;
this.currentIndex = 0;
this.customSubArea = customSubArea;
this.drawOption = drawOption;
this.clearPreviousInstance(this.chart.container);
if (((_b = (_a = this.chart.customAttr) === null || _a === void 0 ? void 0 : _a.tooltip) === null || _b === void 0 ? void 0 : _b.show) && ((_e = (_d = (_c = this.chart.customAttr) === null || _c === void 0 ? void 0 : _c.tooltip) === null || _d === void 0 ? void 0 : _d.carousel) === null || _e === void 0 ? void 0 : _e.enable) &&
this.data.length > 0) {
this.popup = new l7_1.Popup({ closeButton: false, maxWidth: 600 });
var carousel = (_g = (_f = this.chart.customAttr) === null || _f === void 0 ? void 0 : _f.tooltip) === null || _g === void 0 ? void 0 : _g.carousel;
this.stayTime = carousel.stayTime * 1000;
this.intervalTime = carousel.intervalTime * 1000;
this.startCarouselPopups();
var divElement = document.getElementById(this.chart.container);
divElement.addEventListener('mouseenter', this.pauseCarouselPopups);
divElement.addEventListener('mouseleave', this.resumeCarouselPopups);
// 移动端符号地图不支持mouseenter和mouseleave事件这里特殊处理一下
if (this.chart.type === 'symbolic-map') {
// 监听符号触摸事件, 暂停轮播
(_j = (_h = scene === null || scene === void 0 ? void 0 : scene.getLayers()) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.addListener('touchend', function () {
_this.pauseCarouselPopups();
});
// 地图空白区域触摸事件, 启动轮播
(_k = scene === null || scene === void 0 ? void 0 : scene.getMapCanvasContainer()) === null || _k === void 0 ? void 0 : _k.addEventListener('touchend', function () {
_this.resumeCarouselPopups();
});
}
// 监听页面可见性变化
document.addEventListener('visibilitychange', this.handleVisibilityChange);
exports.carouselManagerInstances[this.chart.container] = this;
}
};
/**
* 清除之前的实例数据
* @param containerId
* @private
*/
CarouselManager.prototype.clearPreviousInstance = function (containerId) {
var _a;
if (exports.carouselManagerInstances[containerId]) {
var instance = exports.carouselManagerInstances[containerId];
this.clearExistingTimers();
(_a = instance.popup) === null || _a === void 0 ? void 0 : _a.remove();
instance.removeStyle();
}
};
/**
* 开始轮播
* @private
*/
CarouselManager.prototype.startCarouselPopups = function () {
this.clearExistingTimers();
this.carouselPopups();
};
/**
* 管理轮播弹窗的显示
*
* 此方法用于处理轮播弹窗的显示逻辑它会根据当前的索引显示对应的弹窗
* 并在一定时间后自动移除当前弹窗并显示下一个弹窗
*
* @private
*/
CarouselManager.prototype.carouselPopups = function () {
var _this = this;
var showPopup = function (index) {
_this.removeStyle();
var containerElement = document.getElementById(_this.chart.container);
if (containerElement) {
if (_this.chart.type === 'symbolic-map') {
// 轮播进行时隐藏隐藏鼠标悬浮的tooltip
var mouseTooltip = containerElement.getElementsByClassName('l7-popup-' + _this.chart.container);
for (var _i = 0, _a = Array.from(mouseTooltip); _i < _a.length; _i++) {
var tooltip = _a[_i];
var tooltipElement = tooltip;
tooltipElement.classList.add('l7-popup-hide');
}
_this.createSymbolicMapPopup(index);
}
else {
if (_this.chart.type === 'map') {
// 轮播进行时隐藏隐藏鼠标悬浮的tooltip
var mouseTooltip = containerElement.getElementsByClassName('l7plot-tooltip-container');
for (var _b = 0, _c = Array.from(mouseTooltip); _b < _c.length; _b++) {
var tooltip = _c[_b];
var tooltipElement = tooltip;
tooltipElement.style.display = 'none';
}
}
_this.createPopup(index);
}
_this.clearExistingTimers();
_this.popupTimeoutId = window.setTimeout(function () {
var _a;
_this.currentIndex++;
(_a = _this.popup) === null || _a === void 0 ? void 0 : _a.remove();
_this.cancelHighlightLayer(index);
if (_this.currentIndex >= _this.data.length) {
_this.currentIndex = 0;
}
_this.popupIntervalId = window.setTimeout(function () {
showPopup(_this.currentIndex);
}, _this.intervalTime);
}, _this.stayTime);
}
else {
_this.clearExistingTimers();
}
};
showPopup(this.currentIndex);
};
/**
* 移除样式
* 每次创建弹窗前移除之前的样式
* @private
*/
CarouselManager.prototype.removeStyle = function () {
var _a;
var styleToRemove = document.getElementById('style-' + this.chart.container);
if (styleToRemove) {
styleToRemove.remove();
(_a = styleToRemove.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(styleToRemove);
}
};
/**
* 创建弹窗信息
* @param index
* @private
*/
CarouselManager.prototype.createPopup = function (index) {
var _this = this;
var _a, _b, _c, _d, _e, _f;
var tooltipStyle = this.view.tooltip.options.domStyles;
var tooltipBackgroundColor = tooltipStyle['l7plot-tooltip']['background-color'];
var tooltipFontSize = tooltipStyle['l7plot-tooltip']['font-size'];
var style = document.createElement('style');
style.id = 'style-' + this.chart.container;
style.innerHTML = "\n #" + this.chart.container + " .l7-popup-content {\n background-color: " + tooltipBackgroundColor + " !important;\n font-size: " + tooltipFontSize + ";\n padding: 10px 10px 6px;\n line-height: 1.6;\n }\n #" + this.chart.container + " .l7-popup-tip {\n border-top-color: " + tooltipBackgroundColor + " !important;\n }\n ";
document.head.appendChild(style);
var popupData = this.getPopupData(index);
if (popupData.data) {
var tooltipItem_1 = '';
this.getTooltipItems(popupData.data).forEach(function (fieldData) {
tooltipItem_1 += "\n <li style=\"list-style-type: none; margin-bottom: 4px; white-space: nowrap; display: flex; justify-content: space-between;\">\n <span style=\"" + _this.objectToSemicolonSeparated(tooltipStyle['l7plot-tooltip__name']) + "\">" + fieldData.name + "</span>\n <span style=\"" + _this.objectToSemicolonSeparated(tooltipStyle['l7plot-tooltip__value']) + "\">" + fieldData.value + "</span>\n </li>";
});
var html = "\n <div>\n <div style=\"" + this.objectToSemicolonSeparated(tooltipStyle['l7plot-tooltip__title']) + "\">" + popupData.data.name + "</div>\n <ul style=\"" + this.objectToSemicolonSeparated(tooltipStyle['l7plot-tooltip__list']) + "\">\n " + tooltipItem_1 + "\n </ul>\n </div>\n ";
this.popup.setLngLat({ lng: popupData.centroid[0], lat: popupData.centroid[1] });
this.popup.setHTML(html);
this.popup.closeButton = false;
this.view.addLayer(this.popup);
// 地图层高亮
(_b = (_a = this.view.scene
.getLayers()) === null || _a === void 0 ? void 0 : _a.find(function (i) { return i.name === 'highlightLayer'; })) === null || _b === void 0 ? void 0 : _b.setData(this.getActiveData(index));
if (this.chart.type === 'bubble-map') {
// 气泡地图高亮
var _id = ((_d = (_c = this.view.scene
.getLayers()) === null || _c === void 0 ? void 0 : _c.find(function (i) { return i.name === 'bubbleLayer'; })) === null || _d === void 0 ? void 0 : _d.layerSource.data.dataArray.find(function (i) { return i.name === _this.data[index].name; }))._id;
(_f = (_e = this.view.scene
.getLayers()) === null || _e === void 0 ? void 0 : _e.find(function (i) { return i.name === 'bubbleLayer' && i.coordCenter; })) === null || _f === void 0 ? void 0 : _f.setActive(_id, { color: 'rgba(30,90,255,1)' });
}
}
};
CarouselManager.prototype.getActiveData = function (index) {
var _this = this;
var _a, _b, _c;
if ((_b = (_a = this.drawOption) === null || _a === void 0 ? void 0 : _a.areaId) === null || _b === void 0 ? void 0 : _b.startsWith('custom_')) {
var result_1 = {
type: 'FeatureCollection',
features: []
};
var area = this.customSubArea.find(function (a) { return a.name === _this.data[index].areaName; });
var areaMap_1 = this.view.currentDistrictData.features.reduce(function (p, n) {
p['156' + n.properties.adcode] = n;
return p;
}, {});
(_c = area === null || area === void 0 ? void 0 : area.scopeArr) === null || _c === void 0 ? void 0 : _c.forEach(function (s) {
if (areaMap_1[s]) {
result_1.features.push(areaMap_1[s]);
}
});
return result_1;
}
return {
type: 'FeatureCollection',
features: [
this.view.currentDistrictData.features.find(function (i) { return i.properties.name === _this.data[index].name; })
]
};
};
/**
* 获取弹窗信息包括原始数据及位置信息
* @param index
* @private
*/
CarouselManager.prototype.getPopupData = function (index) {
var _this = this;
var _a, _b, _c, _d;
if ((_b = (_a = this.drawOption) === null || _a === void 0 ? void 0 : _a.areaId) === null || _b === void 0 ? void 0 : _b.startsWith('custom_')) {
var data_1 = this.data[index];
var area = (_c = this.customSubArea) === null || _c === void 0 ? void 0 : _c.find(function (a) { return a.name === data_1.areaName; });
data_1.name = data_1.areaName;
return {
data: data_1,
centroid: area.centroid
};
}
else {
return {
data: this.data[index],
centroid: (_d = this.view.currentDistrictData.features.find(function (i) { return i.properties.name === _this.data[index].name; })) === null || _d === void 0 ? void 0 : _d.properties.centroid
};
}
};
/**
* 将对象转换为 CSS 属性
* @param obj
* @private
*/
CarouselManager.prototype.objectToSemicolonSeparated = function (obj) {
var result = '';
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
result += this.convertToSnakeCase(key) + ":" + obj[key] + ";";
}
}
return result;
};
CarouselManager.prototype.cancelHighlightLayer = function (index) {
var _this = this;
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
(_c = (_b = (_a = this.view.scene) === null || _a === void 0 ? void 0 : _a.getLayers()) === null || _b === void 0 ? void 0 : _b.find(function (i) { return i.name === 'highlightLayer'; })) === null || _c === void 0 ? void 0 : _c.setData({ type: 'FeatureCollection', features: [] });
if (this.chart.type === 'bubble-map') {
var _id = ((_f = (_e = (_d = this.view.scene) === null || _d === void 0 ? void 0 : _d.getLayers()) === null || _e === void 0 ? void 0 : _e.find(function (i) { return i.name === 'bubbleLayer'; })) === null || _f === void 0 ? void 0 : _f.layerSource.data.dataArray.find(function (i) { return i.name === _this.data[index].name; }))._id;
(_h = (_g = this.view.scene
.getLayers()) === null || _g === void 0 ? void 0 : _g.find(function (i) { return i.name === 'bubbleLayer' && i.coordCenter; })) === null || _h === void 0 ? void 0 : _h.setActive(_id, {
color: this.view.scene
.getLayers()
.find(function (i) { return i.name === 'bubbleLayer'; })
.styleAttributeService.getLayerStyleAttribute('color').scale.field
});
}
if (this.chart.type === 'symbolic-map') {
var lngField_1 = this.chart.xAxis[0].gisbiName;
var latField_1 = this.chart.xAxis[1].gisbiName;
var _id = ((_l = (_k = (_j = this.scene) === null || _j === void 0 ? void 0 : _j.getLayers()) === null || _k === void 0 ? void 0 : _k.find(function (i) { return i.type === 'PointLayer'; })) === null || _l === void 0 ? void 0 : _l.layerSource.data.dataArray.find(function (i) {
var targetLng = _this.data[index][lngField_1];
var targetLat = _this.data[index][latField_1];
return i[lngField_1] === targetLng && i[latField_1] === targetLat;
}))._id;
(_o = (_m = this.scene
.getLayers()) === null || _m === void 0 ? void 0 : _m.find(function (i) { return i.type === 'PointLayer' && i.coordCenter; })) === null || _o === void 0 ? void 0 : _o.setActive(_id, {
color: this.scene
.getLayers()
.find(function (i) { return i.type === 'PointLayer'; })
.styleAttributeService.getLayerStyleAttribute('color').scale.field
});
}
};
/**
* 将驼峰式命名转换为蛇形命名
* @param str
* @private
*/
CarouselManager.prototype.convertToSnakeCase = function (str) {
return str.replace(/([A-Z])/g, function (match) { return '-' + match.toLowerCase(); });
};
/**
* 获取弹窗字段信息
* 与tooltip要显示的内容一致
* @param data
* @private
*/
CarouselManager.prototype.getTooltipItems = function (data) {
var _a, _b, _c, _d;
var result = [];
var customAttr = util_1.parseJson(this.chart.customAttr);
var tooltip = customAttr.tooltip;
var formatterMap = (_a = tooltip.seriesTooltipFormatter) === null || _a === void 0 ? void 0 : _a.filter(function (i) { return i.show; }).reduce(function (pre, next) {
pre[next.id] = next;
return pre;
}, {});
if (isEmpty_1["default"](formatterMap)) {
return result;
}
var head = data;
var formatter = formatterMap[(_c = (_b = head.quotaList) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.id];
if (!isEmpty_1["default"](formatter)) {
var originValue = parseFloat(head.value);
var value = formatter_1.valueFormatter(originValue, formatter.formatterCfg);
var name = isEmpty_1["default"](formatter.chartShowName) ? formatter.name : formatter.chartShowName;
result.push(__assign(__assign({}, head), { name: name, value: "" + (value !== null && value !== void 0 ? value : '') }));
}
(_d = head.dynamicTooltipValue) === null || _d === void 0 ? void 0 : _d.forEach(function (item) {
var formatter = formatterMap[item.fieldId];
if (formatter) {
var value = formatter_1.valueFormatter(parseFloat(item.value), formatter.formatterCfg);
var name = isEmpty_1["default"](formatter.chartShowName) ? formatter.name : formatter.chartShowName;
result.push({ color: 'grey', name: name, value: "" + (value !== null && value !== void 0 ? value : '') });
}
});
return result;
};
/**
* 符号地图特殊处理tooltip的配置可自定义显示内容
* @param index
* @private
*/
CarouselManager.prototype.createSymbolicMapPopup = function (index) {
var _this = this;
var buildTooltip = function () {
var _a, _b, _c, _d, _e, _f;
var customAttr = _this.chart.customAttr ? util_1.parseJson(_this.chart.customAttr) : null;
if ((_a = customAttr === null || customAttr === void 0 ? void 0 : customAttr.tooltip) === null || _a === void 0 ? void 0 : _a.show) {
if (!_this.popup) {
return undefined;
}
var tooltip = utils_1.deepCopy(customAttr).tooltip;
var showFields = tooltip.showFields || [];
if (!tooltip.showFields || tooltip.showFields.length === 0) {
showFields = __spreadArrays(_this.chart.xAxisExt.map(function (i) { return i.gisbiName + "@" + i.name; }), _this.chart.xAxis.map(function (i) { return i.gisbiName + "@" + i.name; }));
}
var style = document.createElement('style');
style.id = 'style-' + _this.chart.container;
style.innerHTML = "\n #" + _this.chart.container + " .l7-popup-content {\n background-color: " + tooltip.backgroundColor + " !important;\n padding: 6px 10px 6px;\n line-height: 1.6;\n }\n #" + _this.chart.container + " .l7-popup-tip {\n border-top-color: " + tooltip.backgroundColor + " !important;\n }\n ";
document.head.appendChild(style);
var lngField_2 = _this.chart.xAxis[0].gisbiName;
var latField_2 = _this.chart.xAxis[1].gisbiName;
var htmlPrefix = "<div style='font-size:" + tooltip.fontSize + "px;color:" + tooltip.color + ";'>";
var htmlSuffix = '</div>';
var data = _this.view.sourceOption.data[index];
if (data && ((_b = data.details) === null || _b === void 0 ? void 0 : _b.length)) {
var fieldData = __assign(__assign({}, data), Object.fromEntries(mergeDetailsToMap(data.details)));
var content = buildTooltipContent(tooltip, fieldData, showFields);
var html = "" + htmlPrefix + content + htmlSuffix;
_this.popup.setLngLat({
lng: data[lngField_2],
lat: data[latField_2]
});
_this.popup.setHTML(html);
_this.popup.closeButton = false;
_this.scene.addPopup(_this.popup);
_this.popup.addTo(_this.scene);
var _id = ((_d = (_c = _this.scene
.getLayers()) === null || _c === void 0 ? void 0 : _c.find(function (i) { return i.type === 'PointLayer'; })) === null || _d === void 0 ? void 0 : _d.layerSource.data.dataArray.find(function (i) {
var targetLng = _this.data[index][lngField_2];
var targetLat = _this.data[index][latField_2];
return i[lngField_2] === targetLng && i[latField_2] === targetLat;
}))._id;
(_f = (_e = _this.scene
.getLayers()) === null || _e === void 0 ? void 0 : _e.find(function (i) { return i.type === 'PointLayer' && i.coordCenter; })) === null || _f === void 0 ? void 0 : _f.setActive(_id, { color: 'rgba(30,90,255,1)' });
}
}
return undefined;
};
/**
* 构建 tooltip 内容
* @param tooltip
* @param fieldData
* @param showFields
* @returns {string}
*/
var buildTooltipContent = function (tooltip, fieldData, showFields) {
var content = '';
if (tooltip.customContent) {
content = tooltip.customContent;
showFields.forEach(function (field) {
content = content.replace("${" + field.split('@')[1] + "}", fieldData[field.split('@')[0]]);
});
}
else {
showFields.forEach(function (field) {
content += "<span style=\"margin-bottom: 4px\">" + field.split('@')[1] + ": " + fieldData[field.split('@')[0]] + "</span><br>";
});
}
return content.replace(/\n/g, '<br>');
};
/**
* 合并详情到 map
* @param details
* @returns {Map<string, any>}
*/
var mergeDetailsToMap = function (details) {
var resultMap = new Map();
details.forEach(function (item) {
Object.entries(item).forEach(function (_a) {
var key = _a[0], value = _a[1];
if (resultMap.has(key)) {
var existingValue = resultMap.get(key);
if (existingValue !== value) {
resultMap.set(key, existingValue + ", " + value);
}
}
else {
resultMap.set(key, value);
}
});
});
return resultMap;
};
buildTooltip();
};
return CarouselManager;
}());
exports.CarouselManager = CarouselManager;

View File

@ -1,18 +1,18 @@
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { useI18n } from '@/hooks/web/useI18n'
import {
L7ChartView,
L7Config,
L7DrawConfig,
L7Wrapper
} from '@/data-visualization/chart/components/js/panel/types/impl/l7'
import { MAP_EDITOR_PROPERTY_INNER } from '@/data-visualization/chart/components/js/panel/charts/map/common'
import { flow, parseJson } from '@/data-visualization/chart/components/js/util'
import { deepCopy } from '@/data-visualization/utils/utils'
} from '@/views/chart/components/js/panel/types/impl/l7'
import { MAP_EDITOR_PROPERTY_INNER } from '@/views/chart/components/js/panel/charts/map/common'
import { flow, parseJson } from '@/views/chart/components/js/util'
import { deepCopy } from '@/utils/utils'
import { GaodeMap } from '@antv/l7-maps'
import { Scene } from '@antv/l7-scene'
import { HeatmapLayer } from '@antv/l7-layers'
import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart'
import { mapRendered, mapRendering } from '@/data-visualization/chart/components/js/panel/common/common_antv'
import { DEFAULT_BASIC_STYLE } from '@/views/chart/components/editor/util/chart'
import { mapRendered, mapRendering } from '@/views/chart/components/js/panel/common/common_antv'
const { t } = useI18n()
/**

View File

@ -157,6 +157,11 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
})
})
data = filterChartDataByRange(sourceData, maxValue, minValue)
if (chart.drill) {
getMaxAndMinValueByData(sourceData, 'value', 0, 0, (max, min) => {
data = filterChartDataByRange(sourceData, max, min)
})
}
} else {
data = sourceData
}
@ -301,7 +306,8 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
content.push(name)
}
if (label.showQuota) {
areaMap[name] && content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter))
;(areaMap[name] || areaMap[name] === 0) &&
content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter))
}
item.properties['_DE_LABEL_'] = content.join('\n\n')
}
@ -346,11 +352,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
return listDom
}
private customConfigLegend(
chart: Chart,
options: ChoroplethOptions,
context: Record<string, any>
): ChoroplethOptions {
private customConfigLegend(chart: Chart, options: ChoroplethOptions): ChoroplethOptions {
const { basicStyle, misc } = parseJson(chart.customAttr)
const colors = basicStyle.colors.map(item => hexColorToRGBA(item, basicStyle.alpha))
if (basicStyle.suspension === false && basicStyle.showZoom === undefined) {
@ -420,14 +422,14 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
const isLessThanMin = range[0] < ranges[0][0] && range[1] < ranges[0][0]
let rangeColor = colors[colorIndex]
if (isLessThanMin) {
rangeColor = hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha)
rangeColor = basicStyle.areaBaseColor
}
items.push({
value: tmpRange,
color: rangeColor
})
})
customLegend['customContent'] = (_: string, _items: CategoryLegendListItem[]) => {
customLegend['customContent'] = () => {
if (items?.length) {
return this.createLegendCustomContent(items)
}
@ -435,13 +437,16 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
}
options.color['value'] = ({ value }) => {
const item = items.find(item => value >= item.value[0] && value <= item.value[1])
return item ? item.color : hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha)
return item ? item.color : basicStyle.areaBaseColor
}
options.color.scale.domain = [ranges[0][0], ranges[ranges.length - 1][1]]
} else {
customLegend['customContent'] = (_: string, items: CategoryLegendListItem[]) => {
const showItems = items?.length > 30 ? items.slice(0, 30) : items
if (showItems?.length) {
if (showItems.length === 1) {
showItems[0].value = options.color.scale.domain.slice(0, 2)
}
return this.createLegendCustomContent(showItems)
}
return ''
@ -508,7 +513,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
content.push(area.name)
}
if (label.showQuota) {
areaMap[area.name] &&
;(areaMap[area.name] || areaMap[area.name] === 0) &&
content.push(valueFormatter(areaMap[area.name].value, label.quotaLabelFormatter))
}
labelLocation.push({
@ -567,6 +572,9 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
return result
}
const head = originalItem.properties
if (!head) {
return result
}
const { adcode } = head
const areaName = subAreaMap['156' + adcode]
const valItem = areaMap[areaName]

View File

@ -1,4 +1,4 @@
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { useI18n } from '@/hooks/web/useI18n'
import {
L7ChartView,
L7Config,
@ -13,13 +13,17 @@ import {
svgStrToUrl
} from '@/data-visualization/chart/components/js/util'
import { deepCopy } from '@/data-visualization/utils/utils'
import { GaodeMap } from '@antv/l7-maps'
import { Scene } from '@antv/l7-scene'
import { PointLayer } from '@antv/l7-layers'
import { LayerPopup, Popup } from '@antv/l7'
import { mapRendered, mapRendering } from '@/data-visualization/chart/components/js/panel/common/common_antv'
import {
getMapCenter,
getMapScene,
getMapStyle,
mapRendered,
qqMapRendered
} from '@/data-visualization/chart/components/js/panel/common/common_antv'
import { configCarouselTooltip } from '@/data-visualization/chart/components/js/panel/charts/map/tooltip-carousel'
import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart'
import { filter } from 'lodash-es'
const { t } = useI18n()
@ -102,18 +106,10 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
miscStyle = parseJson(chart.customAttr).misc
}
let mapStyle = basicStyle.mapStyleUrl
if (basicStyle.mapStyle !== 'custom') {
mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
}
const mapKey = await this.getMapKey()
let center: [number, number] = [
DEFAULT_BASIC_STYLE.mapCenter.longitude,
DEFAULT_BASIC_STYLE.mapCenter.latitude
]
if (basicStyle.autoFit === false) {
center = [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
}
const mapStyle = getMapStyle(mapKey, basicStyle)
let center = getMapCenter(basicStyle)
// 联动时,聚焦到数据点,多个取第一个
if (
chart.chartExtRequest?.linkageFilters?.length &&
@ -121,45 +117,25 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
chart.data?.tableRow.length
) {
// 经度
const lng = chart.data?.tableRow?.[0][chart.xAxis[0].dataeaseName]
const lng = chart.data?.tableRow?.[0][chart.xAxis[0].gisbiName]
// 纬度
const lat = chart.data?.tableRow?.[0][chart.xAxis[1].dataeaseName]
const lat = chart.data?.tableRow?.[0][chart.xAxis[1].gisbiName]
center = [lng, lat]
}
const chartObj = drawOption.chartObj as unknown as L7Wrapper<L7Config, Scene>
let scene = chartObj?.getScene()
if (!scene) {
scene = new Scene({
id: container,
logoVisible: false,
map: new GaodeMap({
token: mapKey?.key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
center,
zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined,
showLabel: !(basicStyle.showLabel === false),
WebGLParams: {
preserveDrawingBuffer: true
}
})
})
} else {
if (scene.getLayers()?.length) {
await scene.removeAllLayer()
scene.setPitch(miscStyle.mapPitch)
scene.setMapStyle(mapStyle)
scene.map.showLabel = !(basicStyle.showLabel === false)
}
if (basicStyle.autoFit === false) {
scene.setZoomAndCenter(basicStyle.zoomLevel, center)
}
}
mapRendering(container)
scene.once('loaded', () => {
mapRendered(container)
})
this.configZoomButton(chart, scene)
scene = await getMapScene(
chart,
scene,
container,
mapKey,
basicStyle,
miscStyle,
mapStyle,
center
)
this.configZoomButton(chart, scene, mapKey)
if (xAxis?.length < 2) {
return new L7Wrapper(scene, undefined)
}
@ -171,9 +147,13 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
scene.addPopup(tooltipLayer)
}
this.buildLabel(chart, configList)
symbolicLayer.once('inited', () => {
mapRendered(container)
})
symbolicLayer.on('inited', () => {
chart.container = container
configCarouselTooltip(chart, symbolicLayer, symbolicLayer.sourceOption.data, scene)
qqMapRendered(scene)
})
symbolicLayer.on('click', ev => {
const data = ev.feature
@ -181,17 +161,17 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
const quotaList = []
chart.data.fields.forEach((item, index) => {
Object.keys(data).forEach(key => {
if (key.startsWith('f_') && item.dataeaseName === key) {
if (key.startsWith('f_') && item.gisbiName === key) {
if (index === 0) {
dimensionList.push({
id: item.id,
dataeaseName: item.dataeaseName,
gisbiName: item.gisbiName,
value: data[key]
})
} else {
quotaList.push({
id: item.id,
dataeaseName: item.dataeaseName,
gisbiName: item.gisbiName,
value: data[key]
})
}
@ -239,7 +219,7 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
let colorIndex = 0
// 存储已分配的颜色
const colorAssignments = new Map()
const sizeKey = extBubble.length > 0 ? extBubble[0].dataeaseName : ''
const sizeKey = extBubble.length > 0 ? extBubble[0].gisbiName : ''
//条件颜色
const { threshold } = parseJson(chart.senior)
@ -257,7 +237,7 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
? chart.data.tableRow.map((item, index) => {
item['_index'] = '_index' + index
// 颜色标识
const identifier = item[xAxisExt[0]?.dataeaseName]
const identifier = item[xAxisExt[0]?.gisbiName]
// 检查该标识是否已有颜色分配,如果没有则分配
let color = colorAssignments.get(identifier)
if (!color) {
@ -271,7 +251,7 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
if (conditions.length > 0) {
for (let i = 0; i < conditions.length; i++) {
const c = conditions[i]
const value = item[c.field.dataeaseName]
const value = item[c.field.gisbiName]
for (const t of c.conditions) {
const v = t.value
@ -332,12 +312,12 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
.source(data, {
parser: {
type: 'json',
x: xAxis[0].dataeaseName,
y: xAxis[1].dataeaseName
x: xAxis[0].gisbiName,
y: xAxis[1].gisbiName
}
})
.active(true)
if (xAxisExt[0]?.dataeaseName) {
if (xAxisExt[0]?.gisbiName) {
if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) {
// 图片无法改色
if (basicStyle.customIcon.startsWith('data')) {
@ -452,8 +432,8 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
let showFields = tooltip.showFields || []
if (!tooltip.showFields || tooltip.showFields.length === 0) {
showFields = [
...chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`),
...chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`)
...chart.xAxisExt.map(i => `${i.gisbiName}@${i.name}`),
...chart.xAxis.map(i => `${i.gisbiName}@${i.name}`)
]
}
// 修改背景色
@ -584,8 +564,8 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
let showFields = label.showFields || []
if (!label.showFields || label.showFields.length === 0) {
showFields = [
...chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`),
...chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`)
...chart.xAxisExt.map(i => `${i.gisbiName}@${i.name}`),
...chart.xAxis.map(i => `${i.gisbiName}@${i.name}`)
]
}
data.forEach(item => {
@ -613,8 +593,8 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
.source(data, {
parser: {
type: 'json',
x: xAxis[0].dataeaseName,
y: xAxis[1].dataeaseName
x: xAxis[0].gisbiName,
y: xAxis[1].gisbiName
}
})
.shape('textLayerContent', 'text')

View File

@ -469,8 +469,8 @@ export class CarouselManager {
})
}
if (this.chart.type === 'symbolic-map') {
const lngField = this.chart.xAxis[0].dataeaseName
const latField = this.chart.xAxis[1].dataeaseName
const lngField = this.chart.xAxis[0].gisbiName
const latField = this.chart.xAxis[1].gisbiName
const { _id } = this.scene
?.getLayers()
?.find(i => i.type === 'PointLayer')
@ -554,8 +554,8 @@ export class CarouselManager {
let showFields = tooltip.showFields || []
if (!tooltip.showFields || tooltip.showFields.length === 0) {
showFields = [
...this.chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`),
...this.chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`)
...this.chart.xAxisExt.map(i => `${i.gisbiName}@${i.name}`),
...this.chart.xAxis.map(i => `${i.gisbiName}@${i.name}`)
]
}
const style = document.createElement('style')
@ -571,8 +571,8 @@ export class CarouselManager {
}
`
document.head.appendChild(style)
const lngField = this.chart.xAxis[0].dataeaseName
const latField = this.chart.xAxis[1].dataeaseName
const lngField = this.chart.xAxis[0].gisbiName
const latField = this.chart.xAxis[1].gisbiName
const htmlPrefix = `<div style='font-size:${tooltip.fontSize}px;color:${tooltip.color};'>`
const htmlSuffix = '</div>'
const data = this.view.sourceOption.data[index]

View File

@ -1,9 +1,10 @@
import {
G2PlotChartView,
G2PlotDrawOptions
} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot'
} from '@/views/chart/components/js/panel/types/impl/g2plot'
import type { DualAxes, DualAxesOptions } from '@antv/g2plot/esm/plots/dual-axes'
import {
configRoundAngle,
configPlotTooltipEvent,
getAnalyse,
getLabel,
@ -14,7 +15,7 @@ import {
setGradientColor,
TOOLTIP_TPL
} from '../../common/common_antv'
import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util'
import { flow, hexColorToRGBA, parseJson } from '@/views/chart/components/js/util'
import {
cloneDeep,
isEmpty,
@ -25,7 +26,7 @@ import {
defaultsDeep,
defaults
} from 'lodash-es'
import { valueFormatter } from '@/data-visualization/chart/components/js/formatter'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import {
CHART_MIX_AXIS_TYPE,
CHART_MIX_DEFAULT_BASIC_STYLE,
@ -34,14 +35,15 @@ import {
MixChartBasicStyle
} from './chart-mix-common'
import type { Datum } from '@antv/g2plot/esm/types/common'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { useI18n } from '@/hooks/web/useI18n'
import {
DEFAULT_BASIC_STYLE,
DEFAULT_LABEL,
DEFAULT_LEGEND_STYLE
} from '@/data-visualization/chart/components/editor/util/chart'
} from '@/views/chart/components/editor/util/chart'
import type { Options } from '@antv/g2plot/esm'
import { Group } from '@antv/g-canvas'
import { extremumEvt } from '@/views/chart/components/js/extremumUitl'
const { t } = useI18n()
const DEFAULT_DATA = []
@ -56,7 +58,8 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
'label-selector': ['vPosition', 'seriesLabelFormatter'],
'tooltip-selector': [
...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axis: AxisType[] = [...CHART_MIX_AXIS_TYPE, 'xAxisExtRight', 'yAxisExt']
@ -94,6 +97,7 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
async drawChart(drawOptions: G2PlotDrawOptions<DualAxes>): Promise<DualAxes> {
const { chart, action, container } = drawOptions
chart.container = container
if (!chart.data?.left?.data?.length && !chart.data?.right?.data?.length) {
return
}
@ -117,7 +121,6 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
valueExt: d.value
}
})
// options
const initOptions: DualAxesOptions = {
data: [data1, data2],
@ -127,6 +130,7 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
geometryOptions: [
{
geometry: data1Type,
marginRatio: 0,
color: [],
isGroup: isGroup,
isStack: isStack,
@ -174,6 +178,7 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
newChart.on('point:click', action)
newChart.on('interval:click', action)
extremumEvt(newChart, chart, options, container)
configPlotTooltipEvent(chart, newChart)
return newChart
}
@ -292,18 +297,9 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
tempOption.geometryOptions[1].smooth = smooth
tempOption.geometryOptions[1].point = point
tempOption.geometryOptions[1].lineStyle = lineStyle
if (s.radiusColumnBar === 'roundAngle') {
const columnStyle = {
radius: [
s.columnBarRightAngleRadius,
s.columnBarRightAngleRadius,
s.columnBarRightAngleRadius,
s.columnBarRightAngleRadius
]
}
tempOption.geometryOptions[0].columnStyle = columnStyle
tempOption.geometryOptions[1].columnStyle = columnStyle
tempOption.geometryOptions[0] = {
...tempOption.geometryOptions[0],
...configRoundAngle(chart, 'columnStyle')
}
}
@ -328,7 +324,7 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
}
setupDefaultOptions(chart: ChartObj): ChartObj {
const { customAttr, senior } = chart
const { senior } = chart
if (
senior.functionCfg.emptyDataStrategy == undefined ||
senior.functionCfg.emptyDataStrategy === 'ignoreData'
@ -670,7 +666,8 @@ export class GroupColumnLineMix extends ColumnLineMix {
'label-selector': ['vPosition', 'seriesLabelFormatter'],
'tooltip-selector': [
...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axisConfig = {
@ -782,7 +779,8 @@ export class StackColumnLineMix extends ColumnLineMix {
'label-selector': ['vPosition', 'seriesLabelFormatter'],
'tooltip-selector': [
...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axisConfig = {
@ -895,7 +893,8 @@ export class DualLineMix extends ColumnLineMix {
'label-selector': ['seriesLabelFormatter'],
'tooltip-selector': [
...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axisConfig = {

View File

@ -69,7 +69,7 @@ export class CirclePacking extends G2PlotChartView<CirclePackingOptions, G2Circl
if (chart?.data?.data?.length) {
// data
const data = chart.data.data
const { xAxis, yAxis, drillFields } = chart
const { yAxis } = chart
const ySort = yAxis[0]?.sort ?? 'none'
const sort = {
sort: (a, b) =>
@ -123,7 +123,7 @@ export class CirclePacking extends G2PlotChartView<CirclePackingOptions, G2Circl
'@antv/g2plot/esm/plots/circle-packing'
)
const newChart = new G2CirclePacking(container, options)
newChart.on('point:click', param => {
newChart.on('element:click', param => {
const pointData = param?.data?.data
if (pointData?.name === t('commons.all')) {
return
@ -157,7 +157,7 @@ export class CirclePacking extends G2PlotChartView<CirclePackingOptions, G2Circl
...options,
hierarchyConfig: {
...options.hierarchyConfig,
padding: typeof padding === 'number' && !isNaN(padding) ? padding / 100 : 0
padding: padding / 100 ?? 0
},
pointStyle
}
@ -177,7 +177,7 @@ export class CirclePacking extends G2PlotChartView<CirclePackingOptions, G2Circl
textAlign: 'center',
offsetY: 5,
layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout,
formatter: (d: Datum, _point) => {
formatter: (d: Datum) => {
return d.children.length === 0 ? d.name : ''
}
}

View File

@ -74,7 +74,7 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
// options
const initOptions: GaugeOptions = {
percent: 0,
appendPadding: getPadding(chart),
appendPadding: [0, 10, 15, 10],
axis: {
tickInterval: 0.2,
label: {
@ -109,8 +109,10 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
}
})
})
const hasNoneData = chart.data?.series.some(s => !s.data?.[0])
this.configEmptyDataStyle(newChart, hasNoneData ? [] : [1], container)
const hasNoneData = chart.data?.series.some(
s => s.data?.[0] === undefined || s.data?.[0] === null
)
this.configEmptyDataStyle(hasNoneData ? [] : [1], container, newChart)
if (hasNoneData) {
return
}

View File

@ -15,7 +15,8 @@ export class IndicatorChartView extends AbstractChartView {
'indicator-value-selector',
'indicator-name-selector',
'threshold',
'function-cfg'
'function-cfg',
'linkage'
]
propertyInner: EditorPropertyInner = {
'background-overall-component': ['all'],

View File

@ -13,7 +13,8 @@ import {
configPlotTooltipEvent,
configYaxisTitleLengthLimit,
getTooltipContainer,
TOOLTIP_TPL
TOOLTIP_TPL,
getPadding
} from '../../common/common_antv'
import { DEFAULT_LEGEND_STYLE } from '@/data-visualization/chart/components/editor/util/chart'
@ -209,7 +210,7 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
data: data,
xField: 'yAxis',
yField: 'yAxisExt',
appendPadding: 30,
appendPadding: getPadding(chart),
pointStyle: {
fillOpacity: 0.8,
stroke: '#bbb'
@ -476,7 +477,6 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
this.configLegend,
this.configXAxis,
this.configYAxis,
this.configAnalyse,
this.configSlider,
this.configBasicStyle
)(chart, options, {}, this)

View File

@ -1,20 +1,20 @@
import {
G2PlotChartView,
G2PlotDrawOptions
} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot'
} from '@/views/chart/components/js/panel/types/impl/g2plot'
import type { Sankey, SankeyOptions } from '@antv/g2plot/esm/plots/sankey'
import { getPadding, setGradientColor } from '@/data-visualization/chart/components/js/panel/common/common_antv'
import { getPadding, setGradientColor } from '@/views/chart/components/js/panel/common/common_antv'
import { cloneDeep, get } from 'lodash-es'
import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util'
import { valueFormatter } from '@/data-visualization/chart/components/js/formatter'
import { flow, hexColorToRGBA, parseJson } from '@/views/chart/components/js/util'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import { Datum } from '@antv/g2plot/esm/types/common'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { useI18n } from '@/hooks/web/useI18n'
import {
SANKEY_AXIS_TYPE,
SANKEY_EDITOR_PROPERTY,
SANKEY_EDITOR_PROPERTY_INNER
} from '@/data-visualization/chart/components/js/panel/charts/others/sankey-common'
} from '@/views/chart/components/js/panel/charts/others/sankey-common'
const { t } = useI18n()
const DEFAULT_DATA = []

View File

@ -14,6 +14,8 @@ import {
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { defaults, isEmpty } from 'lodash-es'
import { DEFAULT_LEGEND_STYLE } from '@/data-visualization/chart/components/editor/util/chart'
import { type Datum } from '@antv/g2plot/esm'
import { Group } from '@antv/g-canvas'
const { t } = useI18n()
/**
@ -144,6 +146,17 @@ export class Scatter extends G2PlotChartView<ScatterOptions, G2Scatter> {
const { Scatter: G2Scatter } = await import('@antv/g2plot/esm/plots/scatter')
const newChart = new G2Scatter(container, options)
newChart.on('point:click', action)
if (options.label) {
newChart.on('label:click', e => {
action({
x: e.x,
y: e.y,
data: {
data: e.target.attrs.data
}
})
})
}
configPlotTooltipEvent(chart, newChart)
return newChart
}
@ -277,6 +290,41 @@ export class Scatter extends G2PlotChartView<ScatterOptions, G2Scatter> {
return optionTmp
}
protected configLabel(chart: Chart, options: ScatterOptions): ScatterOptions {
const tmpOption = super.configLabel(chart, options)
if (!tmpOption.label) {
return options
}
const { label: labelAttr } = parseJson(chart.customAttr)
tmpOption.label.style.fill = labelAttr.color
const label = {
...tmpOption.label,
formatter: function (data: Datum) {
const value = valueFormatter(data.value, labelAttr.labelFormatter)
const group = new Group({})
group.addShape({
type: 'text',
attrs: {
x: 0,
y: 0,
data,
text: value,
textAlign: 'start',
textBaseline: 'top',
fontSize: labelAttr.fontSize,
fontFamily: chart.fontFamily,
fill: labelAttr.color
}
})
return group
}
}
return {
...tmpOption,
label
}
}
protected setupOptions(chart: Chart, options: ScatterOptions) {
return flow(
this.configTheme,
@ -286,7 +334,6 @@ export class Scatter extends G2PlotChartView<ScatterOptions, G2Scatter> {
this.configLegend,
this.configXAxis,
this.configYAxis,
this.configAnalyse,
this.configSlider,
this.configBasicStyle
)(chart, options)

View File

@ -1,19 +1,19 @@
import {
G2PlotChartView,
G2PlotDrawOptions
} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot'
} from '@/views/chart/components/js/panel/types/impl/g2plot'
import type { WordCloud as G2WordCloud, WordCloudOptions } from '@antv/g2plot/esm/plots/word-cloud'
import {
filterChartDataByRange,
flow,
getMaxAndMinValueByData,
parseJson
} from '@/data-visualization/chart/components/js/util'
import { getPadding } from '@/data-visualization/chart/components/js/panel/common/common_antv'
import { valueFormatter } from '@/data-visualization/chart/components/js/formatter'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
} from '@/views/chart/components/js/util'
import { getPadding } from '@/views/chart/components/js/panel/common/common_antv'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import { useI18n } from '@/hooks/web/useI18n'
import { isEmpty } from 'lodash-es'
import { DEFAULT_MISC } from '@/data-visualization/chart/components/editor/util/chart'
import { DEFAULT_MISC } from '@/views/chart/components/editor/util/chart'
const { t } = useI18n()
const DEFAULT_DATA = []

View File

@ -27,19 +27,23 @@ import type { Datum } from '@antv/g2plot/esm/types/common'
import { add } from 'mathjs'
import isEmpty from 'lodash-es/isEmpty'
import { cloneDeep } from 'lodash-es'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
const { t } = useI18n()
const DEFAULT_DATA = []
export class Pie extends G2PlotChartView<PieOptions, G2Pie> {
axis: AxisType[] = PIE_AXIS_TYPE
properties = PIE_EDITOR_PROPERTY
propertyInner: EditorPropertyInner = {
...PIE_EDITOR_PROPERTY_INNER,
'basic-style-selector': ['colors', 'alpha', 'radius', 'topN', 'seriesColor']
'basic-style-selector': ['colors', 'alpha', 'radius', 'topN', 'seriesColor'],
'tooltip-selector': [...PIE_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel']
}
axisConfig = PIE_AXIS_CONFIG
async drawChart(drawOptions: G2PlotDrawOptions<G2Pie>): Promise<G2Pie> {
const { chart, container, action } = drawOptions
this.configEmptyDataStyle(chart.data?.data, container, null, t('chart.no_data_or_not_positive'))
chart.container = container
if (!chart.data?.data?.length) {
return
}
@ -115,12 +119,22 @@ export class Pie extends G2PlotChartView<PieOptions, G2Pie> {
field: {
type: 'cat'
}
},
state: {
active: {
style: {
lineWidth: 2,
fillOpacity: 0.5
}
}
}
}
const options = this.setupOptions(chart, initOptions)
const { Pie: G2Pie } = await import('@antv/g2plot/esm/plots/pie')
const newChart = new G2Pie(container, options)
newChart.on('interval:click', action)
newChart.on('interval:click', d => {
d.data?.data?.field !== customAttr.basicStyle.topNLabel && action(d)
})
configPlotTooltipEvent(chart, newChart)
return newChart
}
@ -244,6 +258,7 @@ export class Pie extends G2PlotChartView<PieOptions, G2Pie> {
},
container: getTooltipContainer(`tooltip-${chart.id}`),
itemTpl: TOOLTIP_TPL,
shared: true,
enterable: true
}
return {
@ -338,7 +353,8 @@ export class Pie extends G2PlotChartView<PieOptions, G2Pie> {
export class PieDonut extends Pie {
propertyInner: EditorPropertyInner = {
...PIE_EDITOR_PROPERTY_INNER,
'basic-style-selector': ['colors', 'alpha', 'radius', 'innerRadius', 'topN', 'seriesColor']
'basic-style-selector': ['colors', 'alpha', 'radius', 'innerRadius', 'topN', 'seriesColor'],
'tooltip-selector': [...PIE_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel']
}
protected configBasicStyle(chart: Chart, options: PieOptions): PieOptions {
const tmp = super.configBasicStyle(chart, options)

View File

@ -40,6 +40,7 @@ export class Rose extends G2PlotChartView<RoseOptions, G2Rose> {
async drawChart(drawOptions: G2PlotDrawOptions<G2Rose>): Promise<G2Rose> {
const { chart, container, action } = drawOptions
this.configEmptyDataStyle(chart.data?.data, container, null, t('chart.no_data_or_not_positive'))
if (!chart?.data?.data?.length) {
return
}

View File

@ -6,6 +6,7 @@ export const TABLE_EDITOR_PROPERTY: EditorProperty[] = [
'table-cell-selector',
'title-selector',
'tooltip-selector',
'summary-selector',
'function-cfg',
'threshold',
'scroll-cfg',

View File

@ -0,0 +1,402 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
exports.__esModule = true;
exports.TableHeatmap = void 0;
var g2plot_1 = require("@/views/chart/components/js/panel/types/impl/g2plot");
var util_1 = require("@/views/chart/components/js/util");
var useI18n_1 = require("@/hooks/web/useI18n");
var utils_1 = require("@/utils/utils");
var lodash_es_1 = require("lodash-es");
var common_antv_1 = require("@/views/chart/components/js/panel/common/common_antv");
var formatter_1 = require("@/views/chart/components/js/formatter");
var t = useI18n_1.useI18n().t;
var DEFAULT_DATA = [];
/**
* 热力图
*/
var TableHeatmap = /** @class */ (function (_super) {
__extends(TableHeatmap, _super);
function TableHeatmap() {
var _this = _super.call(this, 't-heatmap', DEFAULT_DATA) || this;
_this.properties = [
'basic-style-selector',
'background-overall-component',
'label-selector',
'legend-selector',
'x-axis-selector',
'y-axis-selector',
'title-selector',
'tooltip-selector',
'jump-set',
'linkage',
'border-style'
];
_this.propertyInner = {
'background-overall-component': ['all'],
'basic-style-selector': ['colors'],
'label-selector': ['fontSize', 'color'],
'x-axis-selector': ['name', 'color', 'fontSize', 'position', 'axisLabel', 'axisLine'],
'y-axis-selector': [
'name',
'color',
'fontSize',
'position',
'axisLabel',
'axisLine',
'showLengthLimit'
],
'title-selector': [
'title',
'fontSize',
'color',
'hPosition',
'isItalic',
'isBolder',
'remarkShow',
'fontFamily',
'letterSpace',
'fontShadow'
],
'legend-selector': ['orient', 'color', 'fontSize', 'hPosition', 'vPosition'],
'tooltip-selector': ['show', 'color', 'fontSize', 'backgroundColor'],
'border-style': ['all']
};
_this.axis = ['xAxis', 'xAxisExt', 'extColor', 'filter'];
_this.axisConfig = {
xAxis: {
name: t('chart.x_axis') + " / " + t('chart.dimension'),
type: 'd',
limit: 1
},
xAxisExt: {
name: t('chart.y_axis') + " / " + t('chart.dimension'),
type: 'd',
limit: 1
},
extColor: {
name: t('chart.color') + " / " + t('chart.dimension_or_quota'),
limit: 1
}
};
_this.getDefaultLength = function (chart, l) {
var containerDom = document.getElementById(chart.container);
var containerHeight = (containerDom === null || containerDom === void 0 ? void 0 : containerDom.clientHeight) || 100;
var containerWidth = (containerDom === null || containerDom === void 0 ? void 0 : containerDom.clientWidth) || 100;
var defaultLength = containerHeight - containerHeight * 0.5;
if (l.orient !== 'vertical') {
defaultLength = containerWidth - containerWidth * 0.5;
}
return defaultLength;
};
_this.sortData = function (fieldObj, data) {
var deType = fieldObj.deType, sort = fieldObj.sort, customSort = fieldObj.customSort;
if (sort === 'desc') {
if (deType === 0) {
return data.sort().reverse();
}
else {
return data.sort(function (a, b) { return b - a; });
}
}
else if (sort === 'asc') {
if (deType === 0) {
return data.sort();
}
else {
return data.sort(function (a, b) { return a - b; });
}
}
// 如果没有指定排序方式,直接返回原始数据或 customSort
return customSort && customSort.length > 0 ? customSort : data;
};
return _this;
}
TableHeatmap.prototype.drawChart = function (drawOptions) {
return __awaiter(this, void 0, Promise, function () {
var chart, container, action, xAxis, xAxisExt, extColor, xField, xFieldExt, extColorField, tmpData, data, initOptions, options, Heatmap, newChart;
var _a;
var _this = this;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
chart = drawOptions.chart, container = drawOptions.container, action = drawOptions.action;
xAxis = utils_1.deepCopy(chart.xAxis);
xAxisExt = utils_1.deepCopy(chart.xAxisExt);
extColor = utils_1.deepCopy(chart.extColor);
if (!(xAxis === null || xAxis === void 0 ? void 0 : xAxis.length) || !(xAxisExt === null || xAxisExt === void 0 ? void 0 : xAxisExt.length) || !(extColor === null || extColor === void 0 ? void 0 : extColor.length)) {
return [2 /*return*/];
}
xField = xAxis[0].gisbiName;
xFieldExt = xAxisExt[0].gisbiName;
extColorField = extColor[0].gisbiName;
tmpData = lodash_es_1.cloneDeep(chart.data.tableRow);
data = tmpData.filter(function (cell) { return cell[xField] && cell[xFieldExt] && cell[extColorField]; });
data.forEach(function (i) {
Object.keys(i).forEach(function (key) {
if (key === '*') {
i['@'] = i[key];
}
});
});
initOptions = {
data: data,
xField: xField,
yField: xFieldExt,
colorField: extColorField === '*' ? '@' : extColorField,
appendPadding: common_antv_1.getPadding(chart),
meta: (_a = {},
_a[xField] = {
type: 'cat',
values: this.sortData(xAxis[0], __spreadArrays(new Set(data.map(function (i) { return i[[xField]]; }))))
},
_a[xFieldExt] = {
type: 'cat',
values: this.sortData(xAxisExt[0], __spreadArrays(new Set(data.map(function (i) { return i[[xFieldExt]]; })))).reverse()
},
_a),
legend: {
layout: 'vertical',
position: 'right',
slidable: true,
label: {
align: 'left',
spacing: 10
}
}
};
chart.container = container;
options = this.setupOptions(chart, initOptions);
return [4 /*yield*/, Promise.resolve().then(function () { return require('@antv/g2plot/esm/plots/heatmap'); })];
case 1:
Heatmap = (_b.sent()).Heatmap;
newChart = new Heatmap(container, options);
newChart.on('plot:click', function (param) {
var _a;
if (!((_a = param.data) === null || _a === void 0 ? void 0 : _a.data)) {
return;
}
var pointData = param.data.data;
var dimensionList = [];
chart.data.fields.forEach(function (item) {
Object.keys(pointData).forEach(function (key) {
if (key.startsWith('f_') && item.gisbiName === key) {
dimensionList.push({
id: item.id,
gisbiName: item.gisbiName,
value: pointData[key]
});
}
});
});
action({
x: param.data.x,
y: param.data.y,
data: {
data: __assign(__assign({}, param.data.data), { value: dimensionList[1].value, name: dimensionList[1].id, dimensionList: dimensionList, quotaList: [dimensionList[1]] })
}
});
});
newChart.on('afterrender', function (ev) {
var _a;
var l = JSON.parse(JSON.stringify(util_1.parseJson(chart.customStyle).legend));
if (l.show) {
var rail = (_a = ev.view.getController('legend').option[extColor[0].gisbiName]) === null || _a === void 0 ? void 0 : _a['rail'];
if (rail) {
rail.defaultLength = _this.getDefaultLength(chart, l);
}
}
});
common_antv_1.configAxisLabelLengthLimit(chart, newChart);
return [2 /*return*/, newChart];
}
});
});
};
TableHeatmap.prototype.configTheme = function (chart, options) {
var tmp = _super.prototype.configTheme.call(this, chart, options);
tmp.theme.innerLabels.offset = 0;
return tmp;
};
TableHeatmap.prototype.configBasicStyle = function (chart, options) {
var _a;
var basicStyle = util_1.parseJson(chart.customAttr).basicStyle;
var color = (_a = basicStyle.colors) === null || _a === void 0 ? void 0 : _a.map(function (ele) {
return util_1.hexColorToRGBA(ele, basicStyle.alpha);
});
return __assign(__assign({}, options), { color: color });
};
TableHeatmap.prototype.configTooltip = function (chart, options) {
var tooltip;
var customAttr;
if (chart.customAttr) {
customAttr = util_1.parseJson(chart.customAttr);
// tooltip
if (customAttr.tooltip) {
var extColor = utils_1.deepCopy(chart.extColor);
var xAxisExt = utils_1.deepCopy(chart.xAxisExt);
var tooltipFiledList_1 = [xAxisExt, extColor];
var t_1 = JSON.parse(JSON.stringify(customAttr.tooltip));
if (t_1.show) {
tooltip = {
showTitle: true,
customItems: function (originalItems) {
var items = [];
var createItem = function (fieldObj, items, originalItems) {
var name = (fieldObj === null || fieldObj === void 0 ? void 0 : fieldObj.chartShowName) ? fieldObj === null || fieldObj === void 0 ? void 0 : fieldObj.chartShowName : fieldObj === null || fieldObj === void 0 ? void 0 : fieldObj.name;
var value = originalItems[0].data[fieldObj.gisbiName];
if (!isNaN(Number(value))) {
value = formatter_1.valueFormatter(value, fieldObj === null || fieldObj === void 0 ? void 0 : fieldObj.formatterCfg);
}
items.push(__assign(__assign({}, originalItems[0]), { name: name, value: value }));
};
tooltipFiledList_1.forEach(function (field) {
createItem(field[0], items, originalItems);
});
return items;
}
};
}
else {
tooltip = false;
}
}
}
return __assign(__assign({}, options), { tooltip: tooltip });
};
TableHeatmap.prototype.configXAxis = function (chart, options) {
var xAxis = common_antv_1.getXAxis(chart);
return __assign(__assign({}, options), { xAxis: xAxis ? __assign(__assign({}, xAxis), { grid: null }) : false });
};
TableHeatmap.prototype.configYAxis = function (chart, options) {
var yAxis = common_antv_1.getYAxis(chart);
return __assign(__assign({}, options), { yAxis: yAxis ? __assign(__assign({}, yAxis), { grid: null }) : false });
};
TableHeatmap.prototype.configLegend = function (chart, options) {
var tmpOptions = _super.prototype.configLegend.call(this, chart, options);
if (tmpOptions.legend) {
var l = JSON.parse(JSON.stringify(util_1.parseJson(chart.customStyle).legend));
tmpOptions.legend.slidable = true;
tmpOptions.legend.minHeight = 10;
tmpOptions.legend.minWidth = 10;
tmpOptions.legend.maxHeight = 600;
tmpOptions.legend.maxWidth = 600;
var containerDom = document.getElementById(chart.container);
var containerHeight = (containerDom === null || containerDom === void 0 ? void 0 : containerDom.clientHeight) || 100;
var containerWidth = (containerDom === null || containerDom === void 0 ? void 0 : containerDom.clientWidth) || 100;
var defaultLength = containerHeight - containerHeight * 0.5;
if (l.orient === 'vertical') {
tmpOptions.legend.offsetY = -5;
}
else {
defaultLength = containerWidth - containerWidth * 0.5;
}
tmpOptions.legend.rail = { defaultLength: defaultLength };
tmpOptions.legend.label = {
spacing: 10,
style: {
fill: l.color,
fontSize: l.fontSize
}
};
}
return tmpOptions;
};
TableHeatmap.prototype.setupDefaultOptions = function (chart) {
chart.customStyle.legend.orient = 'vertical';
chart.customStyle.legend.vPosition = 'center';
chart.customStyle.legend.hPosition = 'right';
chart.customStyle.legend['rail'] = { defaultLength: 100 };
return chart;
};
TableHeatmap.prototype.configLabel = function (chart, options) {
var tmpOptions = _super.prototype.configLabel.call(this, chart, options);
if (tmpOptions.label) {
var extColor_1 = utils_1.deepCopy(chart.extColor);
var layout = [];
if (!tmpOptions.label.fullDisplay) {
layout.push.apply(layout, tmpOptions.label.layout);
}
var label = __assign(__assign({}, tmpOptions.label), { position: 'middle', layout: layout, formatter: function (data) {
var _a, _b;
var value = data[(_a = extColor_1[0]) === null || _a === void 0 ? void 0 : _a.gisbiName];
if (!isNaN(Number(value))) {
return formatter_1.valueFormatter(value, (_b = extColor_1[0]) === null || _b === void 0 ? void 0 : _b.formatterCfg);
}
return value;
} });
return __assign(__assign({}, tmpOptions), { label: label });
}
return tmpOptions;
};
TableHeatmap.prototype.setupOptions = function (chart, options) {
return util_1.flow(this.configTheme, this.configXAxis, this.configYAxis, this.configBasicStyle, this.configLegend, this.configTooltip, this.configLabel)(chart, options);
};
return TableHeatmap;
}(g2plot_1.G2PlotChartView));
exports.TableHeatmap = TableHeatmap;

View File

@ -0,0 +1,564 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
exports.__esModule = true;
exports.TableInfo = void 0;
var s2_1 = require("@antv/s2");
var formatter_1 = require("../../../formatter");
var util_1 = require("../../../util");
var s2_2 = require("../../types/impl/s2");
var common_1 = require("./common");
var useI18n_1 = require("@/hooks/web/useI18n");
var lodash_es_1 = require("lodash-es");
var common_table_1 = require("@/views/chart/components/js/panel/common/common_table");
var t = useI18n_1.useI18n().t;
var ImageCell = /** @class */ (function (_super) {
__extends(ImageCell, _super);
function ImageCell() {
return _super !== null && _super.apply(this, arguments) || this;
}
ImageCell.prototype.drawTextShape = function () {
common_table_1.drawImage.apply(this);
};
return ImageCell;
}(common_table_1.CustomDataCell));
/**
* 明细表
*/
var TableInfo = /** @class */ (function (_super) {
__extends(TableInfo, _super);
function TableInfo() {
var _this = _super.call(this, 'table-info', []) || this;
_this.properties = common_1.TABLE_EDITOR_PROPERTY;
_this.propertyInner = __assign(__assign({}, common_1.TABLE_EDITOR_PROPERTY_INNER), { 'table-header-selector': __spreadArrays(common_1.TABLE_EDITOR_PROPERTY_INNER['table-header-selector'], [
'tableHeaderSort',
'showTableHeader',
'headerGroup'
]), 'basic-style-selector': [
'tableColumnMode',
'tableBorderColor',
'tableScrollBarColor',
'alpha',
'tablePageMode',
'showHoverStyle',
'autoWrap'
], 'table-cell-selector': __spreadArrays(common_1.TABLE_EDITOR_PROPERTY_INNER['table-cell-selector'], [
'tableFreeze',
'tableColumnFreezeHead',
'tableRowFreezeHead',
'mergeCells'
]), 'summary-selector': ['showSummary', 'summaryLabel'] });
_this.axis = ['xAxis', 'filter', 'drill'];
_this.axisConfig = {
xAxis: {
name: t('chart.drag_block_table_data_column') + " / " + t('chart.dimension_or_quota')
}
};
return _this;
}
TableInfo.prototype.drawChart = function (drawOption) {
var _this = this;
var _a, _b, _c, _d, _e;
var container = drawOption.container, chart = drawOption.chart, pageInfo = drawOption.pageInfo, action = drawOption.action, resizeAction = drawOption.resizeAction;
var containerDom = document.getElementById(container);
// fields
var fields = (_b = (_a = chart.data) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : [];
var columns = [];
var meta = [];
var axisMap = chart.xAxis.reduce(function (pre, cur) {
pre[cur.gisbiName] = cur;
return pre;
}, {});
var drillFieldMap = {};
if (chart.drill) {
// 下钻过滤字段
var filterFields_1 = chart.drillFilters.map(function (i) { return i.fieldId; });
// 下钻入口的字段下标
var drillFieldId_1 = chart.drillFields[0].id;
var drillFieldIndex = chart.xAxis.findIndex(function (ele) { return ele.id === drillFieldId_1; });
// 当前下钻字段
var curDrillFieldId_1 = chart.drillFields[filterFields_1.length].id;
var curDrillField = fields.find(function (ele) { return ele.id === curDrillFieldId_1; });
filterFields_1.push(curDrillFieldId_1);
// 移除下钻字段,把当前下钻字段插入到下钻入口位置
fields = fields.filter(function (ele) {
return !filterFields_1.includes(ele.id);
});
drillFieldMap[curDrillField.gisbiName] = chart.drillFields[0].gisbiName;
fields.splice(drillFieldIndex, 0, curDrillField);
}
fields.forEach(function (ele) {
var _a;
var f = axisMap[ele.gisbiName];
if ((f === null || f === void 0 ? void 0 : f.hide) === true) {
return;
}
columns.push(ele.gisbiName);
meta.push({
field: ele.gisbiName,
name: (_a = ele.chartShowName) !== null && _a !== void 0 ? _a : ele.name,
formatter: function (value) {
if (!f) {
return value;
}
if (value === null || value === undefined) {
return value;
}
if (![2, 3, 4].includes(f.deType) || !lodash_es_1.isNumber(value)) {
return value;
}
var formatCfg = f.formatterCfg;
if (!formatCfg) {
formatCfg = formatter_1.formatterItem;
}
return formatter_1.valueFormatter(value, formatCfg);
}
});
});
var _f = util_1.parseJson(chart.customAttr), basicStyle = _f.basicStyle, tableCell = _f.tableCell, tableHeader = _f.tableHeader, tooltip = _f.tooltip;
// 表头分组
var headerGroup = tableHeader.headerGroup, showTableHeader = tableHeader.showTableHeader;
if (headerGroup && showTableHeader !== false) {
var headerGroupConfig = tableHeader.headerGroupConfig;
if ((_c = headerGroupConfig === null || headerGroupConfig === void 0 ? void 0 : headerGroupConfig.columns) === null || _c === void 0 ? void 0 : _c.length) {
var allKeys = columns.map(function (c) { return drillFieldMap[c] || c; });
var leafNodes = common_table_1.getLeafNodes(headerGroupConfig.columns);
var leafKeys = leafNodes.map(function (c) { return c.key; });
if (lodash_es_1.isEqual(leafKeys, allKeys)) {
if (Object.keys(drillFieldMap).length) {
var originField = Object.values(drillFieldMap)[0];
var drillField = Object.keys(drillFieldMap)[0];
var drillCol = common_table_1.getColumns([originField], headerGroupConfig.columns)[0];
drillCol.key = drillField;
}
columns.splice.apply(columns, __spreadArrays([0, columns.length], headerGroupConfig.columns));
meta.push.apply(meta, headerGroupConfig.meta);
}
}
}
// 空值处理
var newData = this.configEmptyDataStrategy(chart);
// data config
var s2DataConfig = {
fields: {
columns: columns
},
meta: meta,
data: newData
};
// options
var s2Options = {
width: containerDom.getBoundingClientRect().width,
height: containerDom.offsetHeight,
showSeriesNumber: tableHeader.showIndex,
conditions: this.configConditions(chart),
tooltip: {
getContainer: function () { return containerDom; },
renderTooltip: function (sheet) { return new common_table_1.SortTooltip(sheet); }
},
interaction: {
hoverHighlight: !(basicStyle.showHoverStyle === false),
scrollbarPosition: newData.length
? s2_1.ScrollbarPositionType.CONTENT
: s2_1.ScrollbarPositionType.CANVAS
}
};
s2Options.style = this.configStyle(chart, s2DataConfig);
// 自适应列宽模式下URL 字段的宽度固定为 120
if (basicStyle.tableColumnMode === 'adapt') {
var urlFields = fields.filter(function (field) { var _a; return field.deType === 7 && !((_a = axisMap[field.gisbiName]) === null || _a === void 0 ? void 0 : _a.hide); });
s2Options.style.colCfg.widthByFieldValue = urlFields === null || urlFields === void 0 ? void 0 : urlFields.reduce(function (p, n) {
var _a;
p[(_a = n.chartShowName) !== null && _a !== void 0 ? _a : n.name] = 120;
return p;
}, {});
}
if (tableCell.tableFreeze && !tableCell.mergeCells) {
s2Options.frozenColCount = (_d = tableCell.tableColumnFreezeHead) !== null && _d !== void 0 ? _d : 0;
s2Options.frozenRowCount = (_e = tableCell.tableRowFreezeHead) !== null && _e !== void 0 ? _e : 0;
}
// tooltip
this.configTooltip(chart, s2Options);
// 合并单元格
this.configMergeCells(chart, s2Options, s2DataConfig);
// 隐藏表头,保留顶部的分割线, 禁用表头横向 resize
if (tableHeader.showTableHeader === false) {
s2Options.style.colCfg.height = 1;
if (tableCell.showHorizonBorder === false) {
s2Options.style.colCfg.height = 0;
}
s2Options.interaction.resize = {
colCellVertical: false
};
s2Options.colCell = function (node, sheet, config) {
node.label = ' ';
return new s2_1.TableColCell(node, sheet, config);
};
}
else {
// header interaction
chart.container = container;
this.configHeaderInteraction(chart, s2Options);
s2Options.colCell = function (node, sheet, config) {
// 配置文本自动换行参数
node.autoWrap = tableCell.mergeCells ? false : basicStyle.autoWrap;
node.maxLines = basicStyle.maxLines;
return new common_table_1.CustomTableColCell(node, sheet, config);
};
}
// 序列号和总计行
this.configSummaryRowAndIndex(chart, pageInfo, s2Options, s2DataConfig);
// 开始渲染
var newChart = new s2_1.TableSheet(containerDom, s2DataConfig, s2Options);
// 总计紧贴在单元格后面
this.summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary);
// 开启自动换行
if (basicStyle.autoWrap && !tableCell.mergeCells) {
// 调整表头宽度时,计算表头高度
newChart.on(s2_1.S2Event.LAYOUT_RESIZE_COL_WIDTH, function (info) {
common_table_1.calculateHeaderHeight(info, newChart, tableHeader, basicStyle, null);
});
newChart.on(s2_1.S2Event.LAYOUT_AFTER_HEADER_LAYOUT, function (ev) {
var _a;
var maxHeight = newChart.store.get('autoCalcHeight');
if (maxHeight) {
// 更新列的高度
ev.colLeafNodes.forEach(function (n) { return (n.height = maxHeight); });
ev.colsHierarchy.height = maxHeight;
newChart.store.set('autoCalcHeight', undefined);
}
else {
if ((_a = ev.colLeafNodes) === null || _a === void 0 ? void 0 : _a.length) {
var _b = ev.colLeafNodes[0], value = _b.value, width = _b.width;
common_table_1.calculateHeaderHeight({ info: { meta: { value: value }, resizedWidth: width } }, newChart, tableHeader, basicStyle, ev);
}
}
});
}
// 自适应铺满
if (basicStyle.tableColumnMode === 'adapt') {
newChart.on(s2_1.S2Event.LAYOUT_RESIZE_COL_WIDTH, function () {
newChart.store.set('lastLayoutResult', newChart.facet.layoutResult);
});
newChart.on(s2_1.S2Event.LAYOUT_AFTER_HEADER_LAYOUT, function (ev) {
var _a, _b;
var lastLayoutResult = newChart.store.get('lastLayoutResult');
if (lastLayoutResult) {
// 拖动表头 resize
var widthByFieldValue_1 = (_b = (_a = newChart.options.style) === null || _a === void 0 ? void 0 : _a.colCfg) === null || _b === void 0 ? void 0 : _b.widthByFieldValue;
var lastLayoutWidthMap_1 = (lastLayoutResult === null || lastLayoutResult === void 0 ? void 0 : lastLayoutResult.colLeafNodes.reduce(function (p, n) {
var _a;
p[n.value] = (_a = widthByFieldValue_1 === null || widthByFieldValue_1 === void 0 ? void 0 : widthByFieldValue_1[n.value]) !== null && _a !== void 0 ? _a : n.width;
return p;
}, {})) || {};
var totalWidth_1 = ev.colLeafNodes.reduce(function (p, n) {
n.width = lastLayoutWidthMap_1[n.value] || n.width;
n.x = p;
return p + n.width;
}, 0);
// 处理分组的单元格,宽度为所有叶子节点之和
ev.colNodes.forEach(function (n) {
if (n.colIndex === -1) {
n.width = calcTreeWidth(n);
n.x = getStartPosition(n);
}
});
ev.colsHierarchy.width = totalWidth_1;
newChart.store.set('lastLayoutResult', undefined);
return;
}
// 第一次渲染初始化,把图片字段固定为 120 进行计算
var urlFields = fields
.filter(function (field) { var _a; return field.deType === 7 && !((_a = axisMap[field.gisbiName]) === null || _a === void 0 ? void 0 : _a.hide); })
.map(function (f) { return f.gisbiName; });
var totalWidthWithImg = ev.colLeafNodes.reduce(function (p, n) {
return p + (urlFields.includes(n.field) ? 120 : n.width);
}, 0);
var containerWidth = containerDom.getBoundingClientRect().width;
if (containerWidth <= totalWidthWithImg) {
// 图库计算的布局宽度已经大于等于容器宽度,不需要再扩大,但是需要处理非整数宽度值,不然会出现透明细线
ev.colLeafNodes.reduce(function (p, n) {
n.width = Math.round(n.width);
n.x = p;
return p + n.width;
}, 0);
return;
}
// 图片字段固定 120, 剩余宽度按比例均摊到其他字段进行扩大
var totalWidthWithoutImg = ev.colLeafNodes.reduce(function (p, n) {
return p + (urlFields.includes(n.field) ? 0 : n.width);
}, 0);
var restWidth = containerWidth - urlFields.length * 120;
var scale = restWidth / totalWidthWithoutImg;
var totalWidth = ev.colLeafNodes.reduce(function (p, n) {
n.width = urlFields.includes(n.field) ? 120 : Math.round(n.width * scale);
n.x = p;
return p + n.width;
}, 0);
// 处理分组的单元格,宽度为所有叶子节点之和
ev.colNodes.forEach(function (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;
}
ev.colsHierarchy.width = containerWidth;
});
}
// 空数据时表格样式
common_table_1.configEmptyDataStyle(newChart, basicStyle, newData, container);
// click
newChart.on(s2_1.S2Event.DATA_CELL_CLICK, function (ev) {
var cell = newChart.getCell(ev.target);
var meta = cell.getMeta();
var nameIdMap = fields.reduce(function (pre, next) {
pre[next['gisbiName']] = next['id'];
return pre;
}, {});
var rowData = newChart.dataSet.getRowData(meta);
var dimensionList = [];
for (var key in rowData) {
if (nameIdMap[key]) {
dimensionList.push({ id: nameIdMap[key], value: rowData[key] });
}
}
var param = {
x: ev.x,
y: ev.y,
data: {
dimensionList: dimensionList,
name: nameIdMap[meta.valueField],
sourceType: 'table-info',
quotaList: []
}
};
action(param);
});
// 合并的单元格直接复用数据单元格的事件
newChart.on(s2_1.S2Event.MERGED_CELLS_CLICK, function (e) { return newChart.emit(s2_1.S2Event.DATA_CELL_CLICK, e); });
// tooltip
var show = tooltip.show;
if (show) {
newChart.on(s2_1.S2Event.COL_CELL_HOVER, function (event) { return _this.showTooltip(newChart, event, meta); });
newChart.on(s2_1.S2Event.DATA_CELL_HOVER, function (event) { return _this.showTooltip(newChart, event, meta); });
newChart.on(s2_1.S2Event.MERGED_CELLS_HOVER, function (event) { return _this.showTooltip(newChart, event, meta); });
// touch
this.configTouchEvent(newChart, drawOption, meta);
}
// header resize
newChart.on(s2_1.S2Event.LAYOUT_RESIZE_COL_WIDTH, function (ev) { return resizeAction(ev); });
// right click
newChart.on(s2_1.S2Event.GLOBAL_CONTEXT_MENU, function (event) { return common_table_1.copyContent(newChart, event, meta); });
// theme
var customTheme = this.configTheme(chart);
newChart.setThemeCfg({ theme: customTheme });
return newChart;
};
TableInfo.prototype.configTheme = function (chart) {
var theme = _super.prototype.configTheme.call(this, chart);
var _a = util_1.parseJson(chart.customAttr), basicStyle = _a.basicStyle, tableCell = _a.tableCell;
if (tableCell.mergeCells) {
var tableFontColor = util_1.hexColorToRGBA(tableCell.tableFontColor, basicStyle.alpha);
var tableItemBgColor = tableCell.tableItemBgColor;
if (!util_1.isAlphaColor(tableItemBgColor)) {
tableItemBgColor = util_1.hexColorToRGBA(tableItemBgColor, basicStyle.alpha);
}
var tableBorderColor = basicStyle.tableBorderColor;
var tableItemAlign = tableCell.tableItemAlign, tableItemFontSize = tableCell.tableItemFontSize;
var fontStyle = tableCell.isItalic ? 'italic' : 'normal';
var fontWeight = tableCell.isBolder === false ? 'normal' : 'bold';
var mergeCellTheme = {
dataCell: {
cell: {
crossBackgroundColor: tableItemBgColor
}
},
mergedCell: {
cell: {
backgroundColor: tableItemBgColor,
crossBackgroundColor: tableItemBgColor,
horizontalBorderColor: tableBorderColor,
verticalBorderColor: tableBorderColor,
horizontalBorderWidth: tableCell.showHorizonBorder ? 1 : 0,
verticalBorderWidth: tableCell.showVerticalBorder ? 1 : 0
},
bolderText: {
fill: tableFontColor,
textAlign: tableItemAlign,
fontSize: tableItemFontSize,
fontStyle: fontStyle,
fontWeight: fontWeight
},
text: {
fill: tableFontColor,
textAlign: tableItemAlign,
fontSize: tableItemFontSize,
fontStyle: fontStyle,
fontWeight: fontWeight
},
measureText: {
fill: tableFontColor,
textAlign: tableItemAlign,
fontSize: tableItemFontSize,
fontStyle: fontStyle,
fontWeight: fontWeight
},
seriesText: {
fill: tableFontColor,
textAlign: tableItemAlign,
fontSize: tableItemFontSize,
fontStyle: fontStyle,
fontWeight: fontWeight
}
}
};
lodash_es_1.merge(theme, mergeCellTheme);
}
return theme;
};
TableInfo.prototype.configSummaryRowAndIndex = function (chart, pageInfo, s2Options, s2DataConfig) {
var _a, _b;
var _c = util_1.parseJson(chart.customAttr), tableHeader = _c.tableHeader, basicStyle = _c.basicStyle, tableCell = _c.tableCell;
var fields = (_b = (_a = chart.data) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : [];
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
var indexLabel_1 = tableHeader.indexLabel;
if (!indexLabel_1) {
indexLabel_1 = '';
}
s2Options.layoutCoordinate = function (_, __, col) {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel_1;
col.value = indexLabel_1;
}
};
}
var showSummary = basicStyle.showSummary, summaryLabel = basicStyle.summaryLabel;
var data = s2DataConfig.data;
var xAxis = chart.xAxis;
if (showSummary && (data === null || data === void 0 ? void 0 : data.length)) {
// 设置汇总行高度和表头一致
var heightByField = {};
heightByField[data.length] = tableHeader.tableTitleHeight;
s2Options.style.rowCfg = { heightByField: heightByField };
// 计算汇总加入到数据里,冻结最后一行
s2Options.frozenTrailingRowCount = 1;
var axis = lodash_es_1.filter(xAxis, function (axis) { return [2, 3, 4].includes(axis.deType); });
var summaryObj = common_table_1.getSummaryRow(data, axis, basicStyle.seriesSummary);
data.push(summaryObj);
}
s2Options.dataCell = function (viewMeta) {
var _a;
// 总计行处理
if (showSummary && viewMeta.rowIndex === data.length - 1) {
if (viewMeta.colIndex === 0) {
if (tableHeader.showIndex) {
viewMeta.fieldValue = summaryLabel !== null && summaryLabel !== void 0 ? summaryLabel : t('chart.total_show');
}
else {
// 第一列不是数值类型的,显示总计
if (![2, 3, 4].includes((_a = xAxis === null || xAxis === void 0 ? void 0 : xAxis[0]) === null || _a === void 0 ? void 0 : _a.deType)) {
viewMeta.fieldValue = summaryLabel !== null && summaryLabel !== void 0 ? summaryLabel : t('chart.total_show');
}
}
}
return new common_table_1.SummaryCell(viewMeta, viewMeta === null || viewMeta === void 0 ? void 0 : viewMeta.spreadsheet);
}
var field = fields.find(function (f) { return f.gisbiName === viewMeta.valueField; });
if ((field === null || field === void 0 ? void 0 : field.deType) === 7 && chart.showPosition !== 'dialog') {
return new ImageCell(viewMeta, viewMeta === null || viewMeta === void 0 ? void 0 : viewMeta.spreadsheet);
}
if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
if (tableCell.mergeCells) {
viewMeta.fieldValue = common_table_1.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 common_table_1.CustomDataCell(viewMeta, viewMeta === null || viewMeta === void 0 ? void 0 : viewMeta.spreadsheet);
};
};
TableInfo.prototype.summaryRowStyle = function (newChart, newData, tableCell, tableHeader, showSummary) {
if (!showSummary || !newData.length)
return;
var columns = newChart.dataCfg.fields.columns;
var showHeader = tableHeader.showTableHeader === true;
// 不显示表头时,减少一个表头的高度
var headerAndSummaryHeight = showHeader ? getMaxTreeDepth(columns) + 1 : 1;
newChart.on(s2_1.S2Event.LAYOUT_BEFORE_RENDER, function () {
var totalHeight = tableHeader.tableTitleHeight * headerAndSummaryHeight +
tableCell.tableItemHeight * (newData.length - 1);
if (totalHeight < newChart.container.cfg.height) {
newChart.options.height =
totalHeight < newChart.container.cfg.height - 8 ? totalHeight + 8 : totalHeight;
}
});
};
return TableInfo;
}(s2_2.S2ChartView));
exports.TableInfo = TableInfo;
function calcTreeWidth(node) {
var _a;
if (!((_a = node.children) === null || _a === void 0 ? void 0 : _a.length)) {
return node.width;
}
return node.children.reduce(function (pre, cur) {
return pre + calcTreeWidth(cur);
}, 0);
}
function getStartPosition(node) {
var _a;
if (!((_a = node.children) === null || _a === void 0 ? void 0 : _a.length)) {
return node.x;
}
return getStartPosition(node.children[0]);
}
function getMaxTreeDepth(nodes) {
if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length)) {
return 0;
}
return Math.max.apply(Math, nodes.map(function (node) {
var _a;
if (!((_a = node.children) === null || _a === void 0 ? void 0 : _a.length)) {
return 1;
}
return getMaxTreeDepth(node.children) + 1;
}));
}

View File

@ -0,0 +1,356 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
exports.__esModule = true;
exports.TableNormal = void 0;
var useI18n_1 = require("@/hooks/web/useI18n");
var formatter_1 = require("@/views/chart/components/js/formatter");
var common_table_1 = require("@/views/chart/components/js/panel/common/common_table");
var s2_1 = require("@/views/chart/components/js/panel/types/impl/s2");
var util_1 = require("@/views/chart/components/js/util");
var s2_2 = require("@antv/s2");
var lodash_es_1 = require("lodash-es");
var common_1 = require("./common");
var t = useI18n_1.useI18n().t;
/**
* 汇总表
*/
var TableNormal = /** @class */ (function (_super) {
__extends(TableNormal, _super);
function TableNormal() {
var _this = _super.call(this, 'table-normal', []) || this;
_this.properties = common_1.TABLE_EDITOR_PROPERTY;
_this.propertyInner = __assign(__assign({}, common_1.TABLE_EDITOR_PROPERTY_INNER), { 'table-header-selector': __spreadArrays(common_1.TABLE_EDITOR_PROPERTY_INNER['table-header-selector'], [
'tableHeaderSort',
'showTableHeader'
]), 'basic-style-selector': __spreadArrays(common_1.TABLE_EDITOR_PROPERTY_INNER['basic-style-selector'], [
'tablePageMode',
'showHoverStyle'
]), 'table-cell-selector': __spreadArrays(common_1.TABLE_EDITOR_PROPERTY_INNER['table-cell-selector'], [
'tableFreeze',
'tableColumnFreezeHead',
'tableRowFreezeHead'
]), 'summary-selector': ['showSummary', 'summaryLabel'] });
_this.axis = ['xAxis', 'yAxis', 'drill', 'filter'];
_this.axisConfig = {
xAxis: {
name: t('chart.drag_block_table_data_column') + " / " + t('chart.dimension'),
type: 'd'
},
yAxis: {
name: t('chart.drag_block_table_data_column') + " / " + t('chart.quota'),
type: 'q'
}
};
return _this;
}
TableNormal.prototype.setupDefaultOptions = function (chart) {
chart.xAxis = [];
return chart;
};
TableNormal.prototype.drawChart = function (drawOption) {
var _this = this;
var _a, _b;
var container = drawOption.container, chart = drawOption.chart, action = drawOption.action, pageInfo = drawOption.pageInfo, resizeAction = drawOption.resizeAction;
var containerDom = document.getElementById(container);
if (!containerDom)
return;
// fields
var fields = chart.data.fields;
var columns = [];
var meta = [];
if (chart.drill) {
// 下钻过滤字段
var filterFields_1 = chart.drillFilters.map(function (i) { return i.fieldId; });
// 下钻入口的字段下标
var drillFieldId_1 = chart.drillFields[0].id;
var drillFieldIndex = chart.xAxis.findIndex(function (ele) { return ele.id === drillFieldId_1; });
// 当前下钻字段
var curDrillFieldId_1 = chart.drillFields[filterFields_1.length].id;
var curDrillField = fields.filter(function (ele) { return ele.id === curDrillFieldId_1; });
filterFields_1.push(curDrillFieldId_1);
// 移除下钻字段,把当前下钻字段插入到下钻入口位置
fields = fields.filter(function (ele) {
return !filterFields_1.includes(ele.id);
});
fields.splice.apply(fields, __spreadArrays([drillFieldIndex, 0], curDrillField));
}
var axisMap = __spreadArrays(chart.xAxis, chart.yAxis).reduce(function (pre, cur) {
pre[cur.gisbiName] = cur;
return pre;
}, {});
// add drill list
fields.forEach(function (ele) {
var _a;
var f = axisMap[ele.gisbiName];
if ((f === null || f === void 0 ? void 0 : f.hide) === true) {
return;
}
columns.push(ele.gisbiName);
meta.push({
field: ele.gisbiName,
name: (_a = ele.chartShowName) !== null && _a !== void 0 ? _a : ele.name,
formatter: function (value) {
if (!f) {
return value;
}
if (value === null || value === undefined) {
return value;
}
if (![2, 3, 4].includes(f.deType) || !lodash_es_1.isNumber(value)) {
return value;
}
var formatCfg = f.formatterCfg;
if (!formatCfg) {
formatCfg = formatter_1.formatterItem;
}
return formatter_1.valueFormatter(value, formatCfg);
}
});
});
// 空值处理
var newData = this.configEmptyDataStrategy(chart);
// data config
var s2DataConfig = {
fields: {
columns: columns
},
meta: meta,
data: newData
};
var _c = util_1.parseJson(chart.customAttr), basicStyle = _c.basicStyle, tableCell = _c.tableCell, tableHeader = _c.tableHeader, tooltip = _c.tooltip;
// options
var s2Options = {
width: containerDom.getBoundingClientRect().width,
height: containerDom.offsetHeight,
showSeriesNumber: tableHeader.showIndex,
conditions: this.configConditions(chart),
tooltip: {
getContainer: function () { return containerDom; },
renderTooltip: function (sheet) { return new common_table_1.SortTooltip(sheet); }
},
interaction: {
hoverHighlight: !(basicStyle.showHoverStyle === false),
scrollbarPosition: newData.length
? s2_2.ScrollbarPositionType.CONTENT
: s2_2.ScrollbarPositionType.CANVAS
}
};
// 列宽设置
s2Options.style = this.configStyle(chart, s2DataConfig);
// 行列冻结
if (tableCell.tableFreeze) {
s2Options.frozenColCount = (_a = tableCell.tableColumnFreezeHead) !== null && _a !== void 0 ? _a : 0;
s2Options.frozenRowCount = (_b = tableCell.tableRowFreezeHead) !== null && _b !== void 0 ? _b : 0;
}
// tooltip
this.configTooltip(chart, s2Options);
// 隐藏表头,保留顶部的分割线, 禁用表头横向 resize
if (tableHeader.showTableHeader === false) {
s2Options.style.colCfg.height = 1;
if (tableCell.showHorizonBorder === false) {
s2Options.style.colCfg.height = 0;
}
s2Options.interaction.resize = {
colCellVertical: false
};
s2Options.colCell = function (node, sheet, config) {
node.label = ' ';
return new s2_2.TableColCell(node, sheet, config);
};
}
else {
// header interaction
chart.container = container;
this.configHeaderInteraction(chart, s2Options);
}
// 配置总计和序号列
this.configSummaryRowAndIndex(chart, pageInfo, s2Options, s2DataConfig);
// 开始渲染
var newChart = new s2_2.TableSheet(containerDom, s2DataConfig, s2Options);
// 总计紧贴在单元格后面
this.summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary);
// 自适应铺满
if (basicStyle.tableColumnMode === 'adapt') {
newChart.on(s2_2.S2Event.LAYOUT_RESIZE_COL_WIDTH, function () {
newChart.store.set('lastLayoutResult', newChart.facet.layoutResult);
});
newChart.on(s2_2.S2Event.LAYOUT_AFTER_HEADER_LAYOUT, function (ev) {
var _a, _b;
var lastLayoutResult = newChart.store.get('lastLayoutResult');
if (lastLayoutResult) {
// 拖动表头 resize
var widthByFieldValue_1 = (_b = (_a = newChart.options.style) === null || _a === void 0 ? void 0 : _a.colCfg) === null || _b === void 0 ? void 0 : _b.widthByFieldValue;
var lastLayoutWidthMap_1 = (lastLayoutResult === null || lastLayoutResult === void 0 ? void 0 : lastLayoutResult.colLeafNodes.reduce(function (p, n) {
var _a;
p[n.value] = (_a = widthByFieldValue_1 === null || widthByFieldValue_1 === void 0 ? void 0 : widthByFieldValue_1[n.value]) !== null && _a !== void 0 ? _a : n.width;
return p;
}, {})) || {};
var totalWidth_1 = ev.colLeafNodes.reduce(function (p, n) {
n.width = lastLayoutWidthMap_1[n.value] || n.width;
n.x = p;
return p + n.width;
}, 0);
ev.colsHierarchy.width = totalWidth_1;
newChart.store.set('lastLayoutResult', undefined);
return;
}
var containerWidth = containerDom.getBoundingClientRect().width;
var scale = containerWidth / ev.colsHierarchy.width;
if (scale <= 1) {
// 图库计算的布局宽度已经大于等于容器宽度,不需要再扩大,但是需要处理非整数宽度值,不然会出现透明细线
ev.colLeafNodes.reduce(function (p, n) {
n.width = Math.round(n.width);
n.x = p;
return p + n.width;
}, 0);
return;
}
var totalWidth = ev.colLeafNodes.reduce(function (p, n) {
n.width = Math.round(n.width * scale);
n.x = p;
return p + n.width;
}, 0);
if (totalWidth > containerWidth) {
// 从最后一列减掉
ev.colLeafNodes[ev.colLeafNodes.length - 1].width -= totalWidth - containerWidth;
}
ev.colsHierarchy.width = containerWidth;
});
}
common_table_1.configEmptyDataStyle(newChart, basicStyle, newData, container);
// click
newChart.on(s2_2.S2Event.DATA_CELL_CLICK, function (ev) {
var cell = newChart.getCell(ev.target);
var meta = cell.getMeta();
var nameIdMap = fields.reduce(function (pre, next) {
pre[next['gisbiName']] = next['id'];
return pre;
}, {});
var rowData = newChart.dataSet.getRowData(meta);
var dimensionList = [];
for (var key in rowData) {
if (nameIdMap[key]) {
dimensionList.push({ id: nameIdMap[key], value: rowData[key] });
}
}
var param = {
x: ev.x,
y: ev.y,
data: {
dimensionList: dimensionList,
name: nameIdMap[meta.valueField],
sourceType: 'table-normal',
quotaList: []
}
};
action(param);
});
// tooltip
var show = tooltip.show;
if (show) {
newChart.on(s2_2.S2Event.COL_CELL_HOVER, function (event) { return _this.showTooltip(newChart, event, meta); });
newChart.on(s2_2.S2Event.DATA_CELL_HOVER, function (event) { return _this.showTooltip(newChart, event, meta); });
// touch
this.configTouchEvent(newChart, drawOption, meta);
}
// header resize
newChart.on(s2_2.S2Event.LAYOUT_RESIZE_COL_WIDTH, function (ev) { return resizeAction(ev); });
// right click
newChart.on(s2_2.S2Event.GLOBAL_CONTEXT_MENU, function (event) { return common_table_1.copyContent(newChart, event, meta); });
// theme
var customTheme = this.configTheme(chart);
newChart.setThemeCfg({ theme: customTheme });
return newChart;
};
TableNormal.prototype.configSummaryRowAndIndex = function (chart, pageInfo, s2Options, s2DataConfig) {
var _a = util_1.parseJson(chart.customAttr), tableHeader = _a.tableHeader, basicStyle = _a.basicStyle;
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
var indexLabel_1 = tableHeader.indexLabel;
if (!indexLabel_1) {
indexLabel_1 = '';
}
s2Options.layoutCoordinate = function (_, __, col) {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel_1;
col.value = indexLabel_1;
}
};
}
var showSummary = basicStyle.showSummary, summaryLabel = basicStyle.summaryLabel;
var data = s2DataConfig.data;
var xAxis = chart.xAxis, yAxis = chart.yAxis;
if (showSummary && (data === null || data === void 0 ? void 0 : data.length)) {
// 设置汇总行高度和表头一致
var heightByField = {};
heightByField[data.length] = tableHeader.tableTitleHeight;
s2Options.style.rowCfg = { heightByField: heightByField };
// 计算汇总加入到数据里,冻结最后一行
s2Options.frozenTrailingRowCount = 1;
var summaryObj = common_table_1.getSummaryRow(data, yAxis, basicStyle.seriesSummary);
data.push(summaryObj);
}
s2Options.dataCell = function (viewMeta) {
// 总计行处理
if (showSummary && viewMeta.rowIndex === data.length - 1) {
if (viewMeta.colIndex === 0) {
if (tableHeader.showIndex || (xAxis === null || xAxis === void 0 ? void 0 : xAxis.length)) {
viewMeta.fieldValue = summaryLabel !== null && summaryLabel !== void 0 ? summaryLabel : t('chart.total_show');
}
}
return new common_table_1.SummaryCell(viewMeta, viewMeta === null || viewMeta === void 0 ? void 0 : viewMeta.spreadsheet);
}
if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
viewMeta.fieldValue = pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1;
}
return new common_table_1.CustomDataCell(viewMeta, viewMeta === null || viewMeta === void 0 ? void 0 : viewMeta.spreadsheet);
};
};
TableNormal.prototype.summaryRowStyle = function (newChart, newData, tableCell, tableHeader, showSummary) {
if (!showSummary || !newData.length)
return;
newChart.on(s2_2.S2Event.LAYOUT_BEFORE_RENDER, function () {
var showHeader = tableHeader.showTableHeader === true;
// 不显示表头时,减少一个表头的高度
var headerAndSummaryHeight = showHeader ? 2 : 1;
var totalHeight = tableHeader.tableTitleHeight * headerAndSummaryHeight +
tableCell.tableItemHeight * (newData.length - 1);
if (totalHeight < newChart.container.cfg.height) {
newChart.options.height =
totalHeight < newChart.container.cfg.height - 8 ? totalHeight + 8 : totalHeight;
}
});
};
return TableNormal;
}(s2_1.S2ChartView));
exports.TableNormal = TableNormal;

File diff suppressed because it is too large Load Diff

View File

@ -119,11 +119,12 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
if (!xAxis?.length || !xAxisExt?.length || !extColor?.length) {
return
}
const xField = xAxis[0].dataeaseName
const xFieldExt = xAxisExt[0].dataeaseName
const extColorField = extColor[0].dataeaseName
const xField = xAxis[0].gisbiName
const xFieldExt = xAxisExt[0].gisbiName
const extColorField = extColor[0].gisbiName
// data
const data = cloneDeep(chart.data.tableRow)
const tmpData = cloneDeep(chart.data.tableRow)
const data = tmpData.filter(cell => cell[xField] && cell[xFieldExt] && cell[extColorField])
data.forEach(i => {
Object.keys(i).forEach(key => {
if (key === '*') {
@ -171,10 +172,10 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
const dimensionList = []
chart.data.fields.forEach(item => {
Object.keys(pointData).forEach(key => {
if (key.startsWith('f_') && item.dataeaseName === key) {
if (key.startsWith('f_') && item.gisbiName === key) {
dimensionList.push({
id: item.id,
dataeaseName: item.dataeaseName,
gisbiName: item.gisbiName,
value: pointData[key]
})
}
@ -197,7 +198,7 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
newChart.on('afterrender', ev => {
const l = JSON.parse(JSON.stringify(parseJson(chart.customStyle).legend))
if (l.show) {
const rail = ev.view.getController('legend').option[extColor[0].dataeaseName]?.['rail']
const rail = ev.view.getController('legend').option[extColor[0].gisbiName]?.['rail']
if (rail) {
rail.defaultLength = this.getDefaultLength(chart, l)
}
@ -207,6 +208,12 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
return newChart
}
protected configTheme(chart: Chart, options: HeatmapOptions): HeatmapOptions {
const tmp = super.configTheme(chart, options)
tmp.theme.innerLabels.offset = 0
return tmp
}
protected configBasicStyle(chart: Chart, options: HeatmapOptions): HeatmapOptions {
const basicStyle = parseJson(chart.customAttr).basicStyle
const color = basicStyle.colors?.map(ele => {
@ -235,7 +242,7 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
const items = []
const createItem = (fieldObj, items, originalItems) => {
const name = fieldObj?.chartShowName ? fieldObj?.chartShowName : fieldObj?.name
let value = originalItems[0].data[fieldObj.dataeaseName]
let value = originalItems[0].data[fieldObj.gisbiName]
if (!isNaN(Number(value))) {
value = valueFormatter(value, fieldObj?.formatterCfg)
}
@ -329,7 +336,7 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
position: 'middle',
layout,
formatter: data => {
const value = data[extColor[0]?.dataeaseName]
const value = data[extColor[0]?.gisbiName]
if (!isNaN(Number(value))) {
return valueFormatter(value, extColor[0]?.formatterCfg)
}

View File

@ -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 '@/data-visualization/hooks/web/useI18n'
import { isEqual, isNumber, merge } from 'lodash-es'
import { filter, isEqual, isNumber, merge } from 'lodash-es'
import {
copyContent,
CustomDataCell,
@ -22,37 +22,19 @@ import {
getRowIndex,
calculateHeaderHeight,
SortTooltip,
configSummaryRow,
summaryRowStyle,
configEmptyDataStyle,
getLeafNodes,
getColumns
getColumns,
drawImage,
getSummaryRow,
SummaryCell
} from '@/data-visualization/chart/components/js/panel/common/common_table'
const { t } = useI18n()
class ImageCell extends CustomDataCell {
protected drawTextShape(): void {
const img = new Image()
const { x, y, width, height, fieldValue } = this.meta
img.src = fieldValue as string
img.setAttribute('crossOrigin', 'anonymous')
img.onload = () => {
!this.cfg.children && (this.cfg.children = [])
const { width: imgWidth, height: imgHeight } = img
const ratio = Math.max(imgWidth / width, imgHeight / height)
// 不铺满,部分留白
const imgShowWidth = (imgWidth / ratio) * 0.8
const imgShowHeight = (imgHeight / ratio) * 0.8
this.textShape = this.addShape('image', {
attrs: {
x: x + (imgShowWidth < width ? (width - imgShowWidth) / 2 : 0),
y: y + (imgShowHeight < height ? (height - imgShowHeight) / 2 : 0),
width: imgShowWidth,
height: imgShowHeight,
img
}
})
}
drawImage.apply(this)
}
}
/**
@ -75,9 +57,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
'alpha',
'tablePageMode',
'showHoverStyle',
'autoWrap',
'showSummary',
'summaryLabel'
'autoWrap'
],
'table-cell-selector': [
...TABLE_EDITOR_PROPERTY_INNER['table-cell-selector'],
@ -85,7 +65,8 @@ export class TableInfo extends S2ChartView<TableSheet> {
'tableColumnFreezeHead',
'tableRowFreezeHead',
'mergeCells'
]
],
'summary-selector': ['showSummary', 'summaryLabel']
}
axis: AxisType[] = ['xAxis', 'filter', 'drill']
axisConfig: AxisConfig = {
@ -103,7 +84,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
const columns = []
const meta = []
const axisMap = chart.xAxis.reduce((pre, cur) => {
pre[cur.dataeaseName] = cur
pre[cur.gisbiName] = cur
return pre
}, {})
const drillFieldMap = {}
@ -121,17 +102,17 @@ export class TableInfo extends S2ChartView<TableSheet> {
fields = fields.filter(ele => {
return !filterFields.includes(ele.id)
})
drillFieldMap[curDrillField.dataeaseName] = chart.drillFields[0].dataeaseName
drillFieldMap[curDrillField.gisbiName] = chart.drillFields[0].gisbiName
fields.splice(drillFieldIndex, 0, curDrillField)
}
fields.forEach(ele => {
const f = axisMap[ele.dataeaseName]
const f = axisMap[ele.gisbiName]
if (f?.hide === true) {
return
}
columns.push(ele.dataeaseName)
columns.push(ele.gisbiName)
meta.push({
field: ele.dataeaseName,
field: ele.gisbiName,
name: ele.chartShowName ?? ele.name,
formatter: function (value) {
if (!f) {
@ -140,7 +121,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
if (value === null || value === undefined) {
return value
}
if (![2, 3].includes(f.deType) || !isNumber(value)) {
if (![2, 3, 4].includes(f.deType) || !isNumber(value)) {
return value
}
let formatCfg = f.formatterCfg
@ -204,7 +185,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
// 自适应列宽模式下URL 字段的宽度固定为 120
if (basicStyle.tableColumnMode === 'adapt') {
const urlFields = fields.filter(
field => field.deType === 7 && !axisMap[field.dataeaseName]?.hide
field => field.deType === 7 && !axisMap[field.gisbiName]?.hide
)
s2Options.style.colCfg.widthByFieldValue = urlFields?.reduce((p, n) => {
p[n.chartShowName ?? n.name] = 120
@ -215,37 +196,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)
// 合并单元格
@ -274,12 +224,12 @@ 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)
// 总计紧贴在单元格后面
summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary)
this.summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary)
// 开启自动换行
if (basicStyle.autoWrap && !tableCell.mergeCells) {
// 调整表头宽度时,计算表头高度
@ -340,8 +290,8 @@ export class TableInfo extends S2ChartView<TableSheet> {
}
// 第一次渲染初始化,把图片字段固定为 120 进行计算
const urlFields = fields
.filter(field => field.deType === 7 && !axisMap[field.dataeaseName]?.hide)
.map(f => f.dataeaseName)
.filter(field => field.deType === 7 && !axisMap[field.gisbiName]?.hide)
.map(f => f.gisbiName)
const totalWidthWithImg = ev.colLeafNodes.reduce((p, n) => {
return p + (urlFields.includes(n.field) ? 120 : n.width)
}, 0)
@ -386,7 +336,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
const cell = newChart.getCell(ev.target)
const meta = cell.getMeta() as ViewMeta
const nameIdMap = fields.reduce((pre, next) => {
pre[next['dataeaseName']] = next['id']
pre[next['gisbiName']] = next['id']
return pre
}, {})
@ -417,13 +367,13 @@ export class TableInfo extends S2ChartView<TableSheet> {
newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
newChart.on(S2Event.MERGED_CELLS_HOVER, event => this.showTooltip(newChart, event, meta))
// touch
this.configTouchEvent(newChart, drawOption, meta)
}
// header resize
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev))
// right click
newChart.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(newChart, event, meta))
// touch
this.configTouchEvent(newChart, drawOption, meta)
// theme
const customTheme = this.configTheme(chart)
newChart.setThemeCfg({ theme: customTheme })
@ -444,6 +394,11 @@ export class TableInfo extends S2ChartView<TableSheet> {
const fontStyle = tableCell.isItalic ? 'italic' : 'normal'
const fontWeight = tableCell.isBolder === false ? 'normal' : 'bold'
const mergeCellTheme: S2Theme = {
dataCell: {
cell: {
crossBackgroundColor: tableItemBgColor
}
},
mergedCell: {
cell: {
backgroundColor: tableItemBgColor,
@ -488,6 +443,92 @@ 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.gisbiName === 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)
}
}
protected summaryRowStyle(newChart: TableSheet, newData, tableCell, tableHeader, showSummary) {
if (!showSummary || !newData.length) return
const columns = newChart.dataCfg.fields.columns
const showHeader = tableHeader.showTableHeader === true
// 不显示表头时,减少一个表头的高度
const headerAndSummaryHeight = showHeader ? getMaxTreeDepth(columns) + 1 : 1
newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => {
const totalHeight =
tableHeader.tableTitleHeight * headerAndSummaryHeight +
tableCell.tableItemHeight * (newData.length - 1)
if (totalHeight < newChart.container.cfg.height) {
newChart.options.height =
totalHeight < newChart.container.cfg.height - 8 ? totalHeight + 8 : totalHeight
}
})
}
constructor() {
super('table-info', [])
}
@ -508,3 +549,17 @@ function getStartPosition(node) {
}
return getStartPosition(node.children[0])
}
function getMaxTreeDepth(nodes) {
if (!nodes?.length) {
return 0
}
return Math.max(
...nodes.map(node => {
if (!node.children?.length) {
return 1
}
return getMaxTreeDepth(node.children) + 1
})
)
}

View File

@ -2,10 +2,11 @@ import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { formatterItem, valueFormatter } from '@/data-visualization/chart/components/js/formatter'
import {
configEmptyDataStyle,
configSummaryRow,
copyContent,
CustomDataCell,
getSummaryRow,
SortTooltip,
summaryRowStyle
SummaryCell
} from '@/data-visualization/chart/components/js/panel/common/common_table'
import { S2ChartView, S2DrawOptions } from '@/data-visualization/chart/components/js/panel/types/impl/s2'
import { parseJson } from '@/data-visualization/chart/components/js/util'
@ -19,7 +20,7 @@ import {
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()
@ -37,8 +38,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
],
'basic-style-selector': [
...TABLE_EDITOR_PROPERTY_INNER['basic-style-selector'],
'showSummary',
'summaryLabel',
'tablePageMode',
'showHoverStyle'
],
'table-cell-selector': [
@ -46,7 +46,8 @@ export class TableNormal extends S2ChartView<TableSheet> {
'tableFreeze',
'tableColumnFreezeHead',
'tableRowFreezeHead'
]
],
'summary-selector': ['showSummary', 'summaryLabel']
}
axis: AxisType[] = ['xAxis', 'yAxis', 'drill', 'filter']
axisConfig: AxisConfig = {
@ -66,7 +67,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
}
drawChart(drawOption: S2DrawOptions<TableSheet>): TableSheet {
const { container, chart, action, resizeAction } = drawOption
const { container, chart, action, pageInfo, resizeAction } = drawOption
const containerDom = document.getElementById(container)
if (!containerDom) return
@ -92,18 +93,18 @@ export class TableNormal extends S2ChartView<TableSheet> {
fields.splice(drillFieldIndex, 0, ...curDrillField)
}
const axisMap = [...chart.xAxis, ...chart.yAxis].reduce((pre, cur) => {
pre[cur.dataeaseName] = cur
pre[cur.gisbiName] = cur
return pre
}, {})
// add drill list
fields.forEach(ele => {
const f = axisMap[ele.dataeaseName]
const f = axisMap[ele.gisbiName]
if (f?.hide === true) {
return
}
columns.push(ele.dataeaseName)
columns.push(ele.gisbiName)
meta.push({
field: ele.dataeaseName,
field: ele.gisbiName,
name: ele.chartShowName ?? ele.name,
formatter: function (value) {
if (!f) {
@ -112,7 +113,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
if (value === null || value === undefined) {
return value
}
if (![2, 3].includes(f.deType) || !isNumber(value)) {
if (![2, 3, 4].includes(f.deType) || !isNumber(value)) {
return value
}
let formatCfg = f.formatterCfg
@ -160,19 +161,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
}
}
}
// tooltip
this.configTooltip(chart, s2Options)
// 隐藏表头,保留顶部的分割线, 禁用表头横向 resize
@ -193,13 +181,12 @@ 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)
// 总计紧贴在单元格后面
summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary)
this.summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary)
// 自适应铺满
if (basicStyle.tableColumnMode === 'adapt') {
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, () => {
@ -253,7 +240,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
const cell = newChart.getCell(ev.target)
const meta = cell.getMeta() as ViewMeta
const nameIdMap = fields.reduce((pre, next) => {
pre[next['dataeaseName']] = next['id']
pre[next['gisbiName']] = next['id']
return pre
}, {})
@ -281,19 +268,86 @@ export class TableNormal extends S2ChartView<TableSheet> {
if (show) {
newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
// touch
this.configTouchEvent(newChart, drawOption, meta)
}
// header resize
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev))
// right click
newChart.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(newChart, event, meta))
// touch
this.configTouchEvent(newChart, drawOption, meta)
// theme
const customTheme = this.configTheme(chart)
newChart.setThemeCfg({ theme: customTheme })
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)
}
}
protected summaryRowStyle(newChart, newData, tableCell, tableHeader, showSummary) {
if (!showSummary || !newData.length) return
newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => {
const showHeader = tableHeader.showTableHeader === true
// 不显示表头时,减少一个表头的高度
const headerAndSummaryHeight = showHeader ? 2 : 1
const totalHeight =
tableHeader.tableTitleHeight * headerAndSummaryHeight +
tableCell.tableItemHeight * (newData.length - 1)
if (totalHeight < newChart.container.cfg.height) {
newChart.options.height =
totalHeight < newChart.container.cfg.height - 8 ? totalHeight + 8 : totalHeight
}
})
}
constructor() {
super('table-normal', [])
}

View File

@ -13,7 +13,8 @@ import {
TotalStatus,
Aggregation,
S2DataConfig,
MergedCell
MergedCell,
LayoutResult
} from '@antv/s2'
import { formatterItem, valueFormatter } from '../../../formatter'
import { hexColorToRGBA, isAlphaColor, parseJson } from '../../../util'
@ -91,7 +92,8 @@ export class TablePivot extends S2ChartView<PivotSheet> {
'showColTooltip',
'showRowTooltip',
'showHorizonBorder',
'showVerticalBorder'
'showVerticalBorder',
'rowHeaderFreeze'
],
'table-total-selector': ['row', 'col'],
'basic-style-selector': [
@ -100,7 +102,9 @@ export class TablePivot extends S2ChartView<PivotSheet> {
'tableScrollBarColor',
'alpha',
'tableLayoutMode',
'showHoverStyle'
'showHoverStyle',
'quotaPosition',
'quotaColLabel'
]
}
axis: AxisType[] = ['xAxis', 'xAxisExt', 'yAxis', 'filter']
@ -126,7 +130,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
const { xAxisExt: columnFields, xAxis: rowFields, yAxis: valueFields } = chart
const [c, r, v] = [columnFields, rowFields, valueFields].map(arr =>
arr.map(i => i.dataeaseName)
arr.map(i => i.gisbiName)
)
// fields
@ -146,14 +150,14 @@ export class TablePivot extends S2ChartView<PivotSheet> {
...chart.xAxisExt,
...chart.yAxis
].reduce((p, n) => {
p[n.dataeaseName] = n
p[n.gisbiName] = n
return p
}, {})
fields.forEach(ele => {
const f = valueFieldMap[ele.dataeaseName]
columns.push(ele.dataeaseName)
const f = valueFieldMap[ele.gisbiName]
columns.push(ele.gisbiName)
meta.push({
field: ele.dataeaseName,
field: ele.gisbiName,
name: ele.chartShowName ?? ele.name,
formatter: value => {
if (!f) {
@ -162,7 +166,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
if (value === null || value === undefined) {
return value
}
if (![2, 3].includes(f.deType) || !isNumber(value)) {
if (![2, 3, 4].includes(f.deType) || !isNumber(value)) {
return value
}
if (f.formatterCfg) {
@ -175,7 +179,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
})
// total config
const { basicStyle, tooltip, tableTotal } = parseJson(chart.customAttr)
const { basicStyle, tooltip, tableTotal, tableHeader } = parseJson(chart.customAttr)
if (!tableTotal.row.subTotalsDimensionsNew || tableTotal.row.subTotalsDimensions == undefined) {
tableTotal.row.subTotalsDimensions = r
}
@ -183,6 +187,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
// 解析合计、小计排序
const sortParams = []
let rowTotalSort = false
if (
tableTotal.row.totalSort &&
tableTotal.row.totalSort !== 'none' &&
@ -190,8 +195,9 @@ export class TablePivot extends S2ChartView<PivotSheet> {
tableTotal.row.showGrandTotals &&
v.indexOf(tableTotal.row.totalSortField) > -1
) {
c.forEach(i => {
const sort = {
sortFieldId: c[0],
sortFieldId: i,
sortMethod: tableTotal.row.totalSort.toUpperCase(),
sortByMeasure: TOTAL_VALUE,
query: {
@ -199,7 +205,10 @@ export class TablePivot extends S2ChartView<PivotSheet> {
}
}
sortParams.push(sort)
})
rowTotalSort = true
}
let colTotalSort = false
if (
tableTotal.col.totalSort &&
tableTotal.col.totalSort !== 'none' &&
@ -207,8 +216,9 @@ export class TablePivot extends S2ChartView<PivotSheet> {
tableTotal.col.showGrandTotals &&
v.indexOf(tableTotal.col.totalSortField) > -1
) {
r.forEach(i => {
const sort = {
sortFieldId: r[0],
sortFieldId: i,
sortMethod: tableTotal.col.totalSort.toUpperCase(),
sortByMeasure: TOTAL_VALUE,
query: {
@ -216,6 +226,8 @@ export class TablePivot extends S2ChartView<PivotSheet> {
}
}
sortParams.push(sort)
})
colTotalSort = true
}
//列维度为空,行排序按照指标列来排序,取第一个有排序设置的指标
if (!columnFields?.length) {
@ -226,7 +238,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
sortMethod: sortField.sort.toUpperCase(),
sortByMeasure: TOTAL_VALUE,
query: {
[EXTRA_FIELD]: sortField.dataeaseName
[EXTRA_FIELD]: sortField.gisbiName
}
}
sortParams.push(sort)
@ -244,15 +256,29 @@ export class TablePivot extends S2ChartView<PivotSheet> {
col: chart.xAxisExt,
quota: chart.yAxis
}
//树形模式下,列维度为空,行小计会变成列总计,特殊处理下
if (basicStyle.tableLayoutMode === 'tree' && !chart.xAxisExt?.length) {
// 沒有列维度需要特殊处理
if (!chart.xAxisExt?.length) {
//树形模式下,列维度为空,行小计的配置会变成列总计
if (basicStyle.tableLayoutMode === 'tree') {
tableTotal.col.calcTotals = tableTotal.row.calcSubTotals
if (!tableTotal.col.calcTotals.cfg?.length) {
tableTotal.col.calcTotals.cfg = chart.yAxis.map(y => {
return {
gisbiName: y.gisbiName,
aggregation: 'SUM'
}
})
}
} else {
// 列总计设置为空
tableTotal.col.calcTotals.calcFunc = () => '-'
}
}
totals.forEach(total => {
if (total.cfg?.length) {
delete total.aggregation
const totalCfgMap = total.cfg.reduce((p, n) => {
p[n.dataeaseName] = n
p[n.gisbiName] = n
return p
}, {})
total.calcFunc = (query, data, _, status) => {
@ -262,12 +288,93 @@ export class TablePivot extends S2ChartView<PivotSheet> {
})
// 空值处理
const newData = this.configEmptyDataStrategy(chart)
// 行列维度排序
if (!rowTotalSort) {
c?.forEach((f, i) => {
if (valueFieldMap[f]?.sort === 'none') {
return
}
const sort = {
sortFieldId: f
}
const sortMethod = valueFieldMap[f]?.sort?.toUpperCase()
if (sortMethod === 'CUSTOM_SORT') {
sort.sortBy = valueFieldMap[f].customSort
} else {
if (i === 0) {
sort.sortMethod = sortMethod
} else {
const fieldValues = newData.map(item => item[f])
const uniqueValues = [...new Set(fieldValues)]
// 根据配置动态决定排序顺序
uniqueValues.sort((a, b) => {
if ([2, 3, 4].includes(valueFieldMap[f]?.deType)) {
return sortMethod === 'ASC' ? a - b : b - a
}
if (!a && !b) {
return 0
}
if (!a) {
return sortMethod === 'ASC' ? -1 : 1
}
if (!b) {
return sortMethod === 'ASC' ? 1 : -1
}
return sortMethod === 'ASC' ? a.localeCompare(b) : b.localeCompare(a)
})
sort.sortBy = uniqueValues
}
}
sortParams.push(sort)
})
}
if (!colTotalSort) {
r?.forEach((f, i) => {
if (valueFieldMap[f]?.sort === 'none') {
return
}
const sort = {
sortFieldId: f
}
const sortMethod = valueFieldMap[f]?.sort?.toUpperCase()
if (sortMethod === 'CUSTOM_SORT') {
sort.sortBy = valueFieldMap[f].customSort
} else {
if (i === 0) {
sort.sortMethod = sortMethod
} else {
const fieldValues = newData.map(item => item[f])
const uniqueValues = [...new Set(fieldValues)]
// 根据配置动态决定排序顺序
uniqueValues.sort((a, b) => {
if ([2, 3, 4].includes(valueFieldMap[f]?.deType)) {
return sortMethod === 'ASC' ? a - b : b - a
}
if (!a && !b) {
return 0
}
if (!a) {
return sortMethod === 'ASC' ? -1 : 1
}
if (!b) {
return sortMethod === 'ASC' ? 1 : -1
}
return sortMethod === 'ASC' ? a.localeCompare(b) : b.localeCompare(a)
})
sort.sortBy = uniqueValues
}
}
sortParams.push(sort)
})
}
// data config
const s2DataConfig: S2DataConfig = {
fields: {
rows: r,
columns: c,
values: v
values: v,
valueInCols: !(basicStyle.quotaPosition === 'row')
},
meta: meta,
data: newData,
@ -277,6 +384,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
width: containerDom.offsetWidth,
height: containerDom.offsetHeight,
totals: tableTotal as Totals,
cornerExtraFieldText: basicStyle.quotaColLabel ?? t('dataset.value'),
conditions: this.configConditions(chart),
tooltip: {
getContainer: () => containerDom
@ -288,21 +396,204 @@ export class TablePivot extends S2ChartView<PivotSheet> {
},
dataCell: meta => {
return new CustomDataCell(meta, meta.spreadsheet)
}
},
frozenRowHeader: !(tableHeader.rowHeaderFreeze === false)
}
// options
s2Options.style = this.configStyle(chart, s2DataConfig)
// 默认展开层级
if (basicStyle.tableLayoutMode === 'tree') {
const { defaultExpandLevel } = basicStyle
if (isNumber(defaultExpandLevel)) {
if (defaultExpandLevel >= chart.xAxis.length) {
s2Options.style.rowExpandDepth = defaultExpandLevel
} else {
s2Options.style.rowExpandDepth = defaultExpandLevel - 2
}
}
if (defaultExpandLevel === 'all') {
s2Options.style.rowExpandDepth = chart.xAxis.length
}
if (!defaultExpandLevel) {
s2Options.style.hierarchyCollapse = true
}
}
// 列汇总别名
if (!(basicStyle.quotaPosition === 'row' && basicStyle.tableLayoutMode === 'tree')) {
if (
basicStyle.quotaPosition !== 'row' &&
chart.xAxisExt?.length &&
chart.yAxis?.length > 1 &&
tableTotal.col.showGrandTotals &&
tableTotal.col.calcTotals?.cfg?.length
) {
const colTotalCfgMap = tableTotal.col.calcTotals.cfg.reduce((p, n) => {
p[n.gisbiName] = n
return p
}, {})
s2Options.layoutCoordinate = (_, __, col) => {
if (col?.isGrandTotals) {
if (colTotalCfgMap[col.value]?.label) {
col.label = colTotalCfgMap[col.value].label
}
}
}
}
if (
basicStyle.quotaPosition === 'row' &&
chart.xAxisExt?.length &&
chart.yAxis?.length > 1 &&
tableTotal.row.showGrandTotals &&
tableTotal.row.calcTotals?.cfg?.length
) {
const rowTotalCfgMap = tableTotal.row.calcTotals.cfg.reduce((p, n) => {
p[n.gisbiName] = n
return p
}, {})
// eslint-disable-next-line
s2Options.layoutCoordinate = (_, row, __) => {
if (row?.isGrandTotals) {
if (rowTotalCfgMap[row.value]?.label) {
row.label = rowTotalCfgMap[row.value].label
}
}
}
}
}
// tooltip
this.configTooltip(chart, s2Options)
// 开始渲染
const s2 = new PivotSheet(containerDom, s2DataConfig, s2Options as unknown as S2Options)
// 自适应铺满
if (basicStyle.tableColumnMode === 'adapt') {
s2.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, () => {
s2.store.set('lastLayoutResult', s2.facet.layoutResult)
})
// 平铺模式行头resize
s2.on(S2Event.LAYOUT_RESIZE_ROW_WIDTH, () => {
s2.store.set('lastLayoutResult', s2.facet.layoutResult)
})
// 树形模式行头resize
s2.on(S2Event.LAYOUT_RESIZE_TREE_WIDTH, () => {
s2.store.set('lastLayoutResult', s2.facet.layoutResult)
})
s2.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, (ev: LayoutResult) => {
const lastLayoutResult = s2.store.get('lastLayoutResult') as LayoutResult
if (lastLayoutResult) {
// 拖动 col 表头 resize
const colWidthByFieldValue = s2.options.style?.colCfg?.widthByFieldValue
// 平铺模式拖动 row 表头 resize
const rowWidthByField = s2.options.style?.rowCfg?.widthByField
// 树形模式拖动 row 表头 resize
const treeRowWidth =
s2.options.style?.treeRowsWidth || lastLayoutResult.rowsHierarchy.width
const colWidthMap =
lastLayoutResult.colLeafNodes.reduce((p, n) => {
p[n.id] = colWidthByFieldValue?.[n.value] ?? n.width
return p
}, {}) || {}
const totalColWidth = ev.colLeafNodes.reduce((p, n) => {
n.width = colWidthMap[n.id] || n.width
n.x = p
return p + n.width
}, 0)
ev.colNodes.forEach(n => {
if (n.isLeaf) {
return
}
n.width = this.getColWidth(n)
n.x = this.getLeftChild(n).x
})
if (basicStyle.tableLayoutMode === 'tree') {
ev.rowNodes.forEach(n => {
n.width = treeRowWidth
})
ev.rowsHierarchy.width = treeRowWidth
ev.colsHierarchy.width = totalColWidth
} else {
const rowWidthMap =
lastLayoutResult.rowNodes.reduce((p, n) => {
p[n.id] = rowWidthByField?.[n.field] ?? n.width
return p
}, {}) || {}
ev.rowNodes.forEach(n => {
n.x = 0
n.width = rowWidthMap[n.id] || n.width
let tmp = n
while (tmp.parent.id !== 'root') {
n.x += tmp.parent.width
tmp = tmp.parent
}
})
const totlaRowWidth = ev.rowsHierarchy.sampleNodesForAllLevels.reduce((p, n) => {
return p + n.width
}, 0)
const maxRowLevel = ev.rowsHierarchy.maxLevel
ev.rowNodes.forEach(n => {
// 总计和中间层级的小计需要重新计算宽度
if (n.isTotalRoot || (n.isSubTotals && n.level < maxRowLevel)) {
let width = 0
for (let i = n.level; i <= maxRowLevel; i++) {
width += ev.rowsHierarchy.sampleNodesForAllLevels[i].width
}
n.width = width
}
})
ev.rowsHierarchy.width = totlaRowWidth
ev.colsHierarchy.width = totalColWidth
}
s2.store.set('lastLayoutResult', undefined)
return
}
const containerWidth = containerDom.getBoundingClientRect().width
const scale = containerWidth / (ev.colsHierarchy.width + ev.rowsHierarchy.width)
if (scale <= 1) {
return
}
const totalRowWidth = Math.round(ev.rowsHierarchy.width * scale)
ev.rowNodes.forEach(n => {
n.width = Math.round(n.width * scale)
})
if (basicStyle.tableLayoutMode !== 'tree') {
ev.rowNodes.forEach(n => {
n.x = 0
let tmp = n
while (tmp.parent.id !== 'root') {
n.x += tmp.parent.width
tmp = tmp.parent
}
})
}
let totalColWidth = ev.colLeafNodes.reduce((p, n) => {
n.width = Math.round(n.width * scale)
n.x = p
return p + n.width
}, 0)
ev.colNodes.forEach(n => {
if (n.isLeaf) {
return
}
n.width = this.getColWidth(n)
n.x = this.getLeftChild(n).x
})
const totalWidth = totalColWidth + totalRowWidth
if (totalWidth > containerWidth) {
// 从最后一列减掉
ev.colLeafNodes[ev.colLeafNodes.length - 1].width -= totalWidth - containerWidth
totalColWidth = totalColWidth - (totalWidth - containerWidth)
}
ev.colsHierarchy.width = totalColWidth
ev.rowsHierarchy.width = totalRowWidth
})
}
// tooltip
const { show } = tooltip
if (show) {
s2.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(s2, event, meta))
s2.on(S2Event.ROW_CELL_HOVER, event => this.showTooltip(s2, event, meta))
s2.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(s2, event, meta))
// touch
this.configTouchEvent(s2, drawOption, meta)
}
// empty data tip
configEmptyDataStyle(s2, newData)
@ -312,19 +603,34 @@ export class TablePivot extends S2ChartView<PivotSheet> {
s2.on(S2Event.COL_CELL_CLICK, ev => this.headerCellClickAction(chart, ev, s2, action))
// right click
s2.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(s2, event, meta))
// touch
this.configTouchEvent(s2, drawOption, meta)
// theme
const customTheme = this.configTheme(chart)
s2.setThemeCfg({ theme: customTheme })
return s2
}
private getColWidth(node) {
let width = 0
if (node.children?.length) {
node.children.forEach(child => {
width += this.getColWidth(child)
})
} else {
width = node.width
}
return width
}
private getLeftChild(node) {
if (!node.children?.length) {
return node
}
return this.getLeftChild(node.children[0])
}
private dataCellClickAction(chart: Chart, ev, s2Instance: PivotSheet, callback) {
const cell = s2Instance.getCell(ev.target)
const meta = cell.getMeta()
const nameIdMap = chart.data.fields.reduce((pre, next) => {
pre[next['dataeaseName']] = next['id']
pre[next['gisbiName']] = next['id']
return pre
}, {})
const rowData = { ...meta.rowQuery, ...meta.colQuery }
@ -352,7 +658,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
const meta = cell.getMeta()
const rowData = meta.query
const nameIdMap = chart.data.fields.reduce((pre, next) => {
pre[next['dataeaseName']] = next['id']
pre[next['gisbiName']] = next['id']
return pre
}, {})
const dimensionList = []
@ -522,7 +828,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
}
function customCalcFunc(query, data, status, chart, totalCfgMap, axisMap, customCalc) {
if (!data?.length || !query[EXTRA_FIELD]) {
return 0
return '-'
}
const aggregation = totalCfgMap[query[EXTRA_FIELD]]?.aggregation || 'SUM'
switch (aggregation) {
@ -549,10 +855,13 @@ function customCalcFunc(query, data, status, chart, totalCfgMap, axisMap, custom
})
return result?.[query[EXTRA_FIELD]]
}
case 'NONE': {
return '-'
}
case 'CUSTOM': {
const val = getCustomCalcResult(query, axisMap, chart, status, customCalc || {})
if (val === '') {
return val
if (val === '' || val === undefined) {
return '-'
}
return parseFloat(val)
}
@ -593,11 +902,17 @@ function getTreeCustomCalcResult(query, axisMap, status: TotalStatus, customCalc
// 列小计
if (status.isColSubTotal && !status.isRowTotal && !status.isRowSubTotal) {
const { colSubTotal } = customCalc
const subLevel = getSubLevel(query, col)
const subColLevel = getSubLevel(query, col)
const subRowLevel = getSubLevel(query, row)
const rowPath = getTreePath(query, row)
const colPath = getTreePath(query, col)
const path = [...rowPath, ...colPath]
const data = colSubTotal?.[subLevel]?.data
let data = colSubTotal?.[subColLevel]?.data
// 列小计里面的行小计
if (rowPath.length < row.length) {
const { rowSubInColSub } = customCalc
data = rowSubInColSub?.[subRowLevel]?.[subColLevel]?.data
}
let val
if (path.length && data) {
path.push(quotaField)
@ -647,7 +962,7 @@ function getTreeCustomCalcResult(query, axisMap, status: TotalStatus, customCalc
if (status.isRowTotal && status.isColSubTotal) {
const { colSubInRowTotal } = customCalc
const colLevel = getSubLevel(query, col)
const { data } = colSubInRowTotal?.[colLevel]
const data = colSubInRowTotal?.[colLevel]?.data
const colPath = getTreePath(query, col)
let val
if (colPath.length && colSubInRowTotal) {
@ -669,23 +984,7 @@ function getTreeCustomCalcResult(query, axisMap, status: TotalStatus, customCalc
}
return val
}
// 列小计里面的行小计
if (status.isColSubTotal && status.isRowSubTotal) {
const { rowSubInColSub } = customCalc
const rowSubLevel = getSubLevel(query, row)
const colSubLevel = getSubLevel(query, col)
const data = rowSubInColSub?.[rowSubLevel]?.[colSubLevel]?.data
const rowPath = getTreePath(query, row)
const colPath = getTreePath(query, col)
const path = [...rowPath, ...colPath]
let val
if (path.length && rowSubInColSub) {
path.push(quotaField)
val = get(data, path)
}
return val
}
return NaN
return '-'
}
function getGridCustomCalcResult(query, axisMap, status: TotalStatus, customCalc) {
@ -759,7 +1058,7 @@ function getGridCustomCalcResult(query, axisMap, status: TotalStatus, customCalc
if (status.isRowTotal && status.isColSubTotal) {
const { colSubInRowTotal } = customCalc
const colLevel = getSubLevel(query, col)
const { data } = colSubInRowTotal?.[colLevel]
const data = colSubInRowTotal?.[colLevel]?.data
const colPath = getTreePath(query, col)
let val
if (colPath.length && colSubInRowTotal) {
@ -807,7 +1106,7 @@ function getCustomCalcResult(query, axisMap, chart: ChartObj, status: TotalStatu
}
function getSubLevel(query, axis) {
const fields: [] = axis.map(a => a.dataeaseName)
const fields: [] = axis.map(a => a.gisbiName)
let subLevel = -1
const queryFields = keys(query)
for (let i = fields.length - 1; i >= 0; i--) {
@ -824,9 +1123,9 @@ function getTreePath(query, axis) {
const path = []
const fields = keys(query)
axis.forEach(a => {
const index = fields.findIndex(f => f === a.dataeaseName)
const index = fields.findIndex(f => f === a.gisbiName)
if (index !== -1) {
path.push(query[a.dataeaseName])
path.push(query[a.gisbiName])
}
})
return path

View File

@ -33,10 +33,23 @@ import { PositionType } from '@antv/l7-core'
import { centroid } from '@turf/centroid'
import type { Plot } from '@antv/g2plot'
import type { PickOptions } from '@antv/g2plot/lib/core/plot'
import { defaults } from 'lodash-es'
import { defaults, find } from 'lodash-es'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
const { t: tI18n } = useI18n()
import { isMobile } from '@/data-visualization/utils/utils'
import { GaodeMap, TMap, TencentMap } from '@antv/l7-maps'
import {
gaodeMapStyleOptions,
qqMapStyleOptions,
tdtMapStyleOptions
} from '@/data-visualization/chart/components/js/panel/charts/map/common'
import ChartCarouselTooltip, {
isPie,
isColumn,
isMix,
isSupport
} from '@/data-visualization/chart/components/js/g2plot_tooltip_carousel'
const { t: tI18n } = useI18n()
export function getPadding(chart: Chart): number[] {
if (chart.drill) {
@ -137,14 +150,22 @@ export function getTheme(chart: Chart) {
},
'g2-tooltip-list-item': {
display: 'flex',
'align-items': 'center'
'align-items': 'flex-start',
'justify-content': 'space-between',
'line-height': tooltipFontsize + 'px'
},
'g2-tooltip-name': {
display: 'inline-block',
'line-height': tooltipFontsize + 'px',
flex: 1
'line-height': tooltipFontsize + 'px'
},
'g2-tooltip-value': {
flex: 1,
display: 'inline-block',
'text-align': 'end',
'line-height': tooltipFontsize + 'px'
},
'g2-tooltip-marker': {
'margin-top': (tooltipFontsize - 8) / 2 + 'px',
'min-width': '8px',
'min-height': '8px'
}
@ -469,7 +490,8 @@ export function getXAxis(chart: Chart) {
style: {
fill: a.axisLabel.color,
fontSize: a.axisLabel.fontSize,
textAlign: textAlign
textAlign: textAlign,
fontFamily: chart.fontFamily
},
formatter: value => {
return chart.type === 'bidirectional-bar' && value.length > a.axisLabel.lengthLimit
@ -574,7 +596,8 @@ export function getYAxis(chart: Chart) {
fill: yAxis.axisLabel.color,
fontSize: yAxis.axisLabel.fontSize,
textBaseline,
textAlign
textAlign,
fontFamily: chart.fontFamily
},
formatter: value => {
return value.length > yAxis.axisLabel.lengthLimit
@ -603,7 +626,7 @@ export function getYAxisExt(chart: Chart) {
return false
}
const title =
yAxis.name && yAxis.name !== ''
yAxis.nameShow && yAxis.name && yAxis.name !== ''
? {
text: yAxis.name,
style: {
@ -629,14 +652,16 @@ export function getYAxisExt(chart: Chart) {
? {
style: {
stroke: axisCfg.lineStyle.color,
lineWidth: axisCfg.lineStyle.width
lineWidth: axisCfg.lineStyle.width,
lineDash: getLineDash(axisCfg.lineStyle.style)
}
}
: null
const tickLine = axisCfg.show
? {
style: {
stroke: axisCfg.lineStyle.color
stroke: axisCfg.lineStyle.color,
lineWidth: axisCfg.lineStyle.width
}
}
: null
@ -673,7 +698,8 @@ export function getYAxisExt(chart: Chart) {
fill: yAxis.axisLabel.color,
fontSize: yAxis.axisLabel.fontSize,
textBaseline,
textAlign
textAlign,
fontFamily: chart.fontFamily
}
}
: null
@ -821,10 +847,9 @@ export function getAnalyseHorizontal(chart: Chart) {
const assistLineArr = senior.assistLineCfg.assistLine
if (assistLineArr?.length > 0) {
const customStyle = parseJson(chart.customStyle)
let xAxisPosition, axisFormatterCfg
let axisFormatterCfg
if (customStyle.xAxis) {
const a = JSON.parse(JSON.stringify(customStyle.xAxis))
xAxisPosition = transAxisPosition(a.position)
axisFormatterCfg = a.axisLabelFormatter
? a.axisLabelFormatter
: DEFAULT_XAXIS_STYLE.axisLabelFormatter
@ -894,7 +919,9 @@ export function getLineDash(type) {
*/
export function setGradientColor(rawColor: string, show = false, angle = 0, start = 0) {
const item = rawColor.split(',')
item.splice(3, 1, '0.3)')
const alpha = parseFloat(item[3].replace(')', ''))
const startAlpha = alpha * 0.3
item.splice(3, 1, `${startAlpha})`)
let color: string
if (start == 0) {
color = `l(${angle}) 0:${item.join(',')} 1:${rawColor}`
@ -993,6 +1020,9 @@ export function configL7Tooltip(chart: Chart): TooltipOptions {
return result
}
const head = originalItem.properties
if (!head) {
return result
}
const formatter = formatterMap[head.quotaList?.[0]?.id]
if (!isEmpty(formatter)) {
const originValue = parseFloat(head.value as string)
@ -1152,6 +1182,19 @@ export class CustomZoom extends Zoom {
'l7-button-control',
container,
() => {
if (this.mapsService.map?.deMapProvider == 'qq') {
if (this.mapsService.map.deMapAutoFit) {
this.mapsService.setZoomAndCenter(this.mapsService.map.deMapAutoZoom, [
this.mapsService.map.deMapAutoLng,
this.mapsService.map.deMapAutoLat
])
} else {
this.mapsService.setZoomAndCenter(
this.controlOption['initZoom'],
this.controlOption['center']
)
}
} else {
if (this.controlOption['bounds']) {
this.mapsService.fitBounds(this.controlOption['bounds'], { animate: true })
} else {
@ -1161,6 +1204,7 @@ export class CustomZoom extends Zoom {
)
}
}
}
)
if (this.controlOption.showZoom) {
this['zoomNumDiv'] = this['createButton'](
@ -1208,7 +1252,11 @@ export class CustomZoom extends Zoom {
} as IZoomControlOption
}
}
export function configL7Zoom(chart: Chart, scene: Scene) {
export function configL7Zoom(
chart: Chart,
scene: Scene,
mapKey?: { key: string; securityCode: string; mapType: string }
) {
const { basicStyle } = parseJson(chart.customAttr)
const zoomOption = scene?.getControlByName('zoom')
if (zoomOption) {
@ -1220,6 +1268,41 @@ export function configL7Zoom(chart: Chart, scene: Scene) {
if (!scene?.getControlByName('zoom')) {
if (!scene.map) {
scene.once('loaded', () => {
switch (mapKey?.mapType) {
case 'tianditu':
//天地图
{
const initZoom = basicStyle.autoFit === false ? basicStyle.zoomLevel : scene.getZoom()
const center =
basicStyle.autoFit === false
? [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
: [scene.map.getCenter().getLng(), scene.map.getCenter().getLat()]
const newZoomOptions = {
initZoom: initZoom,
center: center,
buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground
} as any
scene.addControl(new CustomZoom(newZoomOptions))
}
break
case 'qq':
{
const initZoom = basicStyle.autoFit === false ? basicStyle.zoomLevel : scene.getZoom()
const center =
basicStyle.autoFit === false
? [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
: [scene.map.getCenter().lng, scene.map.getCenter().lat]
const newZoomOptions = {
initZoom: initZoom,
center: center,
buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground
} as any
scene.addControl(new CustomZoom(newZoomOptions))
}
break
default:
scene.map.on('complete', () => {
const initZoom = basicStyle.autoFit === false ? basicStyle.zoomLevel : scene.getZoom()
const center =
@ -1234,6 +1317,7 @@ export function configL7Zoom(chart: Chart, scene: Scene) {
} as any
scene.addControl(new CustomZoom(newZoomOptions))
})
}
})
} else {
const newZoomOptions = {
@ -1250,19 +1334,19 @@ export function configL7Zoom(chart: Chart, scene: Scene) {
const endAxis = chart.xAxisExt
if (startAxis?.length === 2) {
chart.data?.tableRow?.forEach(row => {
coordinates.push([row[startAxis[0].dataeaseName], row[startAxis[1].dataeaseName]])
coordinates.push([row[startAxis[0].gisbiName], row[startAxis[1].gisbiName]])
})
}
if (endAxis?.length === 2) {
chart.data?.tableRow?.forEach(row => {
coordinates.push([row[endAxis[0].dataeaseName], row[endAxis[1].dataeaseName]])
coordinates.push([row[endAxis[0].gisbiName], row[endAxis[1].gisbiName]])
})
}
} else {
const axis = chart.xAxis
if (axis?.length === 2) {
chart.data?.tableRow?.forEach(row => {
coordinates.push([row[axis[0].dataeaseName], row[axis[1].dataeaseName]])
coordinates.push([row[axis[0].gisbiName], row[axis[1].gisbiName]])
})
}
}
@ -1335,6 +1419,18 @@ export function mapRendering(dom: HTMLElement | string) {
dom.classList.add('de-map-rendering')
}
export function qqMapRendered(scene?: Scene) {
if (scene?.map && scene.map.deMapProvider === 'qq') {
setTimeout(() => {
if (scene.map) {
scene.map.deMapAutoZoom = scene.map.getZoom()
scene.map.deMapAutoLng = scene.map.getCenter().getLng()
scene.map.deMapAutoLat = scene.map.getCenter().getLat()
}
}, 1000)
}
}
export function mapRendered(dom: HTMLElement | string) {
if (typeof dom === 'string') {
dom = document.getElementById(dom)
@ -1342,6 +1438,213 @@ export function mapRendered(dom: HTMLElement | string) {
dom.classList.add('de-map-rendered')
}
export function getMapCenter(basicStyle: ChartBasicStyle) {
let center: [number, number]
if (basicStyle.autoFit === false) {
const longitude = basicStyle?.mapCenter?.longitude ?? DEFAULT_BASIC_STYLE.mapCenter.longitude
const latitude = basicStyle?.mapCenter?.latitude ?? DEFAULT_BASIC_STYLE.mapCenter.latitude
center = [longitude, latitude]
} else {
center = undefined
}
return center
}
export function getMapStyle(
mapKey: { key: string; securityCode: string; mapType: string },
basicStyle: ChartBasicStyle
) {
let mapStyle: string
switch (mapKey.mapType) {
case 'tianditu':
if (!find(tdtMapStyleOptions, s => s.value === basicStyle.mapStyle)) {
mapStyle = 'normal'
} else {
mapStyle = basicStyle.mapStyle
}
break
case 'qq':
if (
!find(qqMapStyleOptions, s => s.value === basicStyle.mapStyle) ||
basicStyle.mapStyle === 'normal'
) {
mapStyle = 'normal'
} else {
mapStyle = basicStyle.mapStyleUrl
}
break
default:
if (!find(gaodeMapStyleOptions, s => s.value === basicStyle.mapStyle)) {
basicStyle.mapStyle = 'normal'
}
mapStyle = basicStyle.mapStyleUrl
if (basicStyle.mapStyle !== 'custom') {
mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
}
break
}
return mapStyle
}
export async function getMapScene(
chart: Chart,
scene: Scene,
container: string,
mapKey: { key: string; securityCode: string; mapType: string },
basicStyle: ChartBasicStyle,
miscStyle: ChartMiscAttr,
mapStyle: string,
center?: [number, number]
) {
if (!scene) {
scene = new Scene({
id: container,
logoVisible: false,
map: getMapObject(mapKey, basicStyle, miscStyle, mapStyle, center)
})
} else {
if (mapKey.mapType === 'tianditu') {
scene.map?.checkResize()
}
if (scene.getLayers()?.length) {
await scene.removeAllLayer()
try {
scene.setPitch(miscStyle.mapPitch)
} catch (e) {}
if (mapKey.mapType === 'tianditu') {
if (mapStyle === 'normal') {
scene.map?.removeStyle()
} else {
scene.setMapStyle(mapStyle)
}
} else {
scene.setMapStyle(mapStyle)
}
scene.map.showLabel = !(basicStyle.showLabel === false)
if (mapKey.mapType === 'qq') {
scene.map.setBaseMap({
//底图设置参数为VectorBaseMap对象
type: 'vector', //类型:失量底图
features: basicStyle.showLabel === false ? ['base', 'building2d'] : undefined
//仅渲染:道路及底面(base) + 2d建筑物(building2d),以达到隐藏文字的效果
})
}
}
if (basicStyle.autoFit === false) {
scene.setZoomAndCenter(basicStyle.zoomLevel, center)
if (mapKey.mapType === 'qq') {
scene.map.deMapAutoFit = false
scene.map.deMapZoom = basicStyle.zoomLevel
scene.map.deMapCenter = center
}
}
}
mapRendering(container)
scene.once('loaded', () => {
mapRendered(container)
if (mapKey.mapType === 'qq') {
scene.map.setBaseMap({
//底图设置参数为VectorBaseMap对象
type: 'vector', //类型:失量底图
features: basicStyle.showLabel === false ? ['base', 'building2d'] : undefined
//仅渲染:道路及底面(base) + 2d建筑物(building2d),以达到隐藏文字的效果
})
scene.setMapStyle(mapStyle)
scene.map.deMapProvider = 'qq'
scene.map.deMapAutoFit = !!basicStyle.autoFit
// scene.map.deMapAutoZoom = scene.map.getZoom()
// scene.map.deMapAutoLng = scene.map.getCenter().getLng()
// scene.map.deMapAutoLat = scene.map.getCenter().getLat()
}
// 去除天地图自己的缩放按钮
if (mapKey.mapType === 'tianditu') {
if (mapStyle === 'normal') {
scene.map?.removeStyle()
} else {
scene.setMapStyle(mapStyle)
}
const tdtControl = document.querySelector(
`#component${chart.id} .tdt-control-zoom.tdt-bar.tdt-control`
)
if (tdtControl) {
tdtControl.style.display = 'none'
}
const tdtControlOuter = document.querySelectorAll(
`#wrapper-outer-id-${chart.id} .tdt-control-zoom.tdt-bar.tdt-control`
)
if (tdtControlOuter && tdtControlOuter.length > 0) {
for (let i = 0; i < tdtControlOuter.length; i++) {
tdtControlOuter[i].style.display = 'none'
}
}
const tdtCopyrightControl = document.querySelector(
`#component${chart.id} .tdt-control-copyright.tdt-control`
)
if (tdtCopyrightControl) {
tdtCopyrightControl.style.display = 'none'
}
const tdtCopyrightControlOuter = document.querySelectorAll(
`#wrapper-outer-id-${chart.id} .tdt-control-copyright.tdt-control`
)
if (tdtCopyrightControlOuter && tdtCopyrightControlOuter.length > 0) {
for (let i = 0; i < tdtCopyrightControlOuter.length; i++) {
tdtCopyrightControlOuter[i].style.display = 'none'
}
}
}
})
return scene
}
export function getMapObject(
mapKey: { key: string; securityCode: string; mapType: string },
basicStyle: ChartBasicStyle,
miscStyle: ChartMiscAttr,
mapStyle: string,
center?: [number, number]
) {
switch (mapKey.mapType) {
case 'tianditu':
return new TMap({
token: mapKey?.key ?? undefined,
style: mapStyle, //不生效
pitch: undefined, //不支持
center,
zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined,
showLabel: !(basicStyle.showLabel === false), //不支持
WebGLParams: {
preserveDrawingBuffer: true
}
})
case 'qq':
return new TencentMap({
token: mapKey?.key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
center,
zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : 12,
showLabel: !(basicStyle.showLabel === false),
WebGLParams: {
preserveDrawingBuffer: true
}
})
default:
return new GaodeMap({
token: mapKey?.key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
center,
zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined,
showLabel: !(basicStyle.showLabel === false),
WebGLParams: {
preserveDrawingBuffer: true
}
})
}
}
/**
*
* @param basicStyle
@ -1358,6 +1661,8 @@ export function getTooltipContainer(id) {
let wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER)
if (!wrapperDom) {
wrapperDom = document.createElement('div')
wrapperDom.style.position = 'absolute'
wrapperDom.style.zIndex = '9999'
wrapperDom.id = G2_TOOLTIP_WRAPPER
document.body.appendChild(wrapperDom)
}
@ -1391,14 +1696,78 @@ export function getTooltipContainer(id) {
}
return g2Tooltip
}
/**
*
* @param plot
* @param chart
*/
function configCarouselTooltip(plot, chart) {
const start = isSupport(chart.type) && !document.getElementById('multiplexingDrawer')
if (start) {
// 启用轮播
plot.once('afterrender', () => {
const carousel = chart.customAttr?.tooltip?.carousel
ChartCarouselTooltip.manage(plot, chart, {
xField: 'field',
duration: carousel.enable ? carousel?.stayTime * 1000 : 2000,
interval: carousel.enable ? carousel?.intervalTime * 1000 : 2000
})
})
}
}
/**
* Tooltip
* @param {Chart} chart -
* @param {boolean} isCarousel -
* @param {object} tooltipCtl - Tooltip
* @param {HTMLElement} chartElement -
* @param {Event} event -
* @param {boolean} enlargeElement -
* @returns {{x: number, y: number}} - x y
*/
function calculateTooltipPosition(chart, isCarousel, tooltipCtl, chartElement, event) {
// 辅助函数: 根据不同图表类型计算 Tooltip 的y位置
const getTooltipY = () => {
const top = Number(chartElement.getBoundingClientRect().top)
if (isColumn(chart.type)) {
return top + chartElement.getBoundingClientRect().height / 2
}
if (isMix(chart.type) || isPie(chart.type)) {
return top + tooltipCtl.point.y
}
return top + tooltipCtl.point.y + 60
}
if (isCarousel) {
return {
x: tooltipCtl.point.x + Number(chartElement.getBoundingClientRect().left),
y: getTooltipY()
}
} else {
return { x: event.clientX, y: event.clientY }
}
}
export function configPlotTooltipEvent<O extends PickOptions, P extends Plot<O>>(
chart: Chart,
plot: P
) {
const { tooltip } = parseJson(chart.customAttr)
if (!tooltip.show) {
ChartCarouselTooltip.destroyByContainer(chart.container)
return
}
// 图表容器,用于计算 tooltip 的位置
// 获取图表元素,优先顺序:放大 > 预览 > 公共连接页面 > 默认
const chartElement =
document.getElementById('container-viewDialog-' + chart.id + '-common') ||
document.getElementById('container-preview-' + chart.id + '-common') ||
document.getElementById('enlarge-inner-content-' + chart.id) ||
document.getElementById('shape-id-' + chart.id)
// 是否是放大弹窗
const enlargeElement = chartElement?.id.includes('viewDialog')
// 轮播时tooltip的zIndex
const carousel_zIndex = enlargeElement ? '9999' : '1002'
configCarouselTooltip(plot, chart)
// 鼠标可移入, 移入之后保持显示, 移出之后隐藏
plot.options.tooltip.container.addEventListener('mouseenter', e => {
e.target.style.visibility = 'visible'
@ -1415,10 +1784,25 @@ export function configPlotTooltipEvent<O extends PickOptions, P extends Plot<O>>
if (!tooltipCtl) {
return
}
// 处理 tooltip 与下拉菜单的显示冲突问题
const viewTrackBarElement = document.getElementById('view-track-bar-' + chart.id)
const event = plot.chart.interactions.tooltip?.context?.event
// 是否时轮播模式
const isCarousel =
chart.customAttr?.tooltip?.carousel &&
(!event || // 事件触发时使用event的client坐标
['plot:leave', 'plot:mouseleave'].includes(event?.type) || //鼠标离开时使用tooltipCtl.point
['pie', 'pie-rose', 'pie-donut'].includes(chart.type)) // 饼图时使用tooltipCtl.point
plot.options.tooltip.showMarkers = isCarousel ? true : false
const wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER)
wrapperDom.style.zIndex = isCarousel && wrapperDom ? carousel_zIndex : '9999'
if (tooltipCtl.tooltip) {
// 处理视图放大后再关闭 tooltip 的 dom 被清除
const container = tooltipCtl.tooltip.cfg.container
// 当下拉菜单不显示时移除tooltip的hidden-tooltip样式
if (viewTrackBarElement?.getAttribute('aria-expanded') === 'false') {
container.classList.toggle('hidden-tooltip', false)
}
container.style.display = 'block'
const dom = document.getElementById(container.id)
if (!dom) {
@ -1433,8 +1817,17 @@ export function configPlotTooltipEvent<O extends PickOptions, P extends Plot<O>>
}
plot.chart.getOptions().tooltip.follow = false
tooltipCtl.title = Math.random().toString()
plot.chart.getTheme().components.tooltip.x = event.clientX
plot.chart.getTheme().components.tooltip.y = event.clientY
// 当显示提示为事件触发时使用event的client坐标否则使用tooltipCtl.point 数据点的位置,在图表中,需要加上图表在绘制区的位置
const { x, y } = calculateTooltipPosition(
chart,
isCarousel,
tooltipCtl,
chartElement,
event,
enlargeElement
)
plot.chart.getTheme().components.tooltip.x = x
plot.chart.getTheme().components.tooltip.y = y
})
// https://github.com/antvis/G2/blob/master/src/chart/controller/tooltip.ts#hideTooltip
plot.on('plot:leave', () => {
@ -1457,14 +1850,22 @@ export function configPlotTooltipEvent<O extends PickOptions, P extends Plot<O>>
if (!tooltipCtl) {
return
}
const container = tooltipCtl.tooltip.cfg.container
const container = tooltipCtl.tooltip?.cfg.container
for (const ele of wrapperDom.children) {
if (container.id !== ele.id) {
if (!container || container.id !== ele.id) {
ele.style.display = 'none'
}
}
}
})
plot.on('tooltip:hidden', () => {
const tooltipCtl = plot.chart.getController('tooltip')
if (!tooltipCtl) {
return
}
const container = tooltipCtl.tooltip?.cfg.container
container && (container.style.display = 'none')
})
}
export const TOOLTIP_TPL =
@ -1699,10 +2100,12 @@ export function configYaxisTitleLengthLimit(chart, plot) {
? wrappedTitle.slice(0, wrappedTitle.length - 2) + '...'
: wrappedTitle + '...'
}
// 更新Y轴标题的原始文本和截断后的文本
ev.view.options.axes.yAxisExt.title.originalText = yAxis.name
ev.view.options.axes.yAxisExt.title.text = wrappedTitle
const { title } = ev.view.options.axes.yAxisExt
if (title) {
title.originalText = yAxis.name
title.text = wrappedTitle
}
})
}
@ -1731,7 +2134,7 @@ export const addConditionsStyleColorToData = (chart: Chart, options) => {
})
} else if (item.quotaList?.length) {
const quotaList = item.quotaList.map(q => q.id) ?? []
quotaList.forEach((q, index) => {
quotaList.forEach(q => {
// 定义后,在 handleConditionsStyle 函数中使用
let currentValue = item[valueField]
if (chart.type === 'progress-bar') {
@ -1798,7 +2201,7 @@ const getColorByConditions = (quotaList: [], values: number | number[], chart) =
* @param chart
* @param options
*/
export function handleConditionsStyle(chart: Chart, options: O) {
export function handleConditionsStyle(chart: Chart, options) {
const { threshold } = parseJson(chart.senior)
if (!threshold.enable) return options
const { basicStyle } = parseJson(chart.customAttr)
@ -1810,8 +2213,6 @@ export function handleConditionsStyle(chart: Chart, options: O) {
// 辅助函数配置柱条样式颜色条形图为barStyle,柱形图为columnStyle
const columnStyle = data => {
return {
...options.columnStyle,
...options.barStyle,
...(data[colorField]?.[0] ? { fill: data[colorField][0] } : {})
}
}
@ -1825,8 +2226,8 @@ export function handleConditionsStyle(chart: Chart, options: O) {
const tmpOption = {
...options,
rawFields,
columnStyle: columnStyle,
barStyle: columnStyle,
...configRoundAngle(chart, 'columnStyle', columnStyle),
...configRoundAngle(chart, 'barStyle', columnStyle),
tooltip: {
...options.tooltip,
...(options.tooltip['customItems']
@ -1934,7 +2335,7 @@ export const getTooltipItemConditionColor = item => {
* @param newData
* @param container
*/
export const configEmptyDataStyle = (newChart, newData, container) => {
export const configEmptyDataStyle = (newData, container, newChart?, content?) => {
/**
* dom
*/
@ -1949,15 +2350,121 @@ export const configEmptyDataStyle = (newChart, newData, container) => {
if (!newData.length) {
const emptyDom = document.createElement('div')
emptyDom.id = container + '_empty'
emptyDom.textContent = tI18n('data_set.no_data')
emptyDom.textContent = content || tI18n('data_set.no_data')
emptyDom.setAttribute(
'style',
`position: absolute;
left: 45%;
top: 50%;`
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
color: darkgray;
textAlign: center;`
)
const parent = document.getElementById(container)
parent.insertBefore(emptyDom, parent.firstChild)
newChart.destroy()
newChart?.destroy()
}
}
export const numberToChineseUnderHundred = (num: number): string => {
// 合法性检查
if (num <= 0 || num > 99 || !Number.isInteger(num)) {
throw new Error('请输入1-99之间的整数')
}
const digits = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']
// 处理个位数
if (num < 10) return digits[num]
const tens = Math.floor(num / 10)
const ones = num % 10
// 处理整十
if (ones === 0) {
return tens === 1 ? '十' : digits[tens] + '十'
}
// 处理其他两位数
return tens === 1 ? '十' + digits[ones] : digits[tens] + '十' + digits[ones]
}
/**
*
* @param styleName
* @param callBack
*/
export const configRoundAngle = (chart: Chart, styleName: string, callBack?: (datum) => {}) => {
const { basicStyle } = parseJson(chart.customAttr)
if (['roundAngle', 'topRoundAngle'].includes(basicStyle.radiusColumnBar)) {
const radius = Array(2).fill(basicStyle.columnBarRightAngleRadius)
const topRadius = [0, 0, ...radius]
const bottomRadius = [...radius, 0, 0]
const finalRadius = [...radius, ...radius]
if (chart.type.includes('-stack')) {
return {
[styleName]: datum => {
if (!datum.value) return { radius: [], ...(callBack ? callBack(datum) : {}) }
return { radius: finalRadius, ...(callBack ? callBack(datum) : {}) }
}
}
}
const isTopRound = basicStyle.radiusColumnBar === 'topRoundAngle'
// 对称条形图
if (chart.type === 'bidirectional-bar') {
const valueField = basicStyle.layout === 'vertical' ? 'valueExt' : 'value'
return {
[styleName]: datum => ({
radius: datum[valueField] && isTopRound ? topRadius : isTopRound ? radius : finalRadius,
...(callBack ? callBack(datum) : {})
})
}
}
// 进度条
if (chart.type === 'progress-bar') {
return {
[styleName]: datum => {
return {
radius: isTopRound ? bottomRadius : finalRadius,
...(callBack ? callBack(datum) : {})
}
}
}
}
// 区间条形图
if (chart.type === 'bar-range') {
return {
[styleName]: datum => {
return {
radius:
datum?.values[0] < datum?.values[1]
? isTopRound
? bottomRadius
: finalRadius
: isTopRound
? topRadius
: finalRadius,
...(callBack ? callBack(datum) : {})
}
}
}
}
// 配置柱条样式
const style = datum => {
if (isTopRound) {
return { radius, ...(callBack ? callBack(datum) : {}) }
}
if (!isTopRound) {
return { radius: finalRadius, ...(callBack ? callBack(datum) : {}) }
}
}
return {
[styleName]: style
}
}
return {
[styleName]: datum => {
return { ...(callBack ? callBack(datum) : {}) }
}
}
}

View File

@ -5,8 +5,10 @@ import {
isAlphaColor,
isTransparent,
parseJson,
resetRgbOpacity
} from '../../util'
resetRgbOpacity,
safeDecimalSum,
safeDecimalMean
} from '../..//util'
import {
DEFAULT_BASIC_STYLE,
DEFAULT_TABLE_CELL,
@ -43,13 +45,31 @@ import {
updateShapeAttr,
ViewMeta
} from '@antv/s2'
import { cloneDeep, filter, find, intersection, keys, merge, repeat } from 'lodash-es'
import {
cloneDeep,
filter,
find,
intersection,
keys,
map,
maxBy,
meanBy,
merge,
minBy,
repeat,
sumBy,
size,
sum
} from 'lodash-es'
import { createVNode, render } from 'vue'
import TableTooltip from '@/data-visualization/chart/components/editor/common/TableTooltip.vue'
import Exceljs from 'exceljs'
import { saveAs } from 'file-saver'
import { ElMessage } from 'element-plus-secondary'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import Decimal from 'decimal.js'
const { t: i18nt } = useI18n()
export function getCustomTheme(chart: Chart): S2Theme {
@ -401,8 +421,7 @@ export function getCustomTheme(chart: Chart): S2Theme {
},
dataCell: {
cell: {
crossBackgroundColor:
enableTableCrossBG && !tableCell.mergeCells ? tableItemSubBgColor : tableItemBgColor,
crossBackgroundColor: enableTableCrossBG ? tableItemSubBgColor : tableItemBgColor,
backgroundColor: tableItemBgColor
},
bolderText: {
@ -504,8 +523,8 @@ export function getStyle(chart: Chart, dataConfig: S2DataConfig): Style {
item => item.id === chart.drillFilters[0].fieldId
)
const drillEnterField = xAxis[drillEnterFieldIndex]
fieldMap[curDrillField.dataeaseName] = {
width: fieldMap[drillEnterField.dataeaseName]?.width
fieldMap[curDrillField.gisbiName] = {
width: fieldMap[drillEnterField.gisbiName]?.width
}
}
// 铺满
@ -579,7 +598,7 @@ export function getCurrentField(valueFieldList: Axis[], field: ChartViewField) {
if (list) {
for (let i = 0; i < list.length; i++) {
const f = list[i]
if (field.dataeaseName === f.dataeaseName) {
if (field.gisbiName === f.gisbiName) {
res = f
break
}
@ -600,10 +619,10 @@ export function getConditions(chart: Chart) {
}
const conditions = threshold.tableThreshold ?? []
const dimFields = [...chart.xAxis, ...chart.xAxisExt].map(i => i.dataeaseName)
const dimFields = [...chart.xAxis, ...chart.xAxisExt].map(i => i.gisbiName)
if (conditions?.length > 0) {
const { tableCell, basicStyle, tableHeader } = parseJson(chart.customAttr)
// 合并单元格时,班马纹失效
// 合并单元格时马纹失效
const enableTableCrossBG =
chart.type === 'table-info'
? tableCell.enableTableCrossBG && !tableCell.mergeCells
@ -626,12 +645,12 @@ export function getConditions(chart: Chart) {
let defaultValueColor = valueColor
let defaultBgColor = valueBgColor
// 透视表表头颜色配置
if (chart.type === 'table-pivot' && dimFields.includes(field.field.dataeaseName)) {
if (chart.type === 'table-pivot' && dimFields.includes(field.field.gisbiName)) {
defaultValueColor = headerValueColor
defaultBgColor = headerValueBgColor
}
res.text.push({
field: field.field.dataeaseName,
field: field.field.gisbiName,
mapping(value, rowData) {
// 总计小计
if (rowData?.isTotals) {
@ -647,7 +666,7 @@ export function getConditions(chart: Chart) {
}
})
res.background.push({
field: field.field.dataeaseName,
field: field.field.gisbiName,
mapping(value, rowData) {
if (rowData?.isTotals) {
return null
@ -783,6 +802,9 @@ export function mappingColor(value, defaultColor, field, type, filedValueMap?, r
}
} else {
// time
if (!tv || !value) {
break
}
const fc = field.conditions[i]
tv = new Date(tv.replace(/-/g, '/') + ' GMT+8').getTime()
const v = new Date(value.replace(/-/g, '/') + ' GMT+8').getTime()
@ -842,7 +864,7 @@ function getFieldValueMap(view) {
function getValue(field, filedValueMap, rowData) {
if (field.summary === 'value') {
return rowData ? rowData[field.field?.dataeaseName] : undefined
return rowData ? rowData[field.field?.gisbiName] : undefined
} else {
return filedValueMap[field.summary + '-' + field.fieldId]
}
@ -880,6 +902,7 @@ export function handleTableEmptyStrategy(chart: Chart) {
}
return newData
}
export class SortTooltip extends BaseTooltip {
show(showOptions) {
const { iconName } = showOptions
@ -934,6 +957,7 @@ export class SortTooltip extends BaseTooltip {
})
}
}
const SORT_DEFAULT =
'<svg t="1711681787276" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4355" width="200" height="200"><path d="M922.345786 372.183628l-39.393195 38.687114L676.138314 211.079416l0 683.909301-54.713113 0L621.425202 129.010259l53.320393 0L922.345786 372.183628zM349.254406 894.989741 101.654214 651.815349l39.393195-38.687114 206.814276 199.792349L347.861686 129.010259l54.713113 0 0 765.978459L349.254406 894.988718z" fill="{fill}" p-id="4356"></path></svg>'
const SORT_UP =
@ -1063,7 +1087,14 @@ export function copyContent(s2Instance: SpreadSheet, event, fieldMeta) {
if (cells.length === 1) {
const curCell = cells[0]
if (cell.getMeta().id === curCell.id) {
copyString(cellMeta.value + '', true)
const cellMeta = cell.getMeta()
const value = cellMeta.data?.[cellMeta.valueField]
const metaObj = find(fieldMeta, m => m.field === cellMeta.valueField)
let fieldVal = value?.toString()
if (metaObj) {
fieldVal = metaObj.formatter(value)
}
copyString(fieldVal, true)
}
s2Instance.interaction.clearState()
return
@ -1189,7 +1220,7 @@ 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 colNums = layoutResult.colLeafNodes.length + rowLength + 1
const colNums = layoutResult.colLeafNodes.length + rowLength
if (colNums > 16384) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed'))
return
@ -1346,9 +1377,180 @@ export async function exportGridPivot(instance: PivotSheet, chart: ChartObj) {
if (fieldValue === 0 || fieldValue) {
const meta = metaMap[dataCellMeta.valueField]
const cell = worksheet.getCell(rowIndex + maxColHeight + 1, rowLength + colIndex + 1)
const value = meta?.formatter?.(fieldValue) || fieldValue.toString()
const value = meta?.formatter?.(fieldValue) || fieldValue
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.value = isNumeric(value) ? parseFloat(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 exportRowQuotaGridPivot(instance: PivotSheet, chart: ChartObj) {
const { layoutResult } = instance.facet
const { meta, fields } = instance.dataCfg
const rowLength = fields?.rows?.length || 0
const colLength = fields?.columns?.length || 0
const colNums = layoutResult.colLeafNodes.length + rowLength
if (colNums > 16384) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed'))
return
}
const workbook = new Exceljs.Workbook()
const worksheet = workbook.addWorksheet(i18nt('chart.chart_data'))
const metaMap: Record<string, Meta> = meta?.reduce((p, n) => {
if (n.field) {
p[n.field] = n
}
return p
}, {})
// 角头
if (colLength > 1) {
fields.columns.forEach((column: string, index) => {
if (index >= colLength - 1) {
return
}
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' } }
}
worksheet.mergeCells(index + 1, 1, index + 1, rowLength + 1)
})
}
fields?.rows?.forEach((row, index) => {
const cell = worksheet.getCell(colLength === 0 ? 1 : colLength, index + 1)
cell.value = metaMap[row]?.name ?? row
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.border = { bottom: { style: 'thick', color: { argb: '00000000' } } }
})
const quotaColLabel = chart.customAttr.basicStyle.quotaColLabel ?? t('dataset.value')
const quotaColHeadCell = worksheet.getCell(colLength === 0 ? 1 : colLength, rowLength + 1)
quotaColHeadCell.value = quotaColLabel
quotaColHeadCell.alignment = { vertical: 'middle', horizontal: 'center' }
quotaColHeadCell.border = {
bottom: { style: 'thick', color: { argb: '00000000' } },
right: { style: 'thick', color: { argb: '00000000' } }
}
// 行头
const { rowLeafNodes, rowNodes } = layoutResult
const notLeafNodeHeightMap: Record<string, number> = {}
rowLeafNodes.forEach(node => {
// 行头的高度由子节点相加决定,也就是行头子节点中包含的叶子节点数量
let curNode = node.parent
while (curNode) {
const height = notLeafNodeHeightMap[curNode.id] ?? 0
notLeafNodeHeightMap[curNode.id] = height + 1
curNode = curNode.parent
}
const { rowIndex } = node
const writeRowIndex = rowIndex + 2 + (colLength === 0 ? 1 : colLength - 1)
const writeColIndex = node.level + 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' }
cell.border = {
right: { style: 'thick', color: { argb: '00000000' } }
}
})
const getNodeStartRowIndex = (node: Node) => {
if (!node.children?.length) {
return node.rowIndex + 1
} else {
return getNodeStartRowIndex(node.children[0])
}
}
rowNodes?.forEach(node => {
if (node.isLeaf) {
return
}
const rowIndex = getNodeStartRowIndex(node)
const height = notLeafNodeHeightMap[node.id]
const writeRowIndex = rowIndex + 1 + (colLength === 0 ? 1 : colLength - 1)
const mergeColCount = node.children[0].level - node.level
const cell = worksheet.getCell(writeRowIndex, node.level + 1)
cell.value = node.label
cell.alignment = { vertical: 'middle', horizontal: 'center' }
if (mergeColCount > 1 || height > 1) {
worksheet.mergeCells(
writeRowIndex,
node.level + 1,
writeRowIndex + height - 1,
node.level + mergeColCount
)
}
})
// 列头
const { colLeafNodes, colNodes, colsHierarchy } = layoutResult
const maxColHeight = colsHierarchy.maxLevel + 1
const notLeafNodeWidthMap: Record<string, number> = {}
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 + rowLength + 2
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
const value = node.label
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 getNodeStartColIndex = (node: Node) => {
if (!node.children?.length) {
return node.colIndex + 1
} else {
return getNodeStartColIndex(node.children[0])
}
}
colNodes.forEach(node => {
if (node.isLeaf) {
return
}
const colIndex = getNodeStartColIndex(node)
const width = notLeafNodeWidthMap[node.id]
const writeRowIndex = node.level + 1
const value = node.label
const writeColIndex = colIndex + rowLength + 1
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
cell.value = value
cell.alignment = { vertical: 'middle', horizontal: 'center' }
if (width > 1) {
worksheet.mergeCells(writeRowIndex, writeColIndex, writeRowIndex, 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, rowLength + colIndex + 2)
const value = meta?.formatter?.(fieldValue) || fieldValue
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.value = isNumeric(value) ? parseFloat(value) : value
}
}
}
@ -1361,7 +1563,7 @@ export async function exportGridPivot(instance: PivotSheet, chart: ChartObj) {
export async function exportTreePivot(instance: PivotSheet, chart: ChartObj) {
const layoutResult = instance.facet.layoutResult
if (layoutResult.colLeafNodes.length + 2 > 16384) {
if (layoutResult.colLeafNodes.length + 1 > 16384) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed'))
return
}
@ -1468,9 +1670,9 @@ export async function exportTreePivot(instance: PivotSheet, chart: ChartObj) {
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()
const value = meta?.formatter?.(fieldValue) || fieldValue
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.value = value
cell.value = isNumeric(value) ? parseFloat(value) : value
}
}
}
@ -1480,6 +1682,135 @@ export async function exportTreePivot(instance: PivotSheet, chart: ChartObj) {
})
saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`)
}
export async function exportRowQuotaTreePivot(instance: PivotSheet, chart: ChartObj) {
const layoutResult = instance.facet.layoutResult
if (layoutResult.colLeafNodes.length + 1 > 16384) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed'))
return
}
const { meta, fields } = instance.dataCfg
const colLength = fields?.columns?.length || 0
const workbook = new Exceljs.Workbook()
const worksheet = workbook.addWorksheet(i18nt('chart.chart_data'))
const metaMap: Record<string, Meta> = meta?.reduce((p, n) => {
if (n.field) {
p[n.field] = n
}
return p
}, {})
// 角头
fields.columns?.forEach((column, index) => {
if (index >= fields.columns.length - 1) {
return
}
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 quotaColLabel = chart.customAttr.basicStyle.quotaColLabel ?? t('dataset.value')
const maxColHeight = layoutResult.colsHierarchy.maxLevel + 1
const rowName = fields?.rows
?.map(row => metaMap[row]?.name ?? row)
.concat(quotaColLabel)
.join('/')
const cell = worksheet.getCell(colLength, 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)
let value = node.label
if (node.field === '$$extra$$' && metaMap[value]?.name) {
value = metaMap[value].name
}
cell.value = repeat(' ', node.level) + value
cell.alignment = { vertical: 'middle', horizontal: 'left' }
cell.border = {
right: { style: 'thick', color: { argb: '00000000' } }
}
})
// 列头
const notLeafNodeWidthMap: Record<string, number> = {}
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 + 2
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
cell.value = node.label
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 writeColIndex = colIndex + 1
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
cell.value = node.label
cell.alignment = { vertical: 'middle', horizontal: 'center' }
if (width > 1) {
worksheet.mergeCells(writeRowIndex, writeColIndex, writeRowIndex, 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 + 2)
const value = meta?.formatter?.(fieldValue) || fieldValue
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.value = isNumeric(value) ? parseFloat(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`)
}
function isNumeric(value: string): boolean {
return /^[+-]?\d+(\.\d+)?$/.test(value)
}
export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) {
const { fields } = instance.dataCfg
const rowLength = fields?.rows?.length || 0
@ -1488,12 +1819,21 @@ export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_field'))
return
}
const { quotaPosition } = chart.customAttr.basicStyle
if (chart.customAttr.basicStyle.tableLayoutMode !== 'tree') {
if (quotaPosition === 'row') {
exportRowQuotaGridPivot(instance, chart)
} else {
exportGridPivot(instance, chart)
}
} else {
if (quotaPosition === 'row') {
exportRowQuotaTreePivot(instance, chart)
} else {
exportTreePivot(instance, chart)
}
}
}
export function configMergeCells(chart: Chart, options: S2Options, dataConfig: S2DataConfig) {
const { mergeCells } = parseJson(chart.customAttr).tableCell
@ -1504,7 +1844,7 @@ export function configMergeCells(chart: Chart, options: S2Options, dataConfig: S
const fields = chart.data.fields || []
const fieldsMap =
fields.reduce((p, n) => {
p[n.dataeaseName] = n
p[n.gisbiName] = n
return p
}, {}) || {}
const quotaIndex = dataConfig.meta.findIndex(m => fieldsMap[m.field]?.groupType === 'q')
@ -1571,6 +1911,7 @@ export function configMergeCells(chart: Chart, options: S2Options, dataConfig: S
if (showIndex && meta.colIndex === 0) {
meta.fieldValue = getRowIndex(mergedCellsInfo, meta)
}
meta.deFieldType = fieldsMap[meta.valueField]?.deType
return new CustomMergedCell(sheet, cells, meta)
}
}
@ -1598,12 +1939,13 @@ export function getRowIndex(mergedCellsInfo: MergedCellInfo[][], meta: ViewMeta)
}, 0)
return curRangeStartIndex - lostCells + 1
}
class CustomMergedCell extends MergedCell {
protected drawBackgroundShape() {
const allPoints = getPolygonPoints(this.cells)
// 处理条件样式,这里没有用透明度
// 因为合并的单元格是单独的图层,透明度降低的话会显示底下未合并的单元格,需要单独处理被覆盖的单元格
const { backgroundColor: fill, backgroundColorOpacity: fillOpacity } = this.getBackgroundColor()
const { backgroundColor: fill } = this.getBackgroundColor()
const cellTheme = this.theme.dataCell.cell
this.backgroundShape = renderPolygon(this, {
points: allPoints,
@ -1612,6 +1954,14 @@ class CustomMergedCell extends MergedCell {
lineHeight: cellTheme.horizontalBorderWidth
})
}
drawTextShape(): void {
if (this.meta.deFieldType === 7) {
drawImage.apply(this)
} else {
super.drawTextShape()
}
}
}
export class CustomDataCell extends TableDataCell {
@ -1847,103 +2197,103 @@ 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 yAxis = chart.yAxis
const xAxis = chart.xAxis
const summaryObj = newData.reduce(
(p, n) => {
if (chart.type === 'table-info') {
xAxis
.filter(axis => [2, 3, 4].includes(axis.deType))
.forEach(axis => {
p[axis.dataeaseName] =
(parseFloat(n[axis.dataeaseName]) || 0) + (parseFloat(p[axis.dataeaseName]) || 0)
})
// 导出获取汇总行的函数
export function getSummaryRow(data, axis, sumCon = []) {
const summaryObj = { SUMMARY: true }
for (let i = 0; i < axis.length; i++) {
const a = axis[i].gisbiName
let savedAxis = find(sumCon, s => s.field === a)
if (savedAxis) {
if (savedAxis.summary == undefined) {
savedAxis.summary = 'sum' // 默认汇总方式为求和
}
if (savedAxis.show == undefined) {
savedAxis.show = true // 默认显示汇总结果
}
} else {
yAxis.forEach(axis => {
p[axis.dataeaseName] =
(parseFloat(n[axis.dataeaseName]) || 0) + (parseFloat(p[axis.dataeaseName]) || 0)
})
savedAxis = {
field: a,
summary: 'sum',
show: true
}
return p
},
{ SUMMARY: true }
)
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')
// 如果配置为不显示,则跳过该字段
if (!savedAxis.show) {
continue
}
// 根据汇总方式处理数据
switch (savedAxis.summary) {
case 'sum':
// 计算字段的总和
summaryObj[a] = safeDecimalSum(data, a)
break
case 'avg':
// 计算字段的平均值
summaryObj[a] = safeDecimalMean(data, a)
break
case 'max':
// 计算字段的最大值
summaryObj[a] = maxBy(
filter(data, d => parseFloat(d[a]) !== undefined),
d => parseFloat(d[a]) // 提取数值
)[a]
break
case 'min':
// 计算字段的最小值
summaryObj[a] = minBy(
filter(data, d => parseFloat(d[a]) !== undefined),
d => parseFloat(d[a]) // 提取数值
)[a]
break
case 'var_pop':
// 计算总体方差需要至少2个数据点
if (data.length < 2) {
continue
} else {
if (xAxis.length) {
viewMeta.fieldValue = basicStyle.summaryLabel ?? i18nt('chart.total_show')
const mean = safeDecimalMean(data, a) // 计算平均值
// 计算每个数据点与平均值的差的平方
const squaredDeviations = map(data, d => {
const value = new Decimal(d[a] ?? 0) // 获取字段值如果不存在则使用0
const dev = value.minus(mean) // 计算差值
return dev.times(dev) // 计算平方
})
// 计算方差(平方差的平均值)
const variance = squaredDeviations.reduce((acc, val) => acc.plus(val), new Decimal(0))
summaryObj[a] = variance.dividedBy(data.length - 1).toNumber() // 计算总体方差
}
break
case 'stddev_pop':
// 计算总体标准差需要至少2个数据点
if (data.length < 2) {
continue
} else {
const mean = safeDecimalMean(data, a) // 计算平均值
// 计算每个数据点与平均值的差的平方
const squaredDeviations = map(data, d => {
const value = new Decimal(d[a] ?? 0) // 获取字段值如果不存在则使用0
const dev = value.minus(mean) // 计算差值
return dev.times(dev) // 计算平方
})
// 计算方差(平方差的平均值)
const variance = squaredDeviations.reduce((acc, val) => acc.plus(val), new Decimal(0))
summaryObj[a] = variance.dividedBy(data.length - 1).sqrt().toNumber() // 计算总体标准差
}
}
return new SummaryCell(viewMeta, viewMeta.spreadsheet)
break
}
}
/**
* ,
* @param newChart
* @param newData
* @param tableCell
* @param tableHeader
* @param showSummary
*/
export const summaryRowStyle = (newChart, newData, tableCell, tableHeader, showSummary) => {
if (!showSummary || !newData.length) return
newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => {
const showHeader = tableHeader.showTableHeader === true
// 不显示表头时,减少一个表头的高度
const headerAndSummaryHeight = showHeader ? 2 : 1
const totalHeight =
tableHeader.tableTitleHeight * headerAndSummaryHeight +
tableCell.tableItemHeight * (newData.length - 1)
if (totalHeight < newChart.options.height) {
// 6 是阴影高度
newChart.options.height =
totalHeight < newChart.options.height - 6 ? totalHeight + 6 : totalHeight
}
})
// 返回汇总结果对象
return summaryObj
}
export class SummaryCell extends CustomDataCell {
getTextStyle() {
const textStyle = cloneDeep(this.theme.colCell.bolderText)
textStyle.textAlign = this.theme.dataCell.text.textAlign
return textStyle
}
getBackgroundColor() {
const { backgroundColor, backgroundColorOpacity } = this.theme.colCell.cell
return { backgroundColor, backgroundColorOpacity }
@ -2019,3 +2369,27 @@ export const getColumns = (fields, cols: Array<ColumnNode>) => {
}
return result
}
export function drawImage() {
const img = new Image()
const { x, y, width, height, fieldValue } = this.meta
img.src = fieldValue as string
img.setAttribute('crossOrigin', 'anonymous')
img.onload = () => {
!this.cfg.children && (this.cfg.children = [])
const { width: imgWidth, height: imgHeight } = img
const ratio = Math.max(imgWidth / width, imgHeight / height)
// 不铺满,部分留白
const imgShowWidth = (imgWidth / ratio) * 0.8
const imgShowHeight = (imgHeight / ratio) * 0.8
this.textShape = this.addShape('image', {
attrs: {
x: x + (imgShowWidth < width ? (width - imgShowWidth) / 2 : 0),
y: y + (imgShowHeight < height ? (height - imgShowHeight) / 2 : 0),
width: imgShowWidth,
height: imgShowHeight,
img
}
})
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -170,7 +170,7 @@ export abstract class G2PlotChartView<
public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
return setupSeriesColor(chart, data)
}
// eslint-disable-next-line
public setupSubSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
return undefined
}
@ -191,8 +191,8 @@ export abstract class G2PlotChartView<
return addConditionsStyleColorToData(chart, data)
}
protected configEmptyDataStyle(newChart, newData: any[], container: string) {
configEmptyDataStyle(newChart, newData, container)
protected configEmptyDataStyle(newData, container, newChart?, content?) {
configEmptyDataStyle(newData, container, newChart, content)
}
/**

View File

@ -107,8 +107,8 @@ export abstract class L7ChartView<
return options
}
protected configZoomButton(chart: Chart, plot: S) {
configL7Zoom(chart, plot)
protected configZoomButton(chart: Chart, plot: S, mapKey?: any) {
configL7Zoom(chart, plot, mapKey)
}
protected configLabel(chart: Chart, options: O): O {

View File

@ -136,7 +136,6 @@ export abstract class S2ChartView<P extends SpreadSheet> extends AntVAbstractCha
if (duration > 300) {
return
}
const callback = () => {
const canvasPosition = canvas.getBoundingClientRect()
const touchPosition = [e.changedTouches[0].pageX, e.changedTouches[0].pageY]
const relativePosition = [
@ -144,10 +143,11 @@ export abstract class S2ChartView<P extends SpreadSheet> extends AntVAbstractCha
touchPosition[1] - canvasPosition.y
]
const shape = s2Instance.container.getShape(relativePosition[0], relativePosition[1])
// 图片单元格点击放大图片
if (shape.cfg?.parent.constructor.name === 'ImageCell') {
// 图片单元格,表头排序图标点击放大图片
if (shape.cfg?.type === 'image') {
return
}
const callback = () => {
e.preventDefault()
e.stopPropagation()
if (shape) {