Merge pull request #13432 from dataease/pr@dev-v2@feat_jump-filter

feat(仪表板、数据大屏): 跳转支持携带过滤参数
This commit is contained in:
王嘉豪 2024-11-20 11:41:37 +08:00 committed by GitHub
commit 4a34dc54c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 420 additions and 151 deletions

View File

@ -1,15 +1,16 @@
package io.dataease.visualization.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
*
* 跳转目标仪表板图表字段配置表
* </p>
*
* @author fit2cloud
* @since 2023-09-22
* @since 2024-11-20
*/
@TableName("visualization_link_jump_target_view_info")
public class VisualizationLinkJumpTargetViewInfo implements Serializable {
@ -18,6 +19,9 @@ public class VisualizationLinkJumpTargetViewInfo implements Serializable {
private Long targetId;
/**
* visualization_link_jump_info 表的 ID
*/
private Long linkJumpInfoId;
/**
@ -25,14 +29,31 @@ public class VisualizationLinkJumpTargetViewInfo implements Serializable {
*/
private Long sourceFieldActiveId;
private Long targetViewId;
/**
* 目标图表ID
*/
private String targetViewId;
private Long targetFieldId;
/**
* 目标字段ID
*/
private String targetFieldId;
/**
* 复制来源
*/
private Long copyFrom;
/**
* 复制来源ID
*/
private Long copyId;
/**
* 联动目标类型 view 图表 filter 过滤组件 outParams 外部参数
*/
private String targetType;
public Long getTargetId() {
return targetId;
}
@ -57,19 +78,19 @@ public class VisualizationLinkJumpTargetViewInfo implements Serializable {
this.sourceFieldActiveId = sourceFieldActiveId;
}
public Long getTargetViewId() {
public String getTargetViewId() {
return targetViewId;
}
public void setTargetViewId(Long targetViewId) {
public void setTargetViewId(String targetViewId) {
this.targetViewId = targetViewId;
}
public Long getTargetFieldId() {
public String getTargetFieldId() {
return targetFieldId;
}
public void setTargetFieldId(Long targetFieldId) {
public void setTargetFieldId(String targetFieldId) {
this.targetFieldId = targetFieldId;
}
@ -89,6 +110,14 @@ public class VisualizationLinkJumpTargetViewInfo implements Serializable {
this.copyId = copyId;
}
public String getTargetType() {
return targetType;
}
public void setTargetType(String targetType) {
this.targetType = targetType;
}
@Override
public String toString() {
return "VisualizationLinkJumpTargetViewInfo{" +
@ -99,6 +128,7 @@ public class VisualizationLinkJumpTargetViewInfo implements Serializable {
", targetFieldId = " + targetFieldId +
", copyFrom = " + copyFrom +
", copyId = " + copyId +
", targetType = " + targetType +
"}";
}
}

View File

@ -6,11 +6,11 @@ import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* 跳转目标仪表板图表字段配置表 Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2023-09-22
* @since 2024-11-20
*/
@Mapper
public interface VisualizationLinkJumpTargetViewInfoMapper extends BaseMapper<VisualizationLinkJumpTargetViewInfo> {

View File

@ -4,6 +4,7 @@ import io.dataease.api.visualization.dto.VisualizationLinkJumpDTO;
import io.dataease.api.visualization.request.VisualizationLinkJumpBaseRequest;
import io.dataease.api.visualization.vo.VisualizationLinkJumpInfoVO;
import io.dataease.api.visualization.vo.VisualizationLinkJumpVO;
import io.dataease.api.visualization.vo.VisualizationOutParamsJumpVO;
import io.dataease.api.visualization.vo.VisualizationViewTableVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@ -36,6 +37,10 @@ public interface ExtVisualizationLinkJumpMapper {
void copyLinkJumpTarget(@Param("copyId")Long copyId);
List<VisualizationLinkJumpVO> findLinkJumpWithDvId(@Param("dvId")Long dvId);
List<VisualizationLinkJumpInfoVO> findLinkJumpInfoWithDvId(@Param("dvId")Long dvId);
List<VisualizationViewTableVO> getViewTableDetails(@Param("dvId")Long dvId);
List<VisualizationOutParamsJumpVO> queryOutParamsTargetWithDvId(@Param("dvId")Long dvId);
}

View File

@ -6,6 +6,7 @@ import io.dataease.api.visualization.dto.VisualizationLinkJumpDTO;
import io.dataease.api.visualization.dto.VisualizationLinkJumpInfoDTO;
import io.dataease.api.visualization.request.VisualizationLinkJumpBaseRequest;
import io.dataease.api.visualization.response.VisualizationLinkJumpBaseResponse;
import io.dataease.api.visualization.vo.VisualizationOutParamsJumpVO;
import io.dataease.api.visualization.vo.VisualizationViewTableVO;
import io.dataease.auth.DeLinkPermit;
import io.dataease.chart.dao.auto.entity.CoreChartView;
@ -148,15 +149,18 @@ public class VisualizationLinkJumpService implements VisualizationLinkJumpApi {
public VisualizationComponentDTO viewTableDetailList(Long dvId) {
DataVisualizationInfo dvInfo = dataVisualizationInfoMapper.selectById(dvId);
List<VisualizationViewTableVO> result;
List<VisualizationOutParamsJumpVO> outParamsJumpInfo;
String componentData;
if (dvInfo != null) {
result = extVisualizationLinkJumpMapper.getViewTableDetails(dvId).stream().filter(viewTableInfo -> dvInfo.getComponentData().indexOf(viewTableInfo.getId().toString()) > -1).collect(Collectors.toList());
componentData = dvInfo.getComponentData();
outParamsJumpInfo = extVisualizationLinkJumpMapper.queryOutParamsTargetWithDvId(dvId);
} else {
result = new ArrayList<>();
outParamsJumpInfo = new ArrayList<>();
componentData = "[]";
}
return new VisualizationComponentDTO(componentData,result);
return new VisualizationComponentDTO(componentData,result,outParamsJumpInfo);
}

View File

@ -6,3 +6,10 @@ VALUES (1048232869488627720, 'basic.defaultOpen', '1', 'text', 14);
ALTER TABLE `data_visualization_info`
ADD COLUMN `content_id` varchar(50) NULL DEFAULT '0' COMMENT '内容标识';
ALTER TABLE `visualization_link_jump_target_view_info`
ADD COLUMN `target_type` varchar(50) NULL COMMENT '联动目标类型 view 图表 filter 过滤组件 outParams 外部参数';
ALTER TABLE `visualization_link_jump_target_view_info`
MODIFY COLUMN `target_view_id` varchar(50) NULL DEFAULT NULL COMMENT '目标图表ID' AFTER `source_field_active_id`,
MODIFY COLUMN `target_field_id` varchar(50) NULL DEFAULT NULL COMMENT '目标字段ID' AFTER `target_view_id`;

View File

@ -10,3 +10,10 @@ UPDATE `xpack_setting_authentication` set `synced` = 0 where `name` = 'oidc' or
ALTER TABLE `data_visualization_info`
ADD COLUMN `content_id` varchar(50) NULL DEFAULT '0' COMMENT '内容标识';
ALTER TABLE `visualization_link_jump_target_view_info`
ADD COLUMN `target_type` varchar(50) NULL DEFAULT 'view' COMMENT '联动目标类型 view 图表 filter 过滤组件 outParams 外部参数';
ALTER TABLE `visualization_link_jump_target_view_info`
MODIFY COLUMN `target_view_id` varchar(50) NULL DEFAULT NULL COMMENT '目标图表ID' AFTER `source_field_active_id`,
MODIFY COLUMN `target_field_id` varchar(50) NULL DEFAULT NULL COMMENT '目标字段ID' AFTER `target_view_id`;

View File

@ -155,6 +155,19 @@
WHERE core_chart_view.id = #{viewId}
</select>
<select id="queryOutParamsTargetWithDvId" resultType="io.dataease.api.visualization.vo.VisualizationOutParamsJumpVO">
SELECT
vopi.params_id as id,
vopi.param_name as name,
vopi.param_name as title,
'outerParams' as type
FROM
visualization_outer_params_info vopi
LEFT JOIN visualization_outer_params vop ON vopi.params_id = vop.params_id
WHERE
vop.visualization_id = #{dvId}
</select>
<delete id="deleteJumpTargetViewInfo">
DELETE
ljtv

View File

@ -43,6 +43,8 @@ import waterfall from '@/assets/svg/waterfall.svg'
import wordCloud from '@/assets/svg/word-cloud.svg'
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'
const iconChartMap = {
'area-stack': areaStack,
@ -89,7 +91,9 @@ const iconChartMap = {
waterfall: waterfall,
'word-cloud': wordCloud,
't-heatmap': tHeatmap,
'picture-group': pictureGroup
'picture-group': pictureGroup,
filter: filter,
outerParams: outerParams
}
export { iconChartMap }

View File

@ -196,141 +196,261 @@
</el-form-item>
</div>
</div>
<el-row style="margin-bottom: 8px" :gutter="8">
<el-col :span="7"> 源字段 </el-col>
<el-col :span="2"></el-col>
<el-col :span="7" style="margin-left: -2.9%">
{{ t('visualization.link_view_field') }}
</el-col>
<el-col :span="8"></el-col>
</el-row>
<div class="main-scrollbar-container">
<el-scrollbar height="fit-content" max-height="208px">
<div
style="display: flex; margin-bottom: 6px"
v-for="(targetViewInfo, index) in state.linkJumpInfo.targetViewInfoList"
:key="index"
>
<div style="flex: 1">
<el-select
v-model="targetViewInfo.sourceFieldActiveId"
:placeholder="'请选择字段'"
style="width: 100%"
>
<el-option
v-for="curViewField in state.linkJumpCurViewFieldArray"
:key="curViewField.id"
:label="curViewField.name"
:value="curViewField.id"
>
<span class="custom-option">
<Icon
><component
class="svg-icon"
style="width: 14px; height: 14px"
:class="`field-icon-${fieldType[curViewField.deType]}`"
:is="iconFieldMap[fieldType[curViewField.deType]]"
></component
></Icon>
<span style="float: left; margin-left: 4px; font-size: 14px">{{
curViewField.name
}}</span>
</span>
</el-option>
</el-select>
</div>
<div class="icon-center">
<Icon name="dv-link-target"
><dvLinkTarget style="width: 20px; height: 20px" class="svg-icon"
/></Icon>
</div>
<div style="flex: 1">
<el-select
v-model="targetViewInfo.targetViewId"
:disabled="!targetViewInfo.sourceFieldActiveId"
:placeholder="'请选择图表'"
style="width: 100%"
@change="viewInfoOnChange(targetViewInfo)"
>
<el-option
v-for="item in state.currentLinkPanelViewArray"
:key="item.id"
:label="item.title"
:value="item.id"
>
<span class="custom-option">
<Icon
><component
class="svg-icon view-type-icon"
style="width: 14px; height: 14px"
:is="iconChartMap[item.type]"
></component
></Icon>
<span style="float: left; margin-left: 4px; font-size: 14px">{{
item.title
}}</span>
</span>
</el-option>
</el-select>
</div>
<div style="flex: 1; margin: 0 8px">
<el-select
v-model="targetViewInfo.targetFieldId"
:placeholder="'请选择字段'"
:disabled="fieldIdDisabledCheck(targetViewInfo)"
style="width: 100%"
>
<el-option
v-for="viewField in state.viewIdFieldArrayMap[
targetViewInfo.targetViewId
]"
:key="viewField.id"
:label="viewField.name"
:value="viewField.id"
>
<span class="custom-option">
<Icon
><component
class="svg-icon"
style="width: 14px; height: 14px"
:class="`field-icon-${fieldType[viewField.deType]}`"
:is="iconFieldMap[fieldType[viewField.deType]]"
></component
></Icon>
<span style="float: left; margin-left: 4px; font-size: 14px">{{
viewField.name
}}</span>
</span>
</el-option>
</el-select>
</div>
<el-button
class="m-del-icon-btn"
text
@click="deleteLinkJumpField(index)"
>
<el-icon size="20px">
<Icon name="icon_delete-trash_outlined"
><icon_deleteTrash_outlined class="svg-icon"
/></Icon>
</el-icon>
</el-button>
</div>
</el-scrollbar>
<el-button
style="margin-top: 8px"
:disabled="!state.linkJumpInfo.targetDvId"
type="primary"
icon="Plus"
text
@click="addLinkJumpField"
>
{{ t('visualization.add_jump_field') }}
</el-button>
<div class="jump-com-list">
<el-tabs size="small" v-model="state.activeCollapse">
<el-tab-pane label="联动图表" name="view"> </el-tab-pane>
<el-tab-pane label="携带查询条件" name="filter-params"> </el-tab-pane>
</el-tabs>
</div>
<template v-if="state.activeCollapse === 'view'">
<el-row style="margin-bottom: 8px" :gutter="8">
<el-col :span="7"> 源字段 </el-col>
<el-col :span="2"></el-col>
<el-col :span="7" style="margin-left: -2.9%">
{{ t('visualization.link_view_field') }}
</el-col>
<el-col :span="8"></el-col>
</el-row>
<div class="main-scrollbar-container">
<el-scrollbar height="fit-content" max-height="178px">
<div
style="display: flex; margin-bottom: 6px"
v-for="(targetViewInfo, index) in state.linkJumpInfo
.targetViewInfoList"
:key="index"
>
<div style="flex: 1">
<el-select
v-model="targetViewInfo.sourceFieldActiveId"
:placeholder="'请选择字段'"
style="width: 100%"
>
<el-option
v-for="curViewField in state.linkJumpCurViewFieldArray"
:key="curViewField.id"
:label="curViewField.name"
:value="curViewField.id"
>
<span class="custom-option">
<Icon
><component
class="svg-icon"
style="width: 14px; height: 14px"
:class="`field-icon-${fieldType[curViewField.deType]}`"
:is="iconFieldMap[fieldType[curViewField.deType]]"
></component
></Icon>
<span
style="float: left; margin-left: 4px; font-size: 14px"
>{{ curViewField.name }}</span
>
</span>
</el-option>
</el-select>
</div>
<div class="icon-center">
<Icon name="dv-link-target"
><dvLinkTarget style="width: 20px; height: 20px" class="svg-icon"
/></Icon>
</div>
<div style="flex: 1">
<el-select
v-model="targetViewInfo.targetViewId"
:disabled="!targetViewInfo.sourceFieldActiveId"
:placeholder="'请选择图表'"
style="width: 100%"
@change="viewInfoOnChange(targetViewInfo)"
>
<el-option
v-for="item in state.currentLinkPanelViewArray"
:key="item.id"
:label="item.title"
:value="item.id"
>
<span class="custom-option">
<Icon
><component
class="svg-icon view-type-icon"
style="width: 14px; height: 14px"
:is="iconChartMap[item.type]"
></component
></Icon>
<span
style="float: left; margin-left: 4px; font-size: 14px"
>{{ item.title }}</span
>
</span>
</el-option>
</el-select>
</div>
<div style="flex: 1; margin: 0 8px">
<el-select
v-model="targetViewInfo.targetFieldId"
:placeholder="'请选择字段'"
:disabled="fieldIdDisabledCheck(targetViewInfo)"
style="width: 100%"
>
<el-option
v-for="viewField in state.viewIdFieldArrayMap[
targetViewInfo.targetViewId
]"
:key="viewField.id"
:label="viewField.name"
:value="viewField.id"
>
<span class="custom-option">
<Icon
><component
class="svg-icon"
style="width: 14px; height: 14px"
:class="`field-icon-${fieldType[viewField.deType]}`"
:is="iconFieldMap[fieldType[viewField.deType]]"
></component
></Icon>
<span
style="float: left; margin-left: 4px; font-size: 14px"
>{{ viewField.name }}</span
>
</span>
</el-option>
</el-select>
</div>
<el-button
class="m-del-icon-btn"
text
@click="deleteLinkJumpField(index)"
>
<el-icon size="20px">
<Icon name="icon_delete-trash_outlined"
><icon_deleteTrash_outlined class="svg-icon"
/></Icon>
</el-icon>
</el-button>
</div>
</el-scrollbar>
<el-button
style="margin-top: 8px"
:disabled="!state.linkJumpInfo.targetDvId"
type="primary"
icon="Plus"
text
@click="addLinkJumpField"
>
{{ t('visualization.add_jump_field') }}
</el-button>
</div>
</template>
<template v-if="state.activeCollapse === 'filter'">
<el-row style="margin-bottom: 8px" :gutter="8">
<el-col :span="7"> 源字段 </el-col>
<el-col :span="2"></el-col>
<el-col :span="7" style="margin-left: -2.9%">
{{ t('visualization.link_view_field') }}
</el-col>
<el-col :span="8"></el-col>
</el-row>
<div class="main-scrollbar-container">
<el-scrollbar height="fit-content" max-height="178px">
<div
style="display: flex; margin-bottom: 6px"
v-for="(
targetViewInfo, index
) in state.linkJumpInfo.targetViewInfoList.filter(
item => item.type === 'outerParams'
)"
:key="index"
>
<div style="flex: 1">
<el-select
v-model="targetViewInfo.sourceFieldActiveId"
:placeholder="'请选择字段'"
style="width: 100%"
>
<el-option
v-for="curViewField in state.linkJumpCurViewFieldArray"
:key="curViewField.id"
:label="curViewField.name"
:value="curViewField.id"
>
<span class="custom-option">
<Icon
><component
class="svg-icon"
style="width: 14px; height: 14px"
:class="`field-icon-${fieldType[curViewField.deType]}`"
:is="iconFieldMap[fieldType[curViewField.deType]]"
></component
></Icon>
<span
style="float: left; margin-left: 4px; font-size: 14px"
>{{ curViewField.name }}</span
>
</span>
</el-option>
</el-select>
</div>
<div class="icon-center">
<Icon name="dv-link-target"
><dvLinkTarget style="width: 20px; height: 20px" class="svg-icon"
/></Icon>
</div>
<div style="flex: 1">
<el-select
v-model="targetViewInfo.targetViewId"
:disabled="!targetViewInfo.sourceFieldActiveId"
:placeholder="'请选择参数'"
style="width: 100%"
@change="viewInfoOnChange(targetViewInfo)"
>
<el-option
v-for="item in state.currentOutParams"
:key="item.id"
:label="item.title"
:value="item.id"
>
<span class="custom-option">
<Icon
><component
class="svg-icon view-type-icon"
style="width: 14px; height: 14px"
:is="iconChartMap[item.type]"
></component
></Icon>
<span
style="float: left; margin-left: 4px; font-size: 14px"
>{{ item.title }}</span
>
</span>
</el-option>
</el-select>
</div>
<el-button
class="m-del-icon-btn"
text
@click="deleteLinkJumpField(index)"
>
<el-icon size="20px">
<Icon name="icon_delete-trash_outlined"
><icon_deleteTrash_outlined class="svg-icon"
/></Icon>
</el-icon>
</el-button>
</div>
</el-scrollbar>
<el-button
style="margin-top: 8px"
:disabled="!state.linkJumpInfo.targetDvId"
type="primary"
icon="Plus"
text
@click="addLinkJumpField"
>
{{ t('visualization.add_jump_field') }}
</el-button>
</div>
</template>
</el-form>
</template>
@ -470,6 +590,7 @@ const resourceType = computed(() =>
)
const state = reactive({
activeCollapse: 'view',
loading: false,
showSelected: false,
curJumpViewInfo: {},
@ -526,7 +647,8 @@ const state = reactive({
quotaList: [],
quotaData: [],
dimension: [],
quota: []
quota: [],
currentOutParams: []
})
const outerContentEditor = ref(null)
@ -705,6 +827,17 @@ const getPanelViewList = dvId => {
state.viewIdFieldArrayMap[view.id] = view.tableFields
})
}
// currentLinkPanelViewArray
// currentLinkPanelViewArray 便
state.currentOutParams = rsp.data.outParamsJumpInfo || []
if (state.currentOutParams && state.currentOutParams.length > 0) {
state.currentOutParams.forEach(outerParamsItem => {
state.currentLinkPanelViewArray.push(outerParamsItem)
state.viewIdFieldArrayMap[outerParamsItem.id] = [
{ id: '1000001', name: t('visualization.out_params_no_select') }
]
})
}
//
JSON.parse(rsp.data.bashComponentData).forEach(componentItem => {
if (componentItem.component === 'VQuery') {
@ -723,6 +856,7 @@ const getPanelViewList = dvId => {
})
})
}
const dvNodeClick = data => {
if (data.leaf) {
state.linkJumpInfo.targetViewInfoList = []
@ -736,6 +870,11 @@ const addLinkJumpField = () => {
targetFieldId: ''
})
}
const deleteLinkJumpFieldById = index => {
state.linkJumpInfo.targetViewInfoList.splice(index, 1)
}
const deleteLinkJumpField = index => {
state.linkJumpInfo.targetViewInfoList.splice(index, 1)
}
@ -1244,4 +1383,42 @@ span {
display: flex;
align-items: center;
}
.jump-com-list {
width: 100%;
margin-top: -18px;
:deep(.ed-collapse) {
--ed-collapse-header-font-size: 14px;
--ed-collapse-content-font-size: 14px;
}
:deep(.ed-tabs__active-bar) {
height: 2px;
}
& > :deep(.ed-tabs) {
--ed-tabs-header-height: 36px;
margin-bottom: 12px;
position: sticky;
background: #fff;
.ed-tabs__header {
&::before {
content: '';
width: 8px;
height: 1px;
position: absolute;
bottom: 0;
left: 0;
background: #1f232926;
}
}
}
:deep(.ed-tabs__item) {
font-size: 14px;
}
:deep(.ed-tabs__item):not(.is-active) {
color: #646a73;
}
}
</style>

View File

@ -2672,6 +2672,7 @@ export default {
column_name: '字段名称'
},
visualization: {
out_params_no_select: '外部参数无需选择',
filter_no_select: '过滤组件无需选择',
forbidden_copy: '当前组件不允许复制',
url_check_error: '跳转错误URL不合法',

View File

@ -1,5 +1,6 @@
package io.dataease.api.visualization.dto;
import io.dataease.api.visualization.vo.VisualizationOutParamsJumpVO;
import io.dataease.api.visualization.vo.VisualizationViewTableVO;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -20,4 +21,6 @@ public class VisualizationComponentDTO {
List<VisualizationViewTableVO> visualizationViewTables;
List<VisualizationOutParamsJumpVO> outParamsJumpInfo;
}

View File

@ -19,12 +19,15 @@ public class VisualizationLinkJumpTargetViewInfoVO {
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long sourceFieldActiveId;
@JsonSerialize(using = ToStringSerializer.class)
private Long targetViewId;
@JsonSerialize(using = ToStringSerializer.class)
private Long targetFieldId;
private String targetViewId;
private String targetFieldId;
@JsonSerialize(using = ToStringSerializer.class)
private Long copyFrom;
@JsonSerialize(using = ToStringSerializer.class)
private Long copyId;
/**
* 联动目标类型 view 图表 filter 过滤组件 outParams 外部参数
*/
private String targetType;
}

View File

@ -0,0 +1,15 @@
package io.dataease.api.visualization.vo;
import lombok.Data;
@Data
public class VisualizationOutParamsJumpVO {
private String id;
private String type;
private String name;
private String title;
}