refactor(数据大屏): 优化外部参数,解决外部参数对所有图表兼容性问题

This commit is contained in:
wangjiahao 2024-03-14 17:47:22 +08:00
parent 24982a350a
commit e9deb711a1
14 changed files with 462 additions and 230 deletions

View File

@ -2,6 +2,7 @@ package io.dataease.visualization.dao.ext.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.dataease.api.visualization.dto.VisualizationViewTableDTO;
import io.dataease.api.visualization.vo.DataVisualizationBaseVO;
import io.dataease.api.visualization.vo.DataVisualizationVO;
import io.dataease.api.visualization.vo.VisualizationResourceVO;
@ -45,4 +46,7 @@ public interface ExtDataVisualizationMapper {
void copyLinkageField(@Param("copyId") Long copyId);
List<VisualizationViewTableDTO> getVisualizationViewDetails(@Param("dvId") Long dvId);
}

View File

@ -6,6 +6,7 @@ import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.template.dto.TemplateManageFileDTO;
import io.dataease.api.template.dto.VisualizationTemplateExtendDataDTO;
import io.dataease.api.visualization.DataVisualizationApi;
import io.dataease.api.visualization.dto.VisualizationViewTableDTO;
import io.dataease.api.visualization.request.DataVisualizationBaseRequest;
import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest;
import io.dataease.api.visualization.vo.DataVisualizationVO;
@ -354,6 +355,11 @@ public class DataVisualizationServer implements DataVisualizationApi {
return null;
}
@Override
public List<VisualizationViewTableDTO> detailList(Long dvId) {
return extDataVisualizationMapper.getVisualizationViewDetails(dvId);
}
@Override
public void nameCheck(DataVisualizationBaseRequest request) {

View File

@ -41,7 +41,8 @@
`copy_id`)
SELECT core_chart_view.`id` + #{copyId} as id,
`title`,
#{newDvId} as scene_id, `table_id`,
#{newDvId} as scene_id,
`table_id`,
`type`,
`render`,
`result_count`,
@ -73,7 +74,8 @@
`refresh_time`,
`linkage_active`,
`jump_active`,
core_chart_view.`id` as copy_from, #{copyId} as copy_id
core_chart_view.`id` as copy_from,
#{copyId} as copy_id
FROM core_chart_view
WHERE core_chart_view.scene_id = #{sourceDvId}
</insert>
@ -160,65 +162,58 @@
</select>
<select id="findRecent" resultType="io.dataease.visualization.dao.ext.po.VisualizationResourcePO">
SELECT
dvResource.id,
dvResource.resource_id,
dvResource.name,
dvResource.type,
dvResource.creator,
core_opt_recent.uid AS last_editor,
core_opt_recent.time AS last_edit_time,
(
CASE
SELECT dvResource.id,
dvResource.resource_id,
dvResource.name,
dvResource.type,
dvResource.creator,
core_opt_recent.uid AS last_editor,
core_opt_recent.time AS last_edit_time,
(
CASE
WHEN core_store.resource_id IS NULL THEN
0 ELSE 1
END
) AS favorite
FROM
(
SELECT
core_dataset_group.id,
core_dataset_group.id AS resource_id,
core_dataset_group.NAME,
'dataset' AS type,
core_dataset_group.create_by AS creator
FROM
core_dataset_group
WHERE
core_dataset_group.node_type = 'dataset' UNION ALL
SELECT
core_datasource.id,
core_datasource.id AS resource_id,
core_datasource.NAME,
'datasource' AS type,
core_datasource.create_by AS creator
FROM
core_datasource
WHERE
core_datasource.type != 'folder' UNION ALL
SELECT
data_visualization_info.id,
data_visualization_info.id AS resource_id,
data_visualization_info.NAME,
(
CASE
data_visualization_info.type
WHEN 'dataV' THEN
'screen' ELSE 'panel'
END
) AS type,
data_visualization_info.create_by AS creator
FROM
data_visualization_info
WHERE
data_visualization_info.delete_flag = 0
AND node_type = 'leaf'
) dvResource
LEFT JOIN core_store ON dvResource.id = core_store.resource_id
AND core_store.uid = #{uid}
INNER JOIN core_opt_recent ON dvResource.resource_id = core_opt_recent.resource_id
AND core_opt_recent.uid = #{uid} ${ew.customSqlSegment}
WHEN core_store.resource_id IS NULL THEN
0
ELSE 1
END
) AS favorite
FROM (SELECT core_dataset_group.id,
core_dataset_group.id AS resource_id,
core_dataset_group.NAME,
'dataset' AS type,
core_dataset_group.create_by AS creator
FROM core_dataset_group
WHERE core_dataset_group.node_type = 'dataset'
UNION ALL
SELECT core_datasource.id,
core_datasource.id AS resource_id,
core_datasource.NAME,
'datasource' AS type,
core_datasource.create_by AS creator
FROM core_datasource
WHERE core_datasource.type != 'folder'
UNION ALL
SELECT
data_visualization_info.id,
data_visualization_info.id AS resource_id,
data_visualization_info.NAME,
(
CASE
data_visualization_info.type
WHEN 'dataV' THEN
'screen' ELSE 'panel'
END
) AS type,
data_visualization_info.create_by AS creator
FROM
data_visualization_info
WHERE
data_visualization_info.delete_flag = 0
AND node_type = 'leaf') dvResource
LEFT JOIN core_store ON dvResource.id = core_store.resource_id
AND core_store.uid = #{uid}
INNER JOIN core_opt_recent ON dvResource.resource_id = core_opt_recent.resource_id
AND core_opt_recent.uid = #{uid} ${ew.customSqlSegment}
</select>
<insert id="copyLinkJump">
@ -270,7 +265,7 @@
visualization_link_jump_info.`checked`,
visualization_link_jump_info.`attach_params`,
visualization_link_jump_info.`id` AS copy_from,
#{copyId} AS copy_id
#{copyId} AS copy_id
FROM visualization_link_jump_info
INNER JOIN (SELECT id AS t_id,
copy_from AS s_id
@ -294,7 +289,7 @@
visualization_link_jump_target_view_info.`target_view_id`,
visualization_link_jump_target_view_info.`target_field_id`,
visualization_link_jump_target_view_info.`target_id` AS copy_from,
#{copyId} AS copy_id
#{copyId} AS copy_id
FROM visualization_link_jump_target_view_info
INNER JOIN (SELECT id AS t_id,
copy_from AS s_id
@ -370,4 +365,36 @@
WHERE copy_id = #{copyId}) pvlf_copy
ON visualization_linkage_field.linkage_id = pvlf_copy.s_id
</insert>
<resultMap id="ViewDetailsMap" type="io.dataease.api.visualization.dto.VisualizationViewTableDTO">
<result column="id" property="id"/>
<result column="title" property="title"/>
<result column="scene_id" property="sceneId"/>
<result column="table_id" property="tableId"/>
<result column="type" property="type"/>
<result column="render" property="render"/>
<collection property="tableFields" ofType="io.dataease.dto.dataset.DatasetTableFieldDTO">
<result column="field_id" jdbcType="VARCHAR" property="id"/>
<result column="origin_name" jdbcType="VARCHAR" property="originName"/>
<result column="field_name" jdbcType="VARCHAR" property="name"/>
<result column="field_type" jdbcType="VARCHAR" property="type"/>
<result column="de_type" jdbcType="VARCHAR" property="deType"/>
</collection>
</resultMap>
<select id="getVisualizationViewDetails" resultMap="ViewDetailsMap">
SELECT core_chart_view.id,
core_chart_view.title,
core_chart_view.scene_id,
core_chart_view.table_id,
core_chart_view.`type`,
core_chart_view.render,
core_chart_view.scene_id as 'visualization_id', core_dataset_table_field.id AS 'field_id', core_dataset_table_field.origin_name,
core_dataset_table_field.`name` AS 'field_name', core_dataset_table_field.type AS 'field_type', core_dataset_table_field.de_type
FROM core_chart_view
LEFT JOIN core_dataset_table_field
ON core_chart_view.table_id = core_dataset_table_field.dataset_group_id
WHERE core_chart_view.scene_id = #{dvId}
AND core_chart_view.id IS NOT NULL
</select>
</mapper>

View File

@ -61,14 +61,14 @@
</select>
<select id="queryWithVisualizationId" resultMap="BaseResultMapDTO">
SELECT
#{visualizationId} as visualization_id,
ifnull( visualization_outer_params.checked, 0 ) AS checked
FROM
visualization_group
LEFT JOIN visualization_outer_params ON visualization_group.id = visualization_outer_params.visualization_id
WHERE
visualization_group.id = #{visualizationId}
SELECT
#{visualizationId} as visualization_id,
ifnull( visualization_outer_params.checked, 0 ) AS checked
FROM
data_visualization_info
LEFT JOIN visualization_outer_params ON data_visualization_info.id = visualization_outer_params.visualization_id
WHERE
data_visualization_info.id = #{visualizationId}
</select>
<delete id="deleteOuterParamsTargetWithVisualizationId" >

View File

@ -74,9 +74,9 @@ export const storeStatusApi = (id: string): Promise<IResponse> => {
export const decompression = data => request.post({ url: '/dataVisualization/decompression', data })
export const detailList = dvId => {
export const viewDetailList = dvId => {
return request.get({
url: '/dataVisualization/view/detailList/' + dvId,
url: '/dataVisualization/viewDetailList/' + dvId,
method: 'get',
loading: false
})

View File

@ -1,8 +1,8 @@
import request from '@/config/axios'
export function queryWithDvId(dvId) {
export function queryWithVisualizationId(dvId) {
return request.get({
url: '/outerParams/queryWithDvId/' + dvId
url: '/outerParams/queryWithVisualizationId/' + dvId
})
}

View File

@ -434,6 +434,13 @@ const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
<div class="right-area" v-if="!batchOptStatus && !linkageSettingStatus">
<template v-if="editMode !== 'preview'">
<el-tooltip effect="dark" content="外部参数设置" placement="bottom">
<component-button
tips="外部参数设置"
@custom-click="openOuterParamsSet"
icon-name="icon_params_setting"
/>
</el-tooltip>
<el-tooltip effect="dark" content="批量操作" placement="bottom">
<component-button
tips="批量操作"

View File

@ -1,33 +1,27 @@
<template>
<el-dialog
class="params-class"
width="900px"
:append-to-body="true"
title="外部参数设置"
v-model="state.outerParamsSetVisible"
@submit.prevent
width="70vw"
top="10vh"
trigger="click"
>
<el-row style="height: 430px">
<el-row>
<span style="margin-right: 20px; font-weight: 600">{{
t('visualization.outer_param_set')
}}</span>
<el-checkbox v-model="state.outerParams.checked">{{
t('visualization.enable_outer_param_set')
}}</el-checkbox>
</el-row>
<el-row style="height: 550px">
<el-row v-loading="state.loading">
<el-row class="preview">
<el-col :span="8" style="height: 100%; overflow-y: hidden">
<el-row class="tree-head">
<span style="float: left; margin-left: 30px">{{
t('visualization.param_name')
}}</span>
<span style="float: right; margin-right: 10px">{{
t('visualization.enable_param')
}}</span>
<span class="head-text">参数列表</span>
<span class="head-filter">
<el-button type="primary" icon="Plus" text @click="addOuterParamsInfo"> </el-button>
</span>
</el-row>
<el-row class="tree-content">
<el-tree
class="custom-tree"
menu
ref="outerParamsInfoTree"
:data="state.outerParamsInfoArray"
node-key="id"
@ -35,72 +29,70 @@
:props="state.treeProp"
@node-click="nodeClick"
>
<template v-slot="{ node, data }">
<template #default="{ node, data }">
<span class="custom-tree-node">
<span>
<span style="margin-left: 6px"
><el-input
v-model="data.paramName"
size="mini"
:placeholder="t('visualization.input_param_name')"
/></span>
</span>
<span @click.stop>
<div>
<div @click.stop>
<span class="auth-span">
<el-checkbox
v-model="data.checked"
style="margin-right: 10px"
@change="sourceFieldCheckedChange(data)"
/>
<el-button
icon="el-icon-delete"
type="text"
size="small"
@click="removeOuterParamsInfo(node, data)"
/>
</span>
</div>
</span>
<span :id="'paramName-' + data.paramsInfoId">
<el-input
v-if="curEditDataId === data.paramsInfoId"
v-model="data.paramName"
size="mini"
:placeholder="$t('visualization.input_param_name')"
@blur="closeEdit"
/>
<span class="tree-select-field" v-else-if="data.paramName">
{{ data.paramName }}
</span>
<span class="tree-select-field" v-else> 未配置参数名 </span>
</span>
<span class="icon-more">
<handle-more
style="margin-right: 10px"
@handle-command="cmd => outerParamsOperation(cmd, node, data)"
:menu-list="state.optMenu"
icon-name="icon_more_outlined"
placement="bottom-start"
></handle-more>
</span>
</span>
</template>
</el-tree>
</el-row>
<el-row class="tree-bottom">
<el-button
size="mini"
type="success"
icon="el-icon-plus"
round
@click="addOuterParamsInfo"
>{{ t('visualization.add_param') }}
</el-button>
</el-row>
</el-col>
<el-col :span="16" class="preview-show">
<el-row v-if="state.outerParamsInfo">
<el-row class="top_border">
<el-row style="margin-top: 10px">
<el-col :span="11">
<div class="ellip">{{ t('visualization.link_component') }}</div>
</el-col>
<el-col :span="11">
<div class="ellip">{{ t('visualization.link_component_field') }}</div>
</el-col>
</el-row>
<el-row style="height: 266px; overflow-y: auto">
<el-row
<el-row v-if="state.curNodeId">
<el-row style="margin-top: 5px">
<div style="display: flex" class="inner-content">
<div style="flex: 1">{{ t('visualization.link_view') }}</div>
<div style="width: 36px"></div>
<div style="flex: 1">
{{ t('visualization.link_view_field') }}
</div>
<div style="width: 32px"></div>
</div>
<div style="width: 100%; max-height: 350px; overflow-y: auto">
<div
style="display: flex; padding: 0 16px 8px"
v-for="(targetViewInfo, index) in state.outerParamsInfo.targetViewInfoList"
:key="index"
>
<el-col :span="11">
<div style="flex: 1">
<div class="select-filed">
<el-select
v-model="targetViewInfo.targetViewId"
filterable
style="width: 100%"
size="mini"
:placeholder="t('fu.search_bar.please_select')"
:placeholder="t('visualization.please_select')"
@change="viewInfoOnChange(targetViewInfo)"
>
<el-option
@ -110,15 +102,23 @@
curItem.id === targetViewInfo.targetViewId
)"
:key="item.id"
:label="item.name"
:label="item.title"
:value="item.id"
>
<span style="float: left; font-size: 12px"> {{ item.name }}</span>
<Icon
class-name="view-type-icon"
style="margin-right: 4px"
:name="item.type"
/>
<span style="font-size: 12px"> {{ item.title }}</span>
</el-option>
</el-select>
</div>
</el-col>
<el-col :span="11">
</div>
<el-icon class="link-icon-join">
<Icon style="width: 20px; height: 20px" name="dv-link-target" />
</el-icon>
<div style="flex: 1">
<div class="select-filed">
<el-select
v-model="targetViewInfo.targetFieldId"
@ -126,7 +126,7 @@
:disabled="fieldIdDisabledCheck(targetViewInfo)"
style="width: 100%"
size="mini"
:placeholder="t('fu.search_bar.please_select')"
:placeholder="t('visualization.please_select')"
>
<el-option
v-for="viewField in getFieldArray(targetViewInfo.targetViewId)"
@ -134,70 +134,34 @@
:label="viewField.name"
:value="viewField.id"
>
<span style="float: left">
<svg-icon
v-if="viewField.deType === 0"
icon-class="field_text"
class="field-icon-text"
/>
<svg-icon
v-if="viewField.deType === 1"
icon-class="field_time"
class="field-icon-time"
/>
<svg-icon
v-if="viewField.deType === 2 || viewField.deType === 3"
icon-class="field_value"
class="field-icon-value"
/>
<svg-icon
v-if="viewField.deType === 5"
icon-class="field_location"
class="field-icon-location"
/>
</span>
<span style="float: left; font-size: 12px">{{ viewField.name }}</span>
<Icon
style="width: 14px; height: 14px"
:name="`field_${fieldType[viewField.deType]}`"
:className="`field-icon-${fieldType[viewField.deType]}`"
/>
<span style="font-size: 12px">{{ viewField.name }}</span>
</el-option>
</el-select>
</div>
</el-col>
<el-col :span="2">
<div>
<el-button
icon="el-icon-delete"
type="text"
size="small"
style="float: left"
@click="deleteOuterParamsField(index)"
/>
</div>
</el-col>
</el-row>
</el-row>
</div>
<el-button class="m-del-icon-btn" text @click="deleteOuterParamsField(index)">
<el-icon size="20px">
<Icon name="icon_delete-trash_outlined" />
</el-icon>
</el-button>
</div>
</div>
<el-row class="bottom">
<el-button
size="mini"
type="success"
icon="el-icon-plus"
round
@click="addOuterParamsField"
>{{ t('visualization.add_param_link_field') }}
<el-row style="width: 100%; padding-left: 16px">
<el-button type="primary" icon="Plus" text @click="addOuterParamsField">
{{ t('visualization.add_param_link_field') }}
</el-button>
</el-row>
</el-row>
<el-row v-if="state.outerParamsInfo.linkType === 'outer'" style="height: 300px">
<el-input
v-model="state.outerParamsInfo.content"
:autosize="{ minRows: 14 }"
type="textarea"
:placeholder="t('visualization.input_jump_link')"
/>
</el-row>
</el-row>
<el-row v-else style="height: 100%" class="custom-position">
{{ t('visualization.select_param') }}
</el-row>
<div v-else class="empty">
<empty-background description="请配置参数" img-type="noneWhite" />
</div>
</el-col>
</el-row>
</el-row>
@ -219,18 +183,34 @@ import { ElMessage } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
import { deepCopy } from '@/utils/utils'
import generateID from '@/utils/generateID'
import { queryWithDvId, updateOuterParamsSet } from '@/api/visualization/outerParams'
import { detailList } from '@/api/visualization/dataVisualization'
import { queryWithVisualizationId, updateOuterParamsSet } from '@/api/visualization/outerParams'
import { viewDetailList } from '@/api/visualization/dataVisualization'
import checkArrayRepeat from '@/utils/check'
import HandleMore from '@/components/handle-more/src/HandleMore.vue'
import { fieldType } from '@/utils/attr'
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
const dvMainStore = dvMainStoreWithOut()
const { dvInfo, componentData, canvasStyleData } = storeToRefs(dvMainStore)
const outerParamsInfoTree = ref(null)
const emits = defineEmits(['outerParamsSetVisibleChange'])
const { t } = useI18n()
const curEditDataId = ref(null)
const state = reactive({
loading: false,
outerParamsSetVisible: false,
optMenu: [
{
label: '重命名',
svgName: 'edit',
command: 'rename'
},
{
label: '删除',
svgName: 'delete',
command: 'delete'
}
],
treeProp: {
id: 'paramsInfoId',
label: 'paramName',
@ -240,9 +220,10 @@ const state = reactive({
checked: false,
outerParamsInfoArray: []
},
outerParamsInfoArray: null,
outerParamsInfoArray: [],
mapOuterParamsInfoArray: {},
panelList: [],
curNodeId: null,
outerParamsInfo: {
content: '',
linkType: '',
@ -280,6 +261,18 @@ const viewSelectedField = computed(() =>
state.outerParamsInfo?.targetViewInfoList?.map(targetViewInfo => targetViewInfo.targetViewId)
)
const closeEdit = () => {
curEditDataId.value = null
}
const outerParamsOperation = (cmd, node, data) => {
if (cmd === 'rename') {
curEditDataId.value = data.paramsInfoId
} else if (cmd === 'delete') {
removeOuterParamsInfo(node, data)
}
}
const fieldIdDisabledCheck = targetViewInfo => {
return (
state.viewIdFieldArrayMap[targetViewInfo.targetViewId] &&
@ -292,27 +285,28 @@ const getFieldArray = id => {
return state.viewIdFieldArrayMap[id]
}
const init = () => {
//
queryWithDvId(dvInfo['id']).then(rsp => {
const initParams = () => {
//
queryWithVisualizationId(dvInfo.value.id).then(rsp => {
state.outerParams = rsp.data
state.outerParamsInfoArray = state.outerParams?.outerParamsInfoArray
if (state.outerParamsInfoArray.length > 0) {
if (state.outerParamsInfoArray.length >= 1) {
state.outerParamsInfoArray.forEach(outerParamsInfo => {
state.mapOuterParamsInfoArray[outerParamsInfo.paramsInfoId] = outerParamsInfo
})
const firstNode = state.outerParamsInfoArray[0]
state.curNodeId = null
nextTick(() => {
outerParamsInfoTree.value.setCurrentKey(firstNode.paramsInfoId)
nodeClick(firstNode)
// outerParamsInfoTree.value.setCurrentKey(firstNode.paramsInfoId)
// nodeClick(firstNode)
})
}
})
getPanelViewList(dvInfo['id'])
getPanelViewList(dvInfo.value.id)
}
const cancel = () => {
emits('outerParamsSetVisibleChange', false)
state.outerParamsSetVisible = false
}
const save = () => {
@ -335,11 +329,12 @@ const save = () => {
const nodeClick = data => {
state.outerParamsInfo = state.mapOuterParamsInfoArray[data.paramsInfoId]
state.curNodeId = data.paramsInfoId
}
//
const getPanelViewList = dvId => {
detailList(dvId).then(rsp => {
viewDetailList(dvId).then(rsp => {
state.viewIdFieldArrayMap = {}
state.currentLinkPanelViewArray = rsp.data
if (state.currentLinkPanelViewArray) {
@ -416,11 +411,12 @@ const sourceFieldCheckedChange = data => {
}
const addOuterParamsInfo = () => {
outerParamsInfoTree.value.checked = true
state.outerParams.checked = true
const outerParamsInfo = deepCopy(state.defaultOuterParamsInfo)
outerParamsInfo['paramsInfoId'] = generateID()
state.outerParamsInfoArray.push(outerParamsInfo)
state.mapOuterParamsInfoArray[outerParamsInfo.paramsInfoId] = outerParamsInfo
curEditDataId.value = outerParamsInfo['paramsInfoId']
}
const removeOuterParamsInfo = (node, data) => {
@ -430,12 +426,13 @@ const removeOuterParamsInfo = (node, data) => {
children.splice(index, 1)
if (data.paramsInfoId === state.outerParamsInfo.paramsInfoId) {
delete state.mapOuterParamsInfoArray[data.paramsInfoId]
state.outerParamsInfo = null
state.curNodeId = null
}
}
const optInit = () => {
state.outerParamsSetVisible = true
initParams()
}
defineExpose({
@ -446,23 +443,115 @@ defineExpose({
<style scoped lang="less">
.root-class {
margin: 15px 0px 5px;
text-align: center;
justify-content: right;
}
.preview {
margin-top: 5px;
border: 1px solid #e6e6e6;
height: 350px !important;
border-radius: 4px;
height: 470px !important;
overflow: hidden;
background-size: 100% 100% !important;
}
.tree-head {
height: 40px;
line-height: 40px;
font-size: 12px;
color: #3d4d66;
.head-text {
margin-left: 16px;
font-weight: 500;
font-size: 14px;
color: #1f2329;
}
.head-filter {
flex: 1;
text-align: right;
margin-right: 16px;
font-weight: 400;
font-size: 12px;
color: #646a73;
}
}
:deep(.ed-row) {
width: 100%;
}
.m-del-icon-btn {
color: #646a73;
margin-top: 4px;
margin-left: 4px;
&:hover {
background: rgba(31, 35, 41, 0.1) !important;
}
&:focus {
background: rgba(31, 35, 41, 0.1) !important;
}
&:active {
background: rgba(31, 35, 41, 0.2) !important;
}
}
.empty {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.preview-show {
border-left: 1px solid #e6e6e6;
height: 350px;
background-size: 100% 100% !important;
}
.view-type-icon {
color: var(--ed-color-primary);
width: 22px;
height: 16px;
}
.custom-tree {
height: 100%;
width: 100%;
overflow-y: auto;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
.icon-more {
margin-left: auto;
visibility: hidden;
}
&:hover .icon-more {
margin-left: auto;
visibility: visible;
}
}
.link-icon-join {
font-size: 20px;
margin-top: 7px;
margin-left: 8px;
margin-right: 8px;
}
.inner-content {
width: 100%;
padding: 16px 16px 8px 16px;
font-size: 14px !important;
}
.slot-class {
color: white;
}
@ -490,8 +579,6 @@ defineExpose({
.select-filed {
/*width: 100%;*/
margin-left: 10px;
margin-right: 10px;
overflow: hidden; /*超出部分隐藏*/
white-space: nowrap; /*不换行*/
text-overflow: ellipsis; /*超出部分文字以...显示*/
@ -532,29 +619,12 @@ v-deep(.vue-treeselect__single-value) {
line-height: 28px !important;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
}
.auth-span {
float: right;
width: 40px;
margin-right: 5px;
}
.tree-head {
height: 30px;
line-height: 30px;
border-bottom: 1px solid var(--TableBorderColor, #e6e6e6);
background-color: var(--SiderBG, #f7f8fa);
font-size: 12px;
color: var(--TableColor, #3d4d66);
}
.tree-content {
height: calc(100% - 70px);
overflow-y: auto;

View File

@ -754,6 +754,9 @@ export const dvMainStore = defineStore('dataVisualization', {
setNowTargetPanelJumpInfo(jumpInfo) {
this.nowPanelJumpInfoTargetPanel = jumpInfo.baseJumpInfoVisualizationMap
},
setNowPanelOuterParamsInfo(outerParamsInfo) {
this.nowPanelOuterParamsInfo = outerParamsInfo.outerParamsInfoMap
},
// 添加联动 下钻 等查询组件
addViewTrackFilter(data) {
const viewId = data.viewId
@ -794,6 +797,72 @@ export const dvMainStore = defineStore('dataVisualization', {
useEmitt().emitter.emit('query-data-' + viewId)
})
},
// 添加外部参数的过滤条件
addOuterParamsFilter(params) {
// params 结构 {key1:value1,key2:value2}
const curComponentData = this.componentData
if (params) {
const trackInfo = this.nowPanelOuterParamsInfo
for (let index = 0; index < curComponentData.length; index++) {
const element = curComponentData[index]
if (element.component !== 'UserView') continue
const currentFilters = element.outerParamsFilters || [] // 外部参数信息
// 外部参数 可能会包含多个参数
Object.keys(params).forEach(function (sourceInfo) {
// 获取外部参数的值 sourceInfo 是外部参数名称 支持数组传入
let paramValue = params[sourceInfo]
let paramValueStr = params[sourceInfo]
let operator = 'in'
if (paramValue && !Array.isArray(paramValue)) {
paramValue = [paramValue]
operator = 'eq'
} else if (paramValue && Array.isArray(paramValue)) {
paramValueStr = ''
paramValue.forEach((innerValue, index) => {
if (index === 0) {
paramValueStr = innerValue
} else {
paramValueStr = paramValueStr + ',' + innerValue
}
})
}
// 获取所有目标联动信息
const targetInfoList = trackInfo[sourceInfo] || []
targetInfoList.forEach(targetInfo => {
const targetInfoArray = targetInfo.split('#')
const targetViewId = targetInfoArray[0] // 目标视图
if (element.component === 'UserView' && element.id === targetViewId) {
// 如果目标视图 当前循环组件id相等 则进行条件增减
const targetFieldId = targetInfoArray[1] // 目标视图列ID
const condition = {
fieldId: targetFieldId,
operator: operator,
value: paramValue,
viewIds: [targetViewId]
}
let j = currentFilters.length
while (j--) {
const filter = currentFilters[j]
// 兼容性准备 viewIds 只会存放一个值
if (targetFieldId === filter.fieldId && filter.viewIds.includes(targetViewId)) {
currentFilters.splice(j, 1)
}
}
// 不存在该条件 条件有效 直接保存该条件
// !filterExist && vValid && currentFilters.push(condition)
currentFilters.push(condition)
}
})
if (element.component === 'UserView') {
element['outerParamsFilters'] = currentFilters
}
curComponentData[index] = element
})
}
}
},
trackFilterCursor(element, checkQDList, trackInfo, preActiveComponentIds, viewId) {
const currentFilters = element.linkageFilters || [] // 当前联动filter
// 联动的图表情况历史条件

View File

@ -302,6 +302,7 @@ const filter = (firstLoad?: boolean) => {
user: wsCache.get('user.uid'),
filter,
linkageFilters: element.value.linkageFilters,
outerParamsFilters: element.value.outerParamsFilters,
drill: state.drillClickDimensionList,
resultCount: resultCount.value,
resultMode: resultMode.value

View File

@ -6,7 +6,11 @@ import router from '@/router'
import { initCanvasData } from '@/utils/canvasUtils'
import { queryTargetVisualizationJumpInfo } from '@/api/visualization/linkJump'
import { Base64 } from 'js-base64'
import { getOuterParamsInfo } from '@/api/visualization/outerParams'
import { ElMessage } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
const dvMainStore = dvMainStoreWithOut()
const { t } = useI18n()
const state = reactive({
canvasDataPreview: null,
canvasStylePreview: null,
@ -45,6 +49,21 @@ const loadCanvasDataAsync = async (dvId, dvType) => {
}
}
//
const attachParamsEncode = router.currentRoute.value.query.attachParams
let attachParam
if (attachParamsEncode) {
try {
attachParam = JSON.parse(Base64.decode(decodeURIComponent(attachParamsEncode)))
await getOuterParamsInfo(dvId).then(rsp => {
dvMainStore.setNowPanelOuterParamsInfo(rsp.data)
})
} catch (e) {
console.error(e)
ElMessage.error(t('visualization.outer_param_decode_error'))
}
}
initCanvasData(
dvId,
dvType,
@ -63,6 +82,9 @@ const loadCanvasDataAsync = async (dvId, dvType) => {
if (jumpParam) {
dvMainStore.addViewTrackFilter(jumpParam)
}
if (attachParam) {
dvMainStore.addOuterParamsFilter(attachParam)
}
if (props.publicLinkStatus) {
// title
document.title = dvInfo.name

View File

@ -3,6 +3,7 @@ package io.dataease.api.visualization;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.api.visualization.dto.VisualizationViewTableDTO;
import io.dataease.api.visualization.request.DataVisualizationBaseRequest;
import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest;
import io.dataease.api.visualization.vo.DataVisualizationVO;
@ -104,4 +105,8 @@ public interface DataVisualizationApi {
@Operation(summary = "解析可视化资源模板文件信息")
DataVisualizationVO decompressionLocalFile(@RequestPart(value = "file") MultipartFile file);
@GetMapping("/viewDetailList/{dvId}")
@Operation(summary = "仪表板视图明细数据")
List<VisualizationViewTableDTO> detailList(@PathVariable("dvId") Long dvId);
}

View File

@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.*;
public interface VisualizationOuterParamsApi {
@GetMapping("/queryWithDvId/{dvId}")
@GetMapping("/queryWithVisualizationId/{dvId}")
VisualizationOuterParamsDTO queryWithVisualizationId(@PathVariable("dvId") String dvId);
@PostMapping("/updateOuterParamsSet")

View File

@ -0,0 +1,21 @@
package io.dataease.api.visualization.dto;
import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.dto.dataset.DatasetTableFieldDTO;
import lombok.Data;
import java.util.List;
/**
* @author : WangJiaHao
* @date : 2024/3/14 12:42
*/
@Data
public class VisualizationViewTableDTO extends ChartViewDTO {
private String visualizationId;
private String baseVisualizationData;
private List<DatasetTableFieldDTO> tableFields;
}