mirror of
https://github.com/dataease/dataease.git
synced 2025-02-24 11:32:57 +08:00
feat(图表): 支持圆形填充图
This commit is contained in:
parent
7aff0c0dbe
commit
7f5e2174f4
16
core/core-frontend/src/assets/svg/circle-packing-dark.svg
Normal file
16
core/core-frontend/src/assets/svg/circle-packing-dark.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<svg width="80" height="56" viewBox="0 0 80 56" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="40" cy="28" r="25"/>
|
||||
<circle cx="40" cy="14" r="8" fill="#26DCC3"/>
|
||||
<circle cx="43" cy="42" r="5" fill="#00D6B9"/>
|
||||
<circle cx="31" cy="43" r="5" fill="#3370FF"/>
|
||||
<circle cx="28" cy="19" r="3" fill="#00D6B9"/>
|
||||
<circle cx="53" cy="29" r="10" fill="#3370FF"/>
|
||||
<circle cx="34" cy="29" r="7" fill="#3370FF"/>
|
||||
<circle cx="38.5" cy="11.5" r="3.5" fill="#99EFE3"/>
|
||||
<circle cx="43.5" cy="16.5" r="2.5" fill="#99EFE3"/>
|
||||
<circle cx="49.5" cy="27.5" r="4.5" fill="#ADC6FF"/>
|
||||
<circle cx="58" cy="27" r="3" fill="#ADC6FF"/>
|
||||
<circle cx="58" cy="27" r="3" fill="#ADC6FF"/>
|
||||
<circle cx="55" cy="34" r="3" fill="#ADC6FF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 735 B |
15
core/core-frontend/src/assets/svg/circle-packing-origin.svg
Normal file
15
core/core-frontend/src/assets/svg/circle-packing-origin.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="80" height="56" viewBox="0 0 80 56" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="40" cy="28" r="25"/>
|
||||
<circle cx="40" cy="14" r="8" fill="#26DCC3"/>
|
||||
<circle cx="43" cy="42" r="5" fill="#00D6B9"/>
|
||||
<circle cx="31" cy="43" r="5" fill="#3370FF"/>
|
||||
<circle cx="28" cy="19" r="3" fill="#00D6B9"/>
|
||||
<circle cx="53" cy="29" r="10" fill="#3370FF"/>
|
||||
<circle cx="34" cy="29" r="7" fill="#3370FF"/>
|
||||
<circle cx="38.5" cy="11.5" r="3.5" fill="#99EFE3"/>
|
||||
<circle cx="43.5" cy="16.5" r="2.5" fill="#99EFE3"/>
|
||||
<circle cx="49.5" cy="27.5" r="4.5" fill="#ADC6FF"/>
|
||||
<circle cx="58" cy="27" r="3" fill="#ADC6FF"/>
|
||||
<circle cx="58" cy="27" r="3" fill="#ADC6FF"/>
|
||||
<circle cx="55" cy="34" r="3" fill="#ADC6FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 734 B |
15
core/core-frontend/src/assets/svg/circle-packing.svg
Normal file
15
core/core-frontend/src/assets/svg/circle-packing.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="40" cy="28" r="25" fill="#D6E2FF"/>
|
||||
<circle cx="40" cy="14" r="8" fill="#26DCC3"/>
|
||||
<circle cx="43" cy="42" r="5" fill="#00D6B9"/>
|
||||
<circle cx="31" cy="43" r="5" fill="#3370FF"/>
|
||||
<circle cx="28" cy="19" r="3" fill="#00D6B9"/>
|
||||
<circle cx="53" cy="29" r="10" fill="#3370FF"/>
|
||||
<circle cx="34" cy="29" r="7" fill="#3370FF"/>
|
||||
<circle cx="38.5" cy="11.5" r="3.5" fill="#99EFE3"/>
|
||||
<circle cx="43.5" cy="16.5" r="2.5" fill="#99EFE3"/>
|
||||
<circle cx="49.5" cy="27.5" r="4.5" fill="#ADC6FF"/>
|
||||
<circle cx="58" cy="27" r="3" fill="#ADC6FF"/>
|
||||
<circle cx="58" cy="27" r="3" fill="#ADC6FF"/>
|
||||
<circle cx="55" cy="34" r="3" fill="#ADC6FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 760 B |
@ -73,6 +73,7 @@ import { lockStoreWithOut } from '@/store/modules/data-visualization/lock'
|
||||
import ContextMenuAsideDetails from '@/components/data-visualization/canvas/ContextMenuAsideDetails.vue'
|
||||
import ComposeShow from '@/components/data-visualization/canvas/ComposeShow.vue'
|
||||
import { composeStoreWithOut } from '@/store/modules/data-visualization/compose'
|
||||
import circlePackingOrigin from '@/assets/svg/circle-packing-origin.svg'
|
||||
const dropdownMore = ref(null)
|
||||
const lockStore = lockStoreWithOut()
|
||||
|
||||
@ -233,7 +234,8 @@ const iconMap = {
|
||||
'waterfall-origin': waterfallOrigin,
|
||||
'word-cloud-origin': wordCloudOrigin,
|
||||
't-heatmap-origin': tHeatmapOrigin,
|
||||
group: group
|
||||
group: group,
|
||||
'circle-packing-origin': circlePackingOrigin
|
||||
}
|
||||
const getIconName = item => {
|
||||
if (item.component === 'UserView') {
|
||||
|
@ -80,6 +80,7 @@ import RealTimeGroup from '@/components/data-visualization/RealTimeGroup.vue'
|
||||
import { contextmenuStoreWithOut } from '@/store/modules/data-visualization/contextmenu'
|
||||
import RealTimeTab from '@/components/data-visualization/RealTimeTab.vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import circlePackingOrigin from '@/assets/svg/circle-packing-origin.svg'
|
||||
const dropdownMore = ref(null)
|
||||
const lockStore = lockStoreWithOut()
|
||||
|
||||
@ -329,7 +330,8 @@ const iconMap = {
|
||||
'word-cloud-origin': wordCloudOrigin,
|
||||
't-heatmap-origin': tHeatmapOrigin,
|
||||
'picture-group-origin': pictureGroupOrigin,
|
||||
group: group
|
||||
group: group,
|
||||
'circle-packing-origin': circlePackingOrigin
|
||||
}
|
||||
const getIconName = item => {
|
||||
if (item.component === 'UserView') {
|
||||
|
@ -344,7 +344,8 @@ const boardMoveActive = computed(() => {
|
||||
'table-pivot',
|
||||
'symbolic-map',
|
||||
'heat-map',
|
||||
't-heatmap'
|
||||
't-heatmap',
|
||||
'circle-packing'
|
||||
]
|
||||
return element.value.isPlugin || CHARTS.includes(element.value.innerType)
|
||||
})
|
||||
|
@ -42,6 +42,7 @@ import treemapDark from '@/assets/svg/treemap-dark.svg'
|
||||
import waterfallDark from '@/assets/svg/waterfall-dark.svg'
|
||||
import wordCloudDark from '@/assets/svg/word-cloud-dark.svg'
|
||||
import tHeatmapDark from '@/assets/svg/t-heatmap-dark.svg'
|
||||
import circlePackingDark from '@/assets/svg/circle-packing-dark.svg'
|
||||
|
||||
const iconChartDarkMap = {
|
||||
'area-dark': areaDark,
|
||||
@ -87,7 +88,8 @@ const iconChartDarkMap = {
|
||||
'treemap-dark': treemapDark,
|
||||
'waterfall-dark': waterfallDark,
|
||||
'word-cloud-dark': wordCloudDark,
|
||||
't-heatmap-dark': tHeatmapDark
|
||||
't-heatmap-dark': tHeatmapDark,
|
||||
'circle-packing-dark': circlePackingDark
|
||||
}
|
||||
|
||||
export { iconChartDarkMap }
|
||||
|
@ -45,6 +45,7 @@ import tHeatmap from '@/assets/svg/t-heatmap.svg'
|
||||
import pictureGroup from '@/assets/svg/picture-group.svg'
|
||||
import filter from '@/assets/svg/filter.svg'
|
||||
import outerParams from '@/assets/svg/icon_params_setting.svg'
|
||||
import circlePacking from '@/assets/svg/circle-packing.svg'
|
||||
|
||||
const iconChartMap = {
|
||||
'area-stack': areaStack,
|
||||
@ -93,7 +94,8 @@ const iconChartMap = {
|
||||
't-heatmap': tHeatmap,
|
||||
'picture-group': pictureGroup,
|
||||
filter: filter,
|
||||
outerParams: outerParams
|
||||
outerParams: outerParams,
|
||||
'circle-packing': circlePacking
|
||||
}
|
||||
|
||||
export { iconChartMap }
|
||||
|
@ -1919,7 +1919,13 @@ export default {
|
||||
'When Customizing, Supports SVG, JPG, JPEG, and PNG files up to 1MB',
|
||||
size_range: 'Size Range',
|
||||
x_axis_constant_line: 'X-axis Constant Line',
|
||||
y_axis_constant_line: 'Y-axis Constant Line'
|
||||
y_axis_constant_line: 'Y-axis Constant Line',
|
||||
chart_circle_packing: 'Circle Packing Chart',
|
||||
circle_packing_name: 'Circle Name',
|
||||
circle_packing_value: 'Circle Size',
|
||||
circle_packing_border_color: 'Border color',
|
||||
circle_packing_border_width: 'Border width',
|
||||
circle_packing_padding: 'Circle padding'
|
||||
},
|
||||
dataset: {
|
||||
scope_edit: 'Only effective when editing',
|
||||
|
@ -1919,7 +1919,13 @@ Scatter chart (bubble) chart: {a} (series name), {b} (data name), {c} (value arr
|
||||
x_axis_constant_line: 'X-axis Constant Line',
|
||||
y_axis_constant_line: 'Y-axis Constant Line',
|
||||
sort_priority: 'Sort Priority Setting',
|
||||
sort_priority_tip: 'Top-down, sorting priority from highest to lowest'
|
||||
sort_priority_tip: 'Top-down, sorting priority from highest to lowest',
|
||||
chart_circle_packing: 'Circle packing chart',
|
||||
circle_packing_name: 'Circle name',
|
||||
circle_packing_value: 'Circle size',
|
||||
circle_packing_border_color: 'Border color',
|
||||
circle_packing_border_width: 'Border width',
|
||||
circle_packing_padding: 'Circle padding'
|
||||
},
|
||||
dataset: {
|
||||
scope_edit: 'Only effective when editing',
|
||||
|
@ -1877,7 +1877,13 @@ export default {
|
||||
x_axis_constant_line: 'X 軸恆線',
|
||||
y_axis_constant_line: 'Y 軸恆線',
|
||||
sort_priority: '排序優先級設置',
|
||||
sort_priority_tip: '自上而下,排序優先級從高到低'
|
||||
sort_priority_tip: '自上而下,排序優先級從高到低',
|
||||
chart_circle_packing: '圓形填充圖',
|
||||
circle_packing_name: '圓形名稱',
|
||||
circle_packing_value: '圓形大小',
|
||||
circle_packing_border_color: '邊線顏色',
|
||||
circle_packing_border_width: '邊線寬度',
|
||||
circle_packing_padding: 'Circle padding'
|
||||
},
|
||||
dataset: {
|
||||
scope_edit: '僅編輯時生效',
|
||||
|
@ -1879,7 +1879,13 @@ export default {
|
||||
x_axis_constant_line: 'X 轴恒线',
|
||||
y_axis_constant_line: 'Y 轴恒线',
|
||||
sort_priority: '排序优先级设置',
|
||||
sort_priority_tip: '自上而下,排序优先级从高到低'
|
||||
sort_priority_tip: '自上而下,排序优先级从高到低',
|
||||
chart_circle_packing: '圆形填充图',
|
||||
circle_packing_name: '圆形名称',
|
||||
circle_packing_value: '圆形大小',
|
||||
circle_packing_border_color: '边线颜色',
|
||||
circle_packing_border_width: '边线宽度',
|
||||
circle_packing_padding: '圓形間距'
|
||||
},
|
||||
dataset: {
|
||||
scope_edit: '仅编辑时生效',
|
||||
|
@ -350,6 +350,18 @@ declare interface ChartBasicStyle {
|
||||
* 雷达图面积颜色开关
|
||||
*/
|
||||
radarAreaColor: boolean
|
||||
/**
|
||||
* 圆形填充图边线颜色
|
||||
*/
|
||||
circleBorderColor: string
|
||||
/**
|
||||
* 圆形填充图边线宽度
|
||||
*/
|
||||
circleBorderWidth: number
|
||||
/**
|
||||
* 圆形填充图间距
|
||||
*/
|
||||
circlePadding: number
|
||||
}
|
||||
/**
|
||||
* 表头属性
|
||||
|
@ -93,6 +93,9 @@ const initFieldCtrl = () => {
|
||||
})
|
||||
}
|
||||
}
|
||||
const isCirclePacking = computed(() => {
|
||||
return equalsAny(props.chart.type, 'circle-packing')
|
||||
})
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
@ -196,7 +199,9 @@ onMounted(() => {
|
||||
</el-radio>
|
||||
<el-radio v-if="isRichText" :effect="themes" :label="'custom'"> 自定义 </el-radio>
|
||||
<template v-if="!isRichText">
|
||||
<el-radio :effect="themes" :label="'setZero'">{{ t('chart.set_zero') }}</el-radio>
|
||||
<el-radio v-if="!isCirclePacking" :effect="themes" :label="'setZero'">{{
|
||||
t('chart.set_zero')
|
||||
}}</el-radio>
|
||||
<el-radio v-if="showIgnoreOption" :effect="themes" :label="'ignoreData'">
|
||||
{{ t('chart.ignore_data') }}
|
||||
</el-radio>
|
||||
|
@ -13,7 +13,7 @@ import { SERIES_NUMBER_FIELD } from '@antv/s2'
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { isNumber } from 'mathjs'
|
||||
import { ElMessage, UploadProps } from 'element-plus-secondary'
|
||||
import { ElFormItem, ElInputNumber, ElMessage, UploadProps } from 'element-plus-secondary'
|
||||
import { svgStrToUrl } from '../../../js/util'
|
||||
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
@ -1389,6 +1389,69 @@ onMounted(() => {
|
||||
</el-row>
|
||||
</div>
|
||||
<!-- pie/rose end -->
|
||||
<!-- circle-packing start -->
|
||||
<div v-if="showProperty('circleBorderStyle')">
|
||||
<div class="alpha-setting">
|
||||
<el-row style="display: flex; width: 100%">
|
||||
<el-col :span="10">
|
||||
<el-form-item
|
||||
:label="t('chart.circle_packing_border_color')"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
>
|
||||
<el-color-picker
|
||||
v-model="state.basicStyleForm.circleBorderColor"
|
||||
class="color-picker-style"
|
||||
:triggerWidth="65"
|
||||
is-custom
|
||||
show-alpha
|
||||
:predefine="state.predefineColors"
|
||||
@change="changeBasicStyle('circleBorderColor')"
|
||||
>
|
||||
</el-color-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-form-item
|
||||
:label="t('chart.circle_packing_border_width')"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
>
|
||||
<el-input-number
|
||||
:min="0"
|
||||
:max="50"
|
||||
:effect="themes"
|
||||
controls-position="right"
|
||||
v-model="state.basicStyleForm.circleBorderWidth"
|
||||
class="color-picker-style"
|
||||
@change="changeBasicStyle('circleBorderWidth')"
|
||||
>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-form-item
|
||||
style="width: 150px"
|
||||
:label="t('chart.circle_packing_padding')"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
>
|
||||
<el-input-number
|
||||
:min="0"
|
||||
:max="10"
|
||||
:effect="themes"
|
||||
controls-position="right"
|
||||
v-model="state.basicStyleForm.circlePadding"
|
||||
class="color-picker-style"
|
||||
@change="changeBasicStyle('circlePadding')"
|
||||
>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</div>
|
||||
<!-- circle-packing end -->
|
||||
</div>
|
||||
</template>
|
||||
<style scoped lang="less">
|
||||
|
@ -1520,6 +1520,13 @@ export const CHART_TYPE_CONFIGS = [
|
||||
value: 'sankey',
|
||||
title: t('chart.chart_sankey'),
|
||||
icon: 'sankey'
|
||||
},
|
||||
{
|
||||
render: 'antv',
|
||||
category: 'distribute',
|
||||
value: 'circle-packing',
|
||||
title: t('chart.chart_circle_packing'),
|
||||
icon: 'circle-packing'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -0,0 +1,298 @@
|
||||
import {
|
||||
G2PlotChartView,
|
||||
G2PlotDrawOptions
|
||||
} from '@/views/chart/components/js/panel/types/impl/g2plot'
|
||||
import type {
|
||||
CirclePacking as G2CirclePacking,
|
||||
CirclePackingOptions
|
||||
} from '@antv/g2plot/esm/plots/circle-packing'
|
||||
import { flow, parseJson } from '@/views/chart/components/js/util'
|
||||
import { getPadding } from '@/views/chart/components/js/panel/common/common_antv'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import type { Datum } from '@antv/g2plot/esm/types/common'
|
||||
import { valueFormatter } from '@/views/chart/components/js/formatter'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const { t } = useI18n()
|
||||
const DEFAULT_DATA = []
|
||||
/**
|
||||
* 圆形填充图
|
||||
*/
|
||||
export class CirclePacking extends G2PlotChartView<CirclePackingOptions, G2CirclePacking> {
|
||||
properties: EditorProperty[] = [
|
||||
'basic-style-selector',
|
||||
'background-overall-component',
|
||||
'border-style',
|
||||
'label-selector',
|
||||
'legend-selector',
|
||||
'title-selector',
|
||||
'tooltip-selector',
|
||||
'jump-set',
|
||||
'linkage'
|
||||
]
|
||||
propertyInner: EditorPropertyInner = {
|
||||
'background-overall-component': ['all'],
|
||||
'border-style': ['all'],
|
||||
'basic-style-selector': ['colors', 'alpha', 'circleBorderStyle'],
|
||||
'title-selector': [
|
||||
'title',
|
||||
'fontSize',
|
||||
'color',
|
||||
'hPosition',
|
||||
'isItalic',
|
||||
'isBolder',
|
||||
'remarkShow',
|
||||
'fontFamily',
|
||||
'letterSpace',
|
||||
'fontShadow'
|
||||
],
|
||||
'function-cfg': ['emptyDataStrategy'],
|
||||
'label-selector': ['color', 'fontSize'],
|
||||
'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'],
|
||||
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'tooltipFormatter', 'show']
|
||||
}
|
||||
axis: AxisType[] = ['xAxis', 'yAxis', 'filter', 'drill']
|
||||
axisConfig: AxisConfig = {
|
||||
xAxis: {
|
||||
name: `${t('chart.circle_packing_name')} / ${t('chart.dimension')}`,
|
||||
type: 'd'
|
||||
},
|
||||
yAxis: {
|
||||
name: `${t('chart.circle_packing_value')} / ${t('chart.quota')}`,
|
||||
type: 'q',
|
||||
limit: 1,
|
||||
allowEmpty: true
|
||||
}
|
||||
}
|
||||
async drawChart(drawOptions: G2PlotDrawOptions<G2CirclePacking>): Promise<G2CirclePacking> {
|
||||
const { chart, container, action } = drawOptions
|
||||
if (chart?.data?.tableRow?.length) {
|
||||
// data
|
||||
const data = chart.data.tableRow
|
||||
const { xAxis, yAxis } = chart
|
||||
const ySort = yAxis[0]?.sort ?? 'none'
|
||||
const sort = {
|
||||
sort: (a, b) =>
|
||||
ySort === 'asc' ? a.value - b.value : ySort === 'desc' ? b.value - a.value : 0
|
||||
}
|
||||
// 根据配置获取节点的key,用于构建节点树,拖入字段顺序即为节点的层级
|
||||
const nodeKeys = xAxis.map(item => item.dataeaseName)
|
||||
// 将数据转为圆形填充图数据格式
|
||||
const getCirclePackingData = () => {
|
||||
const result = [{ name: t('commons.all'), children: [] }]
|
||||
const addNode = (nodes, item, level) => {
|
||||
if (level >= nodeKeys.length) return
|
||||
const key = nodeKeys[level]
|
||||
const value = item[key]
|
||||
let node = nodes.find(n => n.name === value)
|
||||
if (!node) {
|
||||
node = { name: value, field: xAxis.find(f => f.dataeaseName === key), children: [] }
|
||||
nodes.push(node)
|
||||
}
|
||||
if (level === nodeKeys.length - 1) {
|
||||
node.value = yAxis.length ? item[yAxis[0].dataeaseName] : 1
|
||||
} else {
|
||||
addNode(node.children, item, level + 1)
|
||||
}
|
||||
}
|
||||
data.forEach(item => addNode(result[0].children, item, 0))
|
||||
return result[0]
|
||||
}
|
||||
// options
|
||||
const initOptions: CirclePackingOptions = {
|
||||
data: getCirclePackingData(),
|
||||
appendPadding: getPadding(chart),
|
||||
hierarchyConfig: {
|
||||
...(ySort === 'none' ? {} : sort)
|
||||
},
|
||||
interactions: [
|
||||
{
|
||||
type: 'legend-active',
|
||||
cfg: {
|
||||
start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }],
|
||||
end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'legend-filter',
|
||||
cfg: {
|
||||
start: [
|
||||
{
|
||||
trigger: 'legend-item:click',
|
||||
action: [
|
||||
'list-unchecked:toggle',
|
||||
'data-filter:filter',
|
||||
'element-active:reset',
|
||||
'element-highlight:reset'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
const options = this.setupOptions(chart, initOptions)
|
||||
const { CirclePacking: G2CirclePacking } = await import(
|
||||
'@antv/g2plot/esm/plots/circle-packing'
|
||||
)
|
||||
const newChart = new G2CirclePacking(container, options)
|
||||
newChart.on('point:click', param => {
|
||||
const data = param?.data?.data
|
||||
if (data?.name === t('commons.all')) {
|
||||
return
|
||||
}
|
||||
action({
|
||||
x: param.x,
|
||||
y: param.y,
|
||||
data: {
|
||||
data: {
|
||||
...data,
|
||||
dimensionList: [
|
||||
{
|
||||
id: data?.field?.id,
|
||||
value: data.name,
|
||||
name: data.name
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
return newChart
|
||||
}
|
||||
}
|
||||
|
||||
protected configBasicStyle(chart: Chart, options: CirclePackingOptions): CirclePackingOptions {
|
||||
// size
|
||||
const customAttr: DeepPartial<ChartAttr> = parseJson(chart.customAttr)
|
||||
const s = JSON.parse(JSON.stringify(customAttr.basicStyle))
|
||||
// 圆形边框样式
|
||||
const pointStyle = {
|
||||
stroke: s.circleBorderColor,
|
||||
lineWidth: s.circleBorderWidth ?? 0
|
||||
}
|
||||
const padding = s.circlePadding
|
||||
return {
|
||||
...options,
|
||||
hierarchyConfig: {
|
||||
...options.hierarchyConfig,
|
||||
padding: padding / 100 ?? 0
|
||||
},
|
||||
pointStyle
|
||||
}
|
||||
}
|
||||
|
||||
protected configLabel(chart: Chart, options: CirclePackingOptions): CirclePackingOptions {
|
||||
const tmpOptions = super.configLabel(chart, options)
|
||||
if (!tmpOptions.label) {
|
||||
return {
|
||||
...tmpOptions,
|
||||
label: false
|
||||
}
|
||||
}
|
||||
const { label: labelAttr } = parseJson(chart.customAttr)
|
||||
const label = {
|
||||
...tmpOptions.label,
|
||||
textAlign: 'center',
|
||||
offsetY: 5,
|
||||
layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout,
|
||||
formatter: (d: Datum, _point) => {
|
||||
return d.children.length === 0 ? d.name : ''
|
||||
}
|
||||
}
|
||||
return {
|
||||
...tmpOptions,
|
||||
label
|
||||
}
|
||||
}
|
||||
|
||||
protected configTooltip(chart: Chart, options: CirclePackingOptions): CirclePackingOptions {
|
||||
const temOptions = super.configTooltip(chart, options)
|
||||
if (!temOptions.tooltip) {
|
||||
return temOptions
|
||||
}
|
||||
const tooltipAttr = parseJson(chart.customAttr).tooltip
|
||||
return {
|
||||
...temOptions,
|
||||
tooltip: {
|
||||
...temOptions,
|
||||
fields: ['name', 'value'],
|
||||
formatter: d => {
|
||||
let value = d.value
|
||||
if (tooltipAttr.tooltipFormatter) {
|
||||
value = valueFormatter(value, tooltipAttr.tooltipFormatter)
|
||||
}
|
||||
return { name: d.name, value }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
configEmptyDataStrategy(chart: Chart, options: CirclePackingOptions): CirclePackingOptions {
|
||||
const { functionCfg } = parseJson(chart.senior)
|
||||
const emptyDataStrategy = functionCfg.emptyDataStrategy
|
||||
const setChildren = children => {
|
||||
if (emptyDataStrategy === 'ignoreData') {
|
||||
for (let i = children.length - 1; i >= 0; i--) {
|
||||
let isNotNullChildren = []
|
||||
if (children[i].children?.length) {
|
||||
isNotNullChildren = children[i].children.filter(item => item.value !== null)
|
||||
}
|
||||
if (children[i].children?.length && isNotNullChildren.length) {
|
||||
setChildren(children[i].children)
|
||||
}
|
||||
if (children[i]?.hasOwnProperty('value') && children[i].value === null) {
|
||||
children.splice(i, 1)
|
||||
}
|
||||
if (!children[i]?.hasOwnProperty('value') && isNotNullChildren.length === 0) {
|
||||
children.splice(i, 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = children.length - 1; i >= 0; i--) {
|
||||
let isNotNullChildren = []
|
||||
if (children[i].children?.length) {
|
||||
isNotNullChildren = children[i].children.filter(item => item.value !== null)
|
||||
if (!isNotNullChildren.length) {
|
||||
children[i].children = []
|
||||
continue
|
||||
}
|
||||
}
|
||||
setChildren(children[i].children)
|
||||
}
|
||||
}
|
||||
}
|
||||
const data = cloneDeep(options.data.children)
|
||||
setChildren(data)
|
||||
options.data.children = data
|
||||
return options
|
||||
}
|
||||
setupDefaultOptions(chart: ChartObj): ChartObj {
|
||||
const { customAttr, customStyle, senior } = chart
|
||||
const { label, basicStyle } = customAttr
|
||||
const { legend } = customStyle
|
||||
senior.functionCfg.emptyDataStrategy = 'ignoreData'
|
||||
customAttr.label = {
|
||||
...label,
|
||||
show: true
|
||||
}
|
||||
legend.show = false
|
||||
basicStyle.circleBorderWidth = 0
|
||||
basicStyle.circleBorderColor = '#fff'
|
||||
basicStyle.circlePadding = 0
|
||||
return chart
|
||||
}
|
||||
protected setupOptions(chart: Chart, options: CirclePackingOptions): CirclePackingOptions {
|
||||
return flow(
|
||||
this.configTheme,
|
||||
this.configEmptyDataStrategy,
|
||||
this.configBasicStyle,
|
||||
this.configLabel,
|
||||
this.configTooltip,
|
||||
this.configLegend
|
||||
)(chart, options)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super('circle-packing', DEFAULT_DATA)
|
||||
}
|
||||
}
|
@ -185,7 +185,6 @@ export function getLabel(chart: Chart) {
|
||||
layout.push({ type: 'hide-overlap' })
|
||||
} else {
|
||||
layout.push({ type: 'limit-in-plot' })
|
||||
layout.push({ type: 'fixed-overlap' })
|
||||
layout.push({ type: 'hide-overlap' })
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ import { deepCopy, isMobile } from '@/utils/utils'
|
||||
import { isDashboard, trackBarStyleCheck } from '@/utils/canvasUtils'
|
||||
import { useEmitt } from '@/hooks/web/useEmitt'
|
||||
import { L7ChartView } from '@/views/chart/components/js/panel/types/impl/l7'
|
||||
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
const { t } = useI18n()
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, embeddedCallBack, inMobile } =
|
||||
storeToRefs(dvMainStore)
|
||||
@ -86,6 +87,7 @@ const emit = defineEmits([
|
||||
|
||||
const g2TypeSeries1 = ['bidirectional-bar']
|
||||
const g2TypeSeries0 = ['bar-range']
|
||||
const g2TypeTree = ['circle-packing']
|
||||
|
||||
const { view, showPosition, scale, terminal, suffixId } = toRefs(props)
|
||||
|
||||
@ -165,6 +167,14 @@ const checkSelected = param => {
|
||||
return state.linkageActiveParam.name === param.field
|
||||
} else if (g2TypeSeries0.includes(view.value.type)) {
|
||||
return state.linkageActiveParam.category === param.category
|
||||
} else if (g2TypeTree.includes(view.value.type)) {
|
||||
if (
|
||||
param.path?.startsWith(state.linkageActiveParam.name) ||
|
||||
state.linkageActiveParam.name === t('commons.all')
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return state.linkageActiveParam.name === param.name
|
||||
} else {
|
||||
return (
|
||||
(state.linkageActiveParam.name === param.name ||
|
||||
@ -444,7 +454,7 @@ const trackClick = trackAction => {
|
||||
}
|
||||
}
|
||||
let quotaList = state.pointParam.data.quotaList
|
||||
if (curView.type === 'bar-range') {
|
||||
if (['bar-range', 'circle-packing'].includes(curView.type)) {
|
||||
quotaList = state.pointParam.data.dimensionList
|
||||
} else {
|
||||
quotaList[0]['value'] = state.pointParam.data.value
|
||||
|
@ -352,6 +352,13 @@ const chartClick = param => {
|
||||
ElMessage.error(t('chart.drill_field_error'))
|
||||
return
|
||||
}
|
||||
if (
|
||||
view.value.type === 'circle-packing' &&
|
||||
(param.data?.childNodeCount === 0 || param.data.name === t('commons.all'))
|
||||
) {
|
||||
ElMessage.error(t('chart.last_layer'))
|
||||
return
|
||||
}
|
||||
if (state.drillClickDimensionList.length < props.view.drillFields.length - 1) {
|
||||
state.drillClickDimensionList.push({
|
||||
dimensionList: param.data.dimensionList,
|
||||
|
Loading…
Reference in New Issue
Block a user