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.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; 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.DataVisualizationBaseVO;
import io.dataease.api.visualization.vo.DataVisualizationVO; import io.dataease.api.visualization.vo.DataVisualizationVO;
import io.dataease.api.visualization.vo.VisualizationResourceVO; import io.dataease.api.visualization.vo.VisualizationResourceVO;
@ -45,4 +46,7 @@ public interface ExtDataVisualizationMapper {
void copyLinkageField(@Param("copyId") Long copyId); 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.TemplateManageFileDTO;
import io.dataease.api.template.dto.VisualizationTemplateExtendDataDTO; import io.dataease.api.template.dto.VisualizationTemplateExtendDataDTO;
import io.dataease.api.visualization.DataVisualizationApi; 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.DataVisualizationBaseRequest;
import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest; import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest;
import io.dataease.api.visualization.vo.DataVisualizationVO; import io.dataease.api.visualization.vo.DataVisualizationVO;
@ -354,6 +355,11 @@ public class DataVisualizationServer implements DataVisualizationApi {
return null; return null;
} }
@Override
public List<VisualizationViewTableDTO> detailList(Long dvId) {
return extDataVisualizationMapper.getVisualizationViewDetails(dvId);
}
@Override @Override
public void nameCheck(DataVisualizationBaseRequest request) { public void nameCheck(DataVisualizationBaseRequest request) {

View File

@ -41,7 +41,8 @@
`copy_id`) `copy_id`)
SELECT core_chart_view.`id` + #{copyId} as id, SELECT core_chart_view.`id` + #{copyId} as id,
`title`, `title`,
#{newDvId} as scene_id, `table_id`, #{newDvId} as scene_id,
`table_id`,
`type`, `type`,
`render`, `render`,
`result_count`, `result_count`,
@ -73,7 +74,8 @@
`refresh_time`, `refresh_time`,
`linkage_active`, `linkage_active`,
`jump_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 FROM core_chart_view
WHERE core_chart_view.scene_id = #{sourceDvId} WHERE core_chart_view.scene_id = #{sourceDvId}
</insert> </insert>
@ -160,65 +162,58 @@
</select> </select>
<select id="findRecent" resultType="io.dataease.visualization.dao.ext.po.VisualizationResourcePO"> <select id="findRecent" resultType="io.dataease.visualization.dao.ext.po.VisualizationResourcePO">
SELECT SELECT dvResource.id,
dvResource.id, dvResource.resource_id,
dvResource.resource_id, dvResource.name,
dvResource.name, dvResource.type,
dvResource.type, dvResource.creator,
dvResource.creator, core_opt_recent.uid AS last_editor,
core_opt_recent.uid AS last_editor, core_opt_recent.time AS last_edit_time,
core_opt_recent.time AS last_edit_time, (
( CASE
CASE
WHEN core_store.resource_id IS NULL THEN WHEN core_store.resource_id IS NULL THEN
0 ELSE 1 0
END ELSE 1
) AS favorite END
FROM ) AS favorite
( FROM (SELECT core_dataset_group.id,
SELECT core_dataset_group.id AS resource_id,
core_dataset_group.id, core_dataset_group.NAME,
core_dataset_group.id AS resource_id, 'dataset' AS type,
core_dataset_group.NAME, core_dataset_group.create_by AS creator
'dataset' AS type, FROM core_dataset_group
core_dataset_group.create_by AS creator WHERE core_dataset_group.node_type = 'dataset'
FROM UNION ALL
core_dataset_group SELECT core_datasource.id,
WHERE core_datasource.id AS resource_id,
core_dataset_group.node_type = 'dataset' UNION ALL core_datasource.NAME,
SELECT 'datasource' AS type,
core_datasource.id, core_datasource.create_by AS creator
core_datasource.id AS resource_id, FROM core_datasource
core_datasource.NAME, WHERE core_datasource.type != 'folder'
'datasource' AS type, UNION ALL
core_datasource.create_by AS creator SELECT
FROM data_visualization_info.id,
core_datasource data_visualization_info.id AS resource_id,
WHERE data_visualization_info.NAME,
core_datasource.type != 'folder' UNION ALL (
SELECT CASE
data_visualization_info.id, data_visualization_info.type
data_visualization_info.id AS resource_id, WHEN 'dataV' THEN
data_visualization_info.NAME, 'screen' ELSE 'panel'
( END
CASE ) AS type,
data_visualization_info.type data_visualization_info.create_by AS creator
WHEN 'dataV' THEN FROM
'screen' ELSE 'panel' data_visualization_info
END WHERE
) AS type, data_visualization_info.delete_flag = 0
data_visualization_info.create_by AS creator AND node_type = 'leaf') dvResource
FROM LEFT JOIN core_store ON dvResource.id = core_store.resource_id
data_visualization_info AND core_store.uid = #{uid}
WHERE INNER JOIN core_opt_recent ON dvResource.resource_id = core_opt_recent.resource_id
data_visualization_info.delete_flag = 0 AND core_opt_recent.uid = #{uid} ${ew.customSqlSegment}
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> </select>
<insert id="copyLinkJump"> <insert id="copyLinkJump">
@ -270,7 +265,7 @@
visualization_link_jump_info.`checked`, visualization_link_jump_info.`checked`,
visualization_link_jump_info.`attach_params`, visualization_link_jump_info.`attach_params`,
visualization_link_jump_info.`id` AS copy_from, visualization_link_jump_info.`id` AS copy_from,
#{copyId} AS copy_id #{copyId} AS copy_id
FROM visualization_link_jump_info FROM visualization_link_jump_info
INNER JOIN (SELECT id AS t_id, INNER JOIN (SELECT id AS t_id,
copy_from AS s_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_view_id`,
visualization_link_jump_target_view_info.`target_field_id`, visualization_link_jump_target_view_info.`target_field_id`,
visualization_link_jump_target_view_info.`target_id` AS copy_from, 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 FROM visualization_link_jump_target_view_info
INNER JOIN (SELECT id AS t_id, INNER JOIN (SELECT id AS t_id,
copy_from AS s_id copy_from AS s_id
@ -370,4 +365,36 @@
WHERE copy_id = #{copyId}) pvlf_copy WHERE copy_id = #{copyId}) pvlf_copy
ON visualization_linkage_field.linkage_id = pvlf_copy.s_id ON visualization_linkage_field.linkage_id = pvlf_copy.s_id
</insert> </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> </mapper>

View File

@ -61,14 +61,14 @@
</select> </select>
<select id="queryWithVisualizationId" resultMap="BaseResultMapDTO"> <select id="queryWithVisualizationId" resultMap="BaseResultMapDTO">
SELECT SELECT
#{visualizationId} as visualization_id, #{visualizationId} as visualization_id,
ifnull( visualization_outer_params.checked, 0 ) AS checked ifnull( visualization_outer_params.checked, 0 ) AS checked
FROM FROM
visualization_group data_visualization_info
LEFT JOIN visualization_outer_params ON visualization_group.id = visualization_outer_params.visualization_id LEFT JOIN visualization_outer_params ON data_visualization_info.id = visualization_outer_params.visualization_id
WHERE WHERE
visualization_group.id = #{visualizationId} data_visualization_info.id = #{visualizationId}
</select> </select>
<delete id="deleteOuterParamsTargetWithVisualizationId" > <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 decompression = data => request.post({ url: '/dataVisualization/decompression', data })
export const detailList = dvId => { export const viewDetailList = dvId => {
return request.get({ return request.get({
url: '/dataVisualization/view/detailList/' + dvId, url: '/dataVisualization/viewDetailList/' + dvId,
method: 'get', method: 'get',
loading: false loading: false
}) })

View File

@ -1,8 +1,8 @@
import request from '@/config/axios' import request from '@/config/axios'
export function queryWithDvId(dvId) { export function queryWithVisualizationId(dvId) {
return request.get({ 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"> <div class="right-area" v-if="!batchOptStatus && !linkageSettingStatus">
<template v-if="editMode !== 'preview'"> <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"> <el-tooltip effect="dark" content="批量操作" placement="bottom">
<component-button <component-button
tips="批量操作" tips="批量操作"

View File

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

View File

@ -754,6 +754,9 @@ export const dvMainStore = defineStore('dataVisualization', {
setNowTargetPanelJumpInfo(jumpInfo) { setNowTargetPanelJumpInfo(jumpInfo) {
this.nowPanelJumpInfoTargetPanel = jumpInfo.baseJumpInfoVisualizationMap this.nowPanelJumpInfoTargetPanel = jumpInfo.baseJumpInfoVisualizationMap
}, },
setNowPanelOuterParamsInfo(outerParamsInfo) {
this.nowPanelOuterParamsInfo = outerParamsInfo.outerParamsInfoMap
},
// 添加联动 下钻 等查询组件 // 添加联动 下钻 等查询组件
addViewTrackFilter(data) { addViewTrackFilter(data) {
const viewId = data.viewId const viewId = data.viewId
@ -794,6 +797,72 @@ export const dvMainStore = defineStore('dataVisualization', {
useEmitt().emitter.emit('query-data-' + viewId) 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) { trackFilterCursor(element, checkQDList, trackInfo, preActiveComponentIds, viewId) {
const currentFilters = element.linkageFilters || [] // 当前联动filter const currentFilters = element.linkageFilters || [] // 当前联动filter
// 联动的图表情况历史条件 // 联动的图表情况历史条件

View File

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

View File

@ -6,7 +6,11 @@ import router from '@/router'
import { initCanvasData } from '@/utils/canvasUtils' import { initCanvasData } from '@/utils/canvasUtils'
import { queryTargetVisualizationJumpInfo } from '@/api/visualization/linkJump' import { queryTargetVisualizationJumpInfo } from '@/api/visualization/linkJump'
import { Base64 } from 'js-base64' 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 dvMainStore = dvMainStoreWithOut()
const { t } = useI18n()
const state = reactive({ const state = reactive({
canvasDataPreview: null, canvasDataPreview: null,
canvasStylePreview: 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( initCanvasData(
dvId, dvId,
dvType, dvType,
@ -63,6 +82,9 @@ const loadCanvasDataAsync = async (dvId, dvType) => {
if (jumpParam) { if (jumpParam) {
dvMainStore.addViewTrackFilter(jumpParam) dvMainStore.addViewTrackFilter(jumpParam)
} }
if (attachParam) {
dvMainStore.addOuterParamsFilter(attachParam)
}
if (props.publicLinkStatus) { if (props.publicLinkStatus) {
// title // title
document.title = dvInfo.name 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.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.github.xiaoymin.knife4j.annotations.ApiSupport; 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.DataVisualizationBaseRequest;
import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest; import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest;
import io.dataease.api.visualization.vo.DataVisualizationVO; import io.dataease.api.visualization.vo.DataVisualizationVO;
@ -104,4 +105,8 @@ public interface DataVisualizationApi {
@Operation(summary = "解析可视化资源模板文件信息") @Operation(summary = "解析可视化资源模板文件信息")
DataVisualizationVO decompressionLocalFile(@RequestPart(value = "file") MultipartFile file); 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 { public interface VisualizationOuterParamsApi {
@GetMapping("/queryWithDvId/{dvId}") @GetMapping("/queryWithVisualizationId/{dvId}")
VisualizationOuterParamsDTO queryWithVisualizationId(@PathVariable("dvId") String dvId); VisualizationOuterParamsDTO queryWithVisualizationId(@PathVariable("dvId") String dvId);
@PostMapping("/updateOuterParamsSet") @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;
}