feat(图表): 指标卡支持联动并支持在嵌入时将过滤参数向外传递

This commit is contained in:
wangjiahao
2025-03-19 18:33:53 +08:00
committed by xuwei-fit2cloud
parent b2bbada221
commit eaa45752eb
7 changed files with 209 additions and 14 deletions

View File

@@ -158,7 +158,23 @@
</el-col>
<el-col :span="16" class="preview-show">
<el-row class="content-head">{{ t('visualization.linkage_setting_tips1') }}</el-row>
<el-row v-if="state.linkageInfo && state.linkageInfo.linkageActive">
<el-row
v-if="
state.linkageInfo &&
state.linkageInfo.linkageActive &&
curComponent?.innerType === 'indicator'
"
style="height: 100%"
class="custom-position"
>
<Icon name="dv-empty"
><dvEmpty style="width: 125px; height: 125px" class="svg-icon"
/></Icon>
<span style="margin-top: 8px; font-size: 14px">
{{ t('visualization.indicator_linkage') }}</span
>
</el-row>
<el-row v-else-if="state.linkageInfo && state.linkageInfo.linkageActive">
<el-row style="margin-top: 5px">
<div style="display: flex" class="inner-content">
<div style="flex: 1">{{ t('visualization.current_chart_source_field') }}</div>
@@ -367,7 +383,10 @@ const sameDsShow = computed(
)
const diffDsShow = computed(
() => curLinkageTargetViewsInfoDiffDs.value && curLinkageTargetViewsInfoDiffDs.value.length > 0
() =>
curLinkageTargetViewsInfoDiffDs.value &&
curLinkageTargetViewsInfoDiffDs.value.length > 0 &&
curComponent.value.innerType !== 'indicator'
)
const dialogInit = viewItem => {
@@ -557,6 +576,9 @@ const linkageFieldAdaptor = async data => {
JSON.stringify(state.curLinkageViewInfo.extStack) +
(state.curLinkageViewInfo.type.includes('chart-mix')
? JSON.stringify(state.curLinkageViewInfo.extBubble)
: '') +
(['indicator'].includes(state.curLinkageViewInfo.type)
? JSON.stringify(state.curLinkageViewInfo.yAxis)
: '')
const targetCheckAllAxisStr =
JSON.stringify(targetChartDetails.xAxis) +
@@ -564,6 +586,9 @@ const linkageFieldAdaptor = async data => {
JSON.stringify(state.curLinkageViewInfo.extStack) +
(targetChartDetails.type.includes('chart-mix')
? JSON.stringify(targetChartDetails.extBubble)
: '') +
(['indicator'].includes(state.curLinkageViewInfo.type)
? JSON.stringify(state.curLinkageViewInfo.yAxis)
: '')
state.sourceLinkageInfo.targetViewFields.forEach(item => {
if (
@@ -592,7 +617,7 @@ const sourceLinkageInfoFilter = computed(() => {
(state.curLinkageViewInfo.type.includes('chart-mix')
? JSON.stringify(state.curLinkageViewInfo.extBubble)
: '') +
(state.curLinkageViewInfo.type.includes('table-normal')
(['table-normal', 'indicator'].includes(state.curLinkageViewInfo.type)
? JSON.stringify(state.curLinkageViewInfo.yAxis)
: '')
return state.sourceLinkageInfo.targetViewFields.filter(item =>

View File

@@ -3,7 +3,7 @@ import { getData } from '@/api/chart'
import { ref, reactive, shallowRef, computed, CSSProperties, toRefs, PropType } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { customAttrTrans, customStyleTrans, recursionTransObj } from '@/utils/canvasStyle'
import { deepCopy } from '@/utils/utils'
import { deepCopy, isMobile } from '@/utils/utils'
import { cloneDeep, defaultsDeep, defaultTo } from 'lodash-es'
import {
BASE_VIEW_CONFIG,
@@ -13,8 +13,18 @@ import {
} from '@/views/chart/components/editor/util/chart'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import { storeToRefs } from 'pinia'
import { isDashboard, trackBarStyleCheck } from '@/utils/canvasUtils'
import ViewTrackBar from '@/components/visualization/ViewTrackBar.vue'
const props = defineProps({
element: {
type: Object,
default() {
return {
propValue: null
}
}
},
view: {
type: Object as PropType<ChartObj>,
default() {
@@ -50,18 +60,25 @@ const props = defineProps({
}
})
const { view, scale, terminal } = toRefs(props)
const { view, scale, terminal, showPosition } = toRefs(props)
const dvMainStore = dvMainStoreWithOut()
const { batchOptStatus } = storeToRefs(dvMainStore)
const dataVMobile = !isDashboard() && isMobile()
const { embeddedCallBack, nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, inMobile } =
storeToRefs(dvMainStore)
const viewTrack = ref(null)
const errMsg = ref('')
const isError = ref(false)
const state = reactive({
pointParam: null,
data: null,
loading: false,
totalItems: 0
totalItems: 0,
trackBarStyle: {
position: 'absolute',
left: '50px',
top: '50px'
}
})
const chartData = shallowRef<Partial<Chart['data']>>({
@@ -101,7 +118,6 @@ const result = computed(() => {
})
const indicatorColor = ref(DEFAULT_INDICATOR_STYLE.color)
const thresholdColor = computed(() => {
let color: string = indicatorColor.value
let backgroundColor: string = DEFAULT_INDICATOR_STYLE.backgroundColor
@@ -184,8 +200,7 @@ const formattedResult = computed(() => {
return _result
})
const emit = defineEmits(['onChartClick', 'onDrillFilters', 'onJumpClick'])
const emit = defineEmits(['onPointClick', 'onChartClick', 'onDrillFilters', 'onJumpClick'])
const contentStyle = ref<CSSProperties>({
display: 'flex',
'flex-direction': 'column',
@@ -375,6 +390,143 @@ const calcData = (view, callback) => {
}
}
const trackClick = trackAction => {
const param = state.pointParam
if (!param?.data?.dimensionList) {
return
}
const linkageParam = {
option: 'linkage',
name: state.pointParam.data.name,
viewId: view.value.id,
dimensionList: state.pointParam.data.dimensionList,
quotaList: state.pointParam.data.quotaList
}
const jumpParam = {
option: 'jump',
name: state.pointParam.data.name,
viewId: view.value.id,
dimensionList: state.pointParam.data.dimensionList,
quotaList: state.pointParam.data.quotaList,
sourceType: state.pointParam.data.sourceType
}
const clickParams = {
option: 'pointClick',
name: state.pointParam.data.name,
viewId: view.value.id,
dimensionList: state.pointParam.data.dimensionList,
quotaList: state.pointParam.data.quotaList
}
switch (trackAction) {
case 'pointClick':
emit('onPointClick', clickParams)
break
case 'linkageAndDrill':
dvMainStore.addViewTrackFilter(linkageParam)
emit('onChartClick', param)
break
case 'drill':
emit('onChartClick', param)
break
case 'linkage':
dvMainStore.addViewTrackFilter(linkageParam)
break
case 'jump':
if (mobileInPc.value && !inMobile.value) return
emit('onJumpClick', jumpParam)
break
default:
break
}
}
const trackMenu = computed(() => {
let trackMenuInfo = []
if (showPosition.value === 'viewDialog') {
return trackMenuInfo
}
let linkageCount = 0
let jumpCount = 0
chartData.value?.fields?.forEach(item => {
const sourceInfo = view.value.id + '#' + item.id
if (nowPanelTrackInfo.value[sourceInfo]) {
linkageCount++
}
if (nowPanelJumpInfo.value[sourceInfo]) {
jumpCount++
}
})
jumpCount &&
view.value?.jumpActive &&
(!mobileInPc.value || inMobile.value) &&
trackMenuInfo.push('jump')
linkageCount && view.value?.linkageActive && trackMenuInfo.push('linkage')
view.value.drillFields.length && trackMenuInfo.push('drill')
// 如果同时配置jump linkage drill 切配置联动时同时下钻 在实际只显示两个 '跳转' '联动和下钻'
if (trackMenuInfo.length === 3 && props.element.actionSelection.linkageActive === 'auto') {
trackMenuInfo = ['jump', 'linkageAndDrill']
} else if (
trackMenuInfo.length === 2 &&
props.element.actionSelection.linkageActive === 'auto' &&
!trackMenuInfo.includes('jump')
) {
trackMenuInfo = ['linkageAndDrill']
}
return trackMenuInfo
})
const pointClickTrans = () => {
if (embeddedCallBack.value === 'yes') {
trackClick('pointClick')
}
}
const action = param => {
state.pointParam = param
// 点击
pointClickTrans()
// 联动 跳转
if (trackMenu.value.length < 2) {
// 只有一个事件直接调用
trackClick(trackMenu.value[0])
} else {
// 图表关联多个事件
const barStyleTemp = {
left: param.x - 50,
top: param.y + 10
}
trackBarStyleCheck(props.element, barStyleTemp, props.scale, trackMenu.value.length)
if (dataVMobile) {
state.trackBarStyle.left = barStyleTemp.left + 40 + 'px'
state.trackBarStyle.top = barStyleTemp.top + 70 + 'px'
} else {
state.trackBarStyle.left = barStyleTemp.left + 'px'
state.trackBarStyle.top = barStyleTemp.top + 'px'
}
viewTrack.value.trackButtonClick()
}
}
const onPointClick = () => {
if (view.value?.yAxis?.length) {
const axis = view.value.yAxis[0]
// 模拟点击
const params = {
data: {
data: {
name: axis.name,
dimensionList: [],
quotaList: view.value.yAxis
}
}
}
action(params)
}
}
defineExpose({
calcData,
renderChart
@@ -382,7 +534,16 @@ defineExpose({
</script>
<template>
<div :style="contentStyle">
<div :style="contentStyle" @click="onPointClick">
<view-track-bar
ref="viewTrack"
:track-menu="trackMenu"
:font-family="fontFamily"
class="track-bar"
:style="state.trackBarStyle"
@trackClick="trackClick"
:is-data-v-mobile="dataVMobile"
/>
<div>
<span :style="indicatorClass">{{ formattedResult }}</span>
<span :style="indicatorSuffixClass" v-if="showSuffix">{{ suffixContent }}</span>

View File

@@ -2868,6 +2868,7 @@ export default {
column_name: 'Field name'
},
visualization: {
indicator_linkage: 'Indicator card linkage only carries chart filtering parameters',
gap_size: 'Gap Size',
small: 'Small',
middle: 'Medium',

View File

@@ -2789,6 +2789,7 @@ export default {
column_name: '欄位名稱'
},
visualization: {
indicator_linkage: '指標卡聯動僅攜帶圖表過濾參數',
gap_size: '間隙大小',
small: '小',
middle: '中',

View File

@@ -2795,6 +2795,7 @@ export default {
column_name: '字段名称'
},
visualization: {
indicator_linkage: '指标卡联动仅携带图表过滤参数',
gap_size: '间隙大小',
small: '小',
middle: '中',

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

@@ -1172,10 +1172,15 @@ const clearG2Tooltip = () => {
:themes="canvasStyleData.dashboard.themeColor"
ref="chartComponent"
:view="view"
:element="element"
:show-position="showPosition"
:suffixId="suffixId"
:font-family="fontFamily"
@touchstart="clearG2Tooltip"
@onChartClick="chartClick"
@onPointClick="onPointClick"
@onDrillFilters="onDrillFilters"
@onJumpClick="jumpClick"
/>
<chart-component-g2-plot
:scale="scale"