feat(图表): 表格图表的条件样式支持动态值

This commit is contained in:
jianneng-fit2cloud 2024-09-25 16:33:26 +08:00
parent d521e1ce32
commit 7063a24116
13 changed files with 852 additions and 98 deletions

View File

@ -250,6 +250,120 @@ public class DefaultChartHandler extends AbstractChartPlugin {
return res;
}
protected List<ChartViewFieldDTO> getAssistFields(List<ChartSeniorAssistDTO> list, List<ChartViewFieldDTO> yAxis, List<ChartViewFieldDTO> xAxis) {
List<ChartViewFieldDTO> res = new ArrayList<>();
for (ChartSeniorAssistDTO dto : list) {
DatasetTableFieldDTO curField = dto.getCurField();
ChartViewFieldDTO field = null;
String alias = "";
for (int i = 0; i < yAxis.size(); i++) {
ChartViewFieldDTO yField = yAxis.get(i);
if (Objects.equals(yField.getId(), curField.getId())) {
field = yField;
alias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i);
break;
}
}
if (ObjectUtils.isEmpty(field) && CollectionUtils.isNotEmpty(xAxis)) {
for (int i = 0; i < xAxis.size(); i++) {
ChartViewFieldDTO xField = xAxis.get(i);
if (StringUtils.equalsIgnoreCase(String.valueOf(xField.getId()), String.valueOf(curField.getId()))) {
field = xField;
alias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i);
break;
}
}
}
if (ObjectUtils.isEmpty(field)) {
continue;
}
ChartViewFieldDTO chartViewFieldDTO = new ChartViewFieldDTO();
BeanUtils.copyBean(chartViewFieldDTO, curField);
chartViewFieldDTO.setSummary(dto.getSummary());
chartViewFieldDTO.setOriginName(alias);// yAxis的字段别名就是查找的字段名
res.add(chartViewFieldDTO);
}
return res;
}
public List<ChartSeniorAssistDTO> getDynamicThresholdFields(ChartViewDTO view) {
List<ChartSeniorAssistDTO> list = new ArrayList<>();
Map<String, Object> senior = view.getSenior();
if (ObjectUtils.isEmpty(senior)) {
return list;
}
ChartSeniorThresholdCfgDTO thresholdCfg = JsonUtil.parseObject((String) JsonUtil.toJSONString(senior.get("threshold")), ChartSeniorThresholdCfgDTO.class);
if (null == thresholdCfg || !thresholdCfg.isEnable()) {
return list;
}
List<TableThresholdDTO> tableThreshold = thresholdCfg.getTableThreshold();
if (ObjectUtils.isEmpty(tableThreshold)) {
return list;
}
List<ChartSeniorThresholdDTO> conditionsList = tableThreshold.stream()
.filter(item -> !ObjectUtils.isEmpty(item))
.map(TableThresholdDTO::getConditions)
.flatMap(List::stream)
.filter(condition -> StringUtils.equalsAnyIgnoreCase(condition.getType(), "dynamic"))
.toList();
List<ChartSeniorAssistDTO> assistDTOs = conditionsList.stream()
.flatMap(condition -> getConditionFields(condition).stream())
.filter(this::solveThresholdCondition)
.toList();
list.addAll(assistDTOs);
return list;
}
private boolean solveThresholdCondition(ChartSeniorAssistDTO fieldDTO) {
Long fieldId = fieldDTO.getFieldId();
String summary = fieldDTO.getValue();
if (ObjectUtils.isEmpty(fieldId) || StringUtils.isEmpty(summary)) {
return false;
}
DatasetTableFieldDTO datasetTableFieldDTO = datasetTableFieldManage.selectById(fieldId);
if (ObjectUtils.isEmpty(datasetTableFieldDTO)) {
return false;
}
ChartViewFieldDTO datasetTableField = new ChartViewFieldDTO();
BeanUtils.copyBean(datasetTableField, datasetTableFieldDTO);
fieldDTO.setCurField(datasetTableField);
fieldDTO.setSummary(summary);
return true;
}
private List<ChartSeniorAssistDTO> getConditionFields(ChartSeniorThresholdDTO condition) {
List<ChartSeniorAssistDTO> list = new ArrayList<>();
if ("between".equals(condition.getTerm())) {
if (!StringUtils.equalsIgnoreCase(condition.getDynamicMaxField().getSummary(), "value")) {
list.add(of(condition.getDynamicMaxField()));
}
if (!StringUtils.equalsIgnoreCase(condition.getDynamicMinField().getSummary(), "value")) {
list.add(of(condition.getDynamicMinField()));
}
} else {
if (!StringUtils.equalsIgnoreCase(condition.getDynamicField().getSummary(), "value")) {
list.add(of(condition.getDynamicField()));
}
}
return list;
}
private ChartSeniorAssistDTO of(ThresholdDynamicFieldDTO dynamicField){
ChartSeniorAssistDTO conditionField = new ChartSeniorAssistDTO();
conditionField.setFieldId(Long.parseLong(dynamicField.getFieldId()));
conditionField.setValue(dynamicField.getSummary());
return conditionField;
}
protected String assistSQL(String sql, List<ChartViewFieldDTO> assistFields) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < assistFields.size(); i++) {

View File

@ -12,8 +12,9 @@ import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.util.ChartDataUtil;
import io.dataease.extensions.view.util.FieldUtil;
import io.dataease.utils.JsonUtil;
import io.dataease.utils.BeanUtils;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@ -21,6 +22,8 @@ import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Component
public class TableInfoHandler extends DefaultChartHandler {
@ -135,6 +138,32 @@ public class TableInfoHandler extends DefaultChartHandler {
calcResult.setContext(filterResult.getContext());
calcResult.setQuerySql(querySql);
calcResult.setOriginData(data);
try {
var dynamicAssistFields = getDynamicThresholdFields(view);
Set<Long> fieldIds = xAxis.stream().map(ChartViewFieldDTO::getId).collect(Collectors.toSet());
List<ChartViewFieldDTO> finalXAxis = xAxis;
dynamicAssistFields.forEach(i -> {
if (!fieldIds.contains(i.getFieldId())) {
ChartViewFieldDTO fieldDTO = new ChartViewFieldDTO();
BeanUtils.copyBean(fieldDTO, i.getCurField());
finalXAxis.add(fieldDTO);
}
});
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(dynamicAssistFields, yAxis, xAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(querySql, assistFields);
req.setQuery(assistSql);
logger.debug("calcite assistSql sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
calcResult.setAssistData(assistData);
calcResult.setDynamicAssistFields(dynamicAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
return calcResult;
}
}

View File

@ -1,9 +1,18 @@
package io.dataease.chart.charts.impl.table;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* @author jianneng
* @date 2024/9/11 11:37
@ -12,4 +21,29 @@ import org.springframework.stereotype.Component;
public class TableNormalHandler extends YoyChartHandler {
@Getter
private String type = "table-normal";
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
var result = (T) super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
try {
var originSql = result.getQuerySql();
var dynamicAssistFields = getDynamicThresholdFields(view);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(dynamicAssistFields, yAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(originSql, assistFields);
req.setQuery(assistSql);
logger.debug("calcite assistSql sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
result.setAssistData(assistData);
result.setDynamicAssistFields(dynamicAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}

View File

@ -17,6 +17,7 @@ import io.dataease.utils.BeanUtils;
import io.dataease.utils.IDUtils;
import io.dataease.utils.JsonUtil;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import reactor.util.function.Tuple2;
@ -35,6 +36,25 @@ public class TablePivotHandler extends GroupChartHandler {
T result = super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
Map<String, Object> customCalc = calcCustomExpr(view, filterResult, sqlMap, sqlMeta, provider);
result.getData().put("customCalc", customCalc);
try {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
var originSql = result.getQuerySql();
var dynamicAssistFields = getDynamicThresholdFields(view);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(dynamicAssistFields, yAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(originSql, assistFields);
req.setQuery(assistSql);
logger.debug("calcite assistSql sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
result.setAssistData(assistData);
result.setDynamicAssistFields(dynamicAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}

View File

@ -198,6 +198,28 @@ declare interface Threshold {
* url
*/
url: string
/**
* 类型固定值动态值
*/
type: 'fixed' | 'dynamic'
/**
* 动态值字段
*/
dynamicField: ThresholdDynamicField
/**
* 动态值最小值字段 仅当term为between时使用
*/
dynamicMinField: ThresholdDynamicField
/**
* 动态值最大值字段 仅当term为between时使用
*/
dynamicMaxField: ThresholdDynamicField
}
declare interface ThresholdDynamicField {
fieldId: string
summary: string
field: ChartViewField
}
/**

View File

@ -193,40 +193,62 @@ const changeTableThreshold = () => {
ElMessage.error(t('chart.exp_can_not_empty'))
return
}
if (ele.term === 'between') {
if (
!ele.term.includes('null') &&
!ele.term.includes('empty') &&
(ele.min === '' || ele.max === '')
) {
ElMessage.error(t('chart.value_can_not_empty'))
return
}
if (
(field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) &&
(parseFloat(ele.min).toString() === 'NaN' || parseFloat(ele.max).toString() === 'NaN')
) {
ElMessage.error(t('chart.value_error'))
return
}
if (
(field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) &&
parseFloat(ele.min) > parseFloat(ele.max)
) {
ElMessage.error(t('chart.value_min_max_invalid'))
return
if (ele.type !== 'dynamic') {
if (ele.term === 'between') {
if (
!ele.term.includes('null') &&
!ele.term.includes('empty') &&
(ele.min === '' || ele.max === '')
) {
ElMessage.error(t('chart.value_can_not_empty'))
return
}
if (
(field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) &&
(parseFloat(ele.min).toString() === 'NaN' || parseFloat(ele.max).toString() === 'NaN')
) {
ElMessage.error(t('chart.value_error'))
return
}
if (
(field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) &&
parseFloat(ele.min) > parseFloat(ele.max)
) {
ElMessage.error(t('chart.value_min_max_invalid'))
return
}
} else {
if (!ele.term.includes('null') && !ele.term.includes('empty') && ele.value === '') {
ElMessage.error(t('chart.value_can_not_empty'))
return
}
if (
(field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) &&
parseFloat(ele.value).toString() === 'NaN'
) {
ElMessage.error(t('chart.value_error'))
return
}
}
} else {
if (!ele.term.includes('null') && !ele.term.includes('empty') && ele.value === '') {
ElMessage.error(t('chart.value_can_not_empty'))
return
}
if (
(field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) &&
parseFloat(ele.value).toString() === 'NaN'
) {
ElMessage.error(t('chart.value_error'))
return
if (ele.term === 'between') {
if (
!ele.term.includes('null') &&
!ele.term.includes('empty') &&
(!ele.dynamicMinField?.fieldId || !ele.dynamicMaxField?.fieldId)
) {
ElMessage.error(t('chart.field_can_not_empty'))
return
}
} else {
if (
!ele.term.includes('null') &&
!ele.term.includes('empty') &&
!ele.dynamicField?.fieldId
) {
ElMessage.error(t('chart.field_can_not_empty'))
return
}
}
}
}
@ -235,7 +257,24 @@ const changeTableThreshold = () => {
changeThreshold()
closeTableThreshold()
}
const getFieldName = field => (field.chartShowName ? field.chartShowName : field.name)
const getDynamicStyleLabel = (item, fieldObj) => {
const handleSummary = field => {
if (!field?.field) {
return ''
}
if (field.summary === 'value') {
return getFieldName(field.field) + '(' + t('chart.field') + ')'
} else {
let suffix = field.summary === 'avg' ? t('chart.drag_block_label_value') : ''
return getFieldName(field.field) + '(' + t('chart.' + field.summary) + suffix + ')'
}
}
if (item.type === 'dynamic') {
return handleSummary(fieldObj)
}
}
init()
</script>
@ -519,17 +558,15 @@ init()
style="flex-direction: column"
>
<div class="field-style" :class="{ 'field-style-dark': themes === 'dark' }">
<span>
<el-icon>
<Icon :className="`field-icon-${fieldType[fieldItem.field.deType]}`"
><component
class="svg-icon"
:class="`field-icon-${fieldType[fieldItem.field.deType]}`"
:is="iconFieldMap[fieldType[fieldItem.field.deType]]"
></component
></Icon>
</el-icon>
</span>
<el-icon>
<Icon :className="`field-icon-${fieldType[fieldItem.field.deType]}`"
><component
class="svg-icon"
:class="`field-icon-${fieldType[fieldItem.field.deType]}`"
:is="iconFieldMap[fieldType[fieldItem.field.deType]]"
></component
></Icon>
</el-icon>
<span :title="fieldItem.field.name" class="field-text">{{
fieldItem.field.name
}}</span>
@ -577,7 +614,17 @@ init()
</span>
<span v-else-if="item.term === 'default'" title="默认"> 默认 </span>
</div>
<div style="flex: 1; margin: 0 8px">
<div v-if="item.type !== 'dynamic'" style="flex: 1; margin: 0 8px">
<span style="margin: 0 8px">
{{ t('chart.fix') }}
</span>
</div>
<div v-else style="flex: 1; margin: 0 8px">
<span style="margin: 0 8px">
{{ t('chart.dynamic') }}
</span>
</div>
<div v-if="item.type !== 'dynamic'" style="flex: 1; margin: 0 8px">
<span
v-if="
!item.term.includes('null') &&
@ -594,11 +641,44 @@ init()
!item.term.includes('empty') &&
item.term === 'between'
"
:title="item.min + ' ≤= ' + t('chart.drag_block_label_value') + ' ≤ ' + item.max"
>
{{ item.min }}&nbsp;{{ t('chart.drag_block_label_value') }}&nbsp;{{ item.max }}
</span>
<span v-else>&nbsp;</span>
</div>
<div v-else style="flex: 1; margin: 0 8px">
<span
v-if="
!item.term.includes('null') &&
!item.term.includes('default') &&
!item.term.includes('empty') &&
item.term !== 'between'
"
:title="getDynamicStyleLabel(item, item.dynamicField) + ''"
>
{{ getDynamicStyleLabel(item, item.dynamicField) }}</span
>
<span
v-else-if="
!item.term.includes('null') &&
!item.term.includes('empty') &&
item.term === 'between'
"
:title="
getDynamicStyleLabel(item, item.dynamicMinField) +
'≤' +
t('chart.drag_block_label_value') +
'≤' +
getDynamicStyleLabel(item, item.dynamicMaxField)
"
>
{{ getDynamicStyleLabel(item, item.dynamicMinField) }}{{
t('chart.drag_block_label_value')
}}{{ getDynamicStyleLabel(item, item.dynamicMaxField) }}
</span>
<span v-else>&nbsp;</span>
</div>
<template v-if="chart.type === 'picture-group'">
<div title="显示图片" class="pic-group-main">
<img
@ -688,7 +768,7 @@ init()
v-model="state.editTableThresholdDialog"
:title="t('chart.threshold')"
:visible="state.editTableThresholdDialog"
width="800px"
width="1050px"
class="dialog-css"
append-to-body
>
@ -826,12 +906,21 @@ span {
flex-direction: row;
align-items: center;
flex-wrap: nowrap;
:nth-child(1) {
width: 48px !important;
}
:nth-child(2) {
width: 40px !important;
}
:nth-child(3) {
width: 30px !important;
}
&:deep(span) {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
cursor: default;
display: block;
}
}
@ -868,9 +957,7 @@ span {
display: flex;
align-items: center;
justify-content: flex-start;
background: #f5f6f7;
&.field-style-dark {
background: #1a1a1a;
}

View File

@ -30,7 +30,11 @@ const thresholdCondition = {
color: '#ff0000ff',
backgroundColor: '#ffffff00',
min: '0',
max: '1'
max: '1',
type: 'fixed',
dynamicField: { summary: 'value' },
dynamicMinField: { summary: 'value' },
dynamicMaxField: { summary: 'value' }
}
const textOptions = [
{
@ -241,11 +245,108 @@ const addField = item => {
item.field = JSON.parse(JSON.stringify(ele))
initOptions(item)
}
if (item.dynamicField?.fieldId === ele.id) {
item.dynamicField.field = JSON.parse(JSON.stringify(ele))
initOptions(item)
}
if (item.dynamicMinField?.fieldId === ele.id) {
item.dynamicMinField.field = JSON.parse(JSON.stringify(ele))
initOptions(item)
}
if (item.dynamicMaxField?.fieldId === ele.id) {
item.dynamicMaxField.field = JSON.parse(JSON.stringify(ele))
initOptions(item)
}
})
}
changeThreshold()
}
const fieldOptions = [
{ label: t('chart.field_fixed'), value: 'fixed' },
{ label: t('chart.field_dynamic'), value: 'dynamic' }
]
const dynamicSummaryOptions = [
{
id: 'value',
name: t('chart.field') + t('chart.drag_block_label_value')
},
{
id: 'avg',
name: t('chart.avg') + t('chart.drag_block_label_value')
},
{
id: 'max',
name: t('chart.max')
},
{
id: 'min',
name: t('chart.min')
}
]
const getConditionsFields = (fieldItem, conditionItem, conditionItemField) => {
const fieldItemDeType = state.fields.filter(ele => ele.id === fieldItem.fieldId)?.[0]?.deType
if (!fieldItemDeType) {
fieldItem.fieldId = null
conditionItem.fieldId = null
conditionItemField.fieldId = null
changeThreshold()
return state.fields
}
const result = state.fields.filter(item => item.deType === fieldItemDeType) ?? []
if (!result.find(ele => ele.id === conditionItemField.fieldId)) {
conditionItemField.fieldId = result[0]?.id
addField(conditionItem)
}
return result
}
const getDynamicSummaryOptions = itemId => {
const deType = state.fields.filter(ele => ele.id === itemId)?.[0]?.deType
if (deType === 1) {
//
return dynamicSummaryOptions.filter(ele => {
return ele.id !== 'avg'
})
} else if (deType === 0 || deType === 5) {
//
return dynamicSummaryOptions.filter(ele => {
return ele.id === 'value'
})
} else {
return dynamicSummaryOptions
}
}
const isNotEmptyAndNull = item => {
return !item.term.includes('null') && !item.term.includes('empty')
}
const isBetween = item => {
return item.term === 'between'
}
const isDynamic = item => {
return item.type === 'dynamic'
}
const changeConditionItemType = item => {
if (item.type === 'dynamic') {
item.dynamicField.summary = 'value'
item.dynamicMinField.summary = 'value'
item.dynamicMaxField.summary = 'value'
}
changeThreshold()
}
const getFieldOptions = fieldItem => {
const deType = state.fields.filter(ele => ele.id === fieldItem.fieldId)?.[0]?.deType
if (deType === 1) {
return fieldOptions.filter(ele => ele.value === 'fixed')
} else {
return fieldOptions
}
}
init()
</script>
@ -310,9 +411,9 @@ init()
v-for="(item, index) in fieldItem.conditions"
:key="index"
class="line-item"
:gutter="10"
:gutter="12"
>
<el-col :span="4">
<el-col :span="3">
<el-form-item class="form-item">
<el-select v-model="item.term" @change="changeThreshold">
<el-option-group
@ -330,13 +431,27 @@ init()
</el-select>
</el-form-item>
</el-col>
<el-col :span="2" v-if="isNotEmptyAndNull(item)" style="padding-left: 0 !important">
<el-form-item class="form-item">
<el-select
v-model="item.type"
class="select-item"
@change="changeConditionItemType(item)"
style="width: 100%"
>
<el-option
v-for="opt in getFieldOptions(fieldItem)"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</el-form-item>
</el-col>
<!--不是between 不是动态值-->
<el-col
v-if="
!item.term.includes('null') &&
!item.term.includes('empty') &&
item.term !== 'between'
"
:span="10"
v-if="isNotEmptyAndNull(item) && !isBetween(item) && !isDynamic(item)"
:span="12"
style="text-align: center"
>
<el-form-item class="form-item">
@ -359,8 +474,72 @@ init()
/>
</el-form-item>
</el-col>
<el-col v-if="item.term === 'between'" :span="4" style="text-align: center">
<!--不是between 是动态值-->
<!--动态值 字段-->
<el-col v-if="isNotEmptyAndNull(item) && !isBetween(item) && isDynamic(item)" :span="6">
<el-form-item class="form-item">
<el-select
v-model="item.dynamicField.fieldId"
@change="addField(item)"
style="width: 100%"
>
<el-option
class="series-select-option"
v-for="itemFieldOption in getConditionsFields(
fieldItem,
item,
item.dynamicField
)"
:key="itemFieldOption.id"
:label="itemFieldOption.name"
:value="itemFieldOption.id"
:disabled="chart.type === 'table-info' && itemFieldOption.deType === 7"
>
<el-icon style="margin-right: 8px">
<Icon
><component
:class="`field-icon-${
fieldType[[2, 3].includes(itemFieldOption.deType) ? 2 : 0]
}`"
class="svg-icon"
:is="iconFieldMap[fieldType[itemFieldOption.deType]]"
></component
></Icon>
</el-icon>
{{ itemFieldOption.name }}
</el-option>
</el-select>
</el-form-item>
</el-col>
<!--动态值聚合方式-->
<el-col
v-if="isNotEmptyAndNull(item) && !isBetween(item) && isDynamic(item)"
:span="6"
style="text-align: center"
>
<el-form-item class="form-item">
<el-select
:placeholder="t('chart.aggregation')"
v-model="item.dynamicField.summary"
@change="changeThreshold"
style="width: 100%"
>
<el-option
v-for="opt in getDynamicSummaryOptions(item.dynamicField.fieldId)"
:key="opt.id"
:label="opt.name"
:value="opt.id"
/>
</el-select>
</el-form-item>
</el-col>
<!--是between 不是动态值-->
<!--between 开始值-->
<el-col
v-if="isNotEmptyAndNull(item) && isBetween(item) && !isDynamic(item)"
:span="5"
style="text-align: center"
>
<el-form-item class="form-item">
<el-input-number
v-model="item.min"
@ -372,14 +551,21 @@ init()
/>
</el-form-item>
</el-col>
<el-col v-if="item.term === 'between'" :span="2" style="text-align: center">
<span style="margin: 0 4px">
&nbsp;&nbsp;{{ t('chart.drag_block_label_value') }}&nbsp;&nbsp;
<el-col
v-if="isBetween(item) && !isDynamic(item)"
:span="2"
style="margin-top: 4px; text-align: center"
>
<span style="margin: 0 -5px">
&nbsp;{{ t('chart.drag_block_label_value') }}&nbsp;
</span>
</el-col>
<el-col v-if="item.term === 'between'" :span="4" style="text-align: center">
<!--between 结束值-->
<el-col
v-if="isNotEmptyAndNull(item) && isBetween(item) && !isDynamic(item)"
:span="5"
style="text-align: center"
>
<el-form-item class="form-item">
<el-input-number
v-model="item.max"
@ -392,11 +578,127 @@ init()
</el-form-item>
</el-col>
<div
style="display: flex; align-items: center; justify-content: center; margin-left: 8px"
<!--是between 动态值-->
<!--开始值 动态值字段-->
<el-col
v-if="isNotEmptyAndNull(item) && isBetween(item) && isDynamic(item)"
class="minField"
:span="3"
>
<div class="color-title">{{ t('chart.textColor') }}</div>
<el-form-item class="form-item">
<el-select v-model="item.dynamicMinField.fieldId" @change="addField(item)">
<el-option
class="series-select-option"
v-for="itemFieldOption in getConditionsFields(
fieldItem,
item,
item.dynamicMinField
)"
:key="itemFieldOption.id"
:label="itemFieldOption.name"
:value="itemFieldOption.id"
:disabled="chart.type === 'table-info' && itemFieldOption.deType === 7"
>
<el-icon style="margin-right: 8px">
<Icon
><component
:class="`field-icon-${
fieldType[[2, 3].includes(itemFieldOption.deType) ? 2 : 0]
}`"
class="svg-icon"
:is="iconFieldMap[fieldType[itemFieldOption.deType]]"
></component
></Icon>
</el-icon>
{{ itemFieldOption.name }}
</el-option>
</el-select>
</el-form-item>
</el-col>
<!--开始值 动态值聚合方式-->
<el-col
v-if="isNotEmptyAndNull(item) && isBetween(item) && isDynamic(item)"
class="minValue"
:span="2"
style="padding-left: 0 !important"
>
<el-form-item class="form-item">
<el-select v-model="item.dynamicMinField.summary" @change="changeThreshold">
<el-option
v-for="opt in getDynamicSummaryOptions(item.dynamicMinField.fieldId)"
:key="opt.id"
:label="opt.name"
:value="opt.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col
v-if="isBetween(item) && isDynamic(item)"
class="term"
:span="2"
style="margin-top: 4px; text-align: center"
>
<span style="margin: 0 -5px">
&nbsp;{{ t('chart.drag_block_label_value') }}&nbsp;
</span>
</el-col>
<!--结束值 动态值字段-->
<el-col
v-if="isNotEmptyAndNull(item) && isBetween(item) && isDynamic(item)"
class="maxField"
:span="3"
>
<el-form-item class="form-item">
<el-select v-model="item.dynamicMaxField.fieldId" @change="addField(item)">
<el-option
class="series-select-option"
v-for="itemFieldOption in getConditionsFields(
fieldItem,
item,
item.dynamicMaxField
)"
:key="itemFieldOption.id"
:label="itemFieldOption.name"
:value="itemFieldOption.id"
:disabled="chart.type === 'table-info' && itemFieldOption.deType === 7"
>
<el-icon style="margin-right: 8px">
<Icon
><component
:class="`field-icon-${
fieldType[[2, 3].includes(itemFieldOption.deType) ? 2 : 0]
}`"
class="svg-icon"
:is="iconFieldMap[fieldType[itemFieldOption.deType]]"
></component
></Icon>
</el-icon>
{{ itemFieldOption.name }}
</el-option>
</el-select>
</el-form-item>
</el-col>
<!--结束值 动态值聚合方式-->
<el-col
v-if="isNotEmptyAndNull(item) && isBetween(item) && isDynamic(item)"
class="maxValue"
:span="2"
style="padding-left: 0 !important"
>
<el-form-item class="form-item">
<el-select v-model="item.dynamicMaxField.summary" @change="changeThreshold">
<el-option
v-for="opt in getDynamicSummaryOptions(item.dynamicMaxField.fieldId)"
:key="opt.id"
:label="opt.name"
:value="opt.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item class="form-item" :label="t('chart.textColor')">
<el-color-picker
is-custom
size="large"
@ -407,12 +709,9 @@ init()
@change="changeThreshold"
/>
</el-form-item>
</div>
<div
style="display: flex; align-items: center; justify-content: center; margin-left: 8px"
>
<div class="color-title">{{ t('chart.backgroundColor') }}</div>
<el-form-item class="form-item">
</el-col>
<el-col :span="3">
<el-form-item class="form-item" :label="t('chart.backgroundColor')">
<el-color-picker
is-custom
size="large"
@ -423,22 +722,22 @@ init()
@change="changeThreshold"
/>
</el-form-item>
</div>
<div
style="display: flex; align-items: center; justify-content: center; margin-left: 8px"
>
<el-button
class="circle-button m-icon-btn"
text
@click="removeCondition(fieldItem, index)"
>
<el-icon size="20px" style="color: #646a73">
<Icon name="icon_delete-trash_outlined"
><icon_deleteTrash_outlined class="svg-icon"
/></Icon>
</el-icon>
</el-button>
</div>
</el-col>
<el-col :span="1">
<div style="display: flex; align-items: center; justify-content: center">
<el-button
class="circle-button m-icon-btn"
text
@click="removeCondition(fieldItem, index)"
>
<el-icon size="20px" style="color: #646a73">
<Icon name="icon_delete-trash_outlined"
><icon_deleteTrash_outlined class="svg-icon"
/></Icon>
</el-icon>
</el-button>
</div>
</el-col>
</el-row>
</el-row>

View File

@ -1074,7 +1074,19 @@ const onAssistLineChange = val => {
const onThresholdChange = val => {
view.value.senior.threshold = val
renderChart(view.value)
let type = undefined
view.value.senior.threshold?.tableThreshold?.some(item => {
if (item.conditions.some(i => i.type === 'dynamic')) {
type = 'calcData'
return true
}
return false
})
if (type) {
calcData(view.value)
} else {
renderChart(view.value)
}
}
const onMapMappingChange = val => {

View File

@ -520,6 +520,7 @@ export function getConditions(chart: Chart) {
const headerValueBgColor = isAlphaColor(tableHeader.tableHeaderBgColor)
? tableHeader.tableHeaderBgColor
: hexColorToRGBA(tableHeader.tableHeaderBgColor, basicStyle.alpha)
const filedValueMap = getFieldValueMap(chart)
for (let i = 0; i < conditions.length; i++) {
const field = conditions[i]
let defaultValueColor = valueColor
@ -541,7 +542,7 @@ export function getConditions(chart: Chart) {
return null
}
return {
fill: mappingColor(value, defaultValueColor, field, 'color')
fill: mappingColor(value, defaultValueColor, field, 'color', filedValueMap, rowData)
}
}
})
@ -554,7 +555,14 @@ export function getConditions(chart: Chart) {
if (rowData?.id && rowData?.field === rowData.id) {
return null
}
const fill = mappingColor(value, defaultBgColor, field, 'backgroundColor')
const fill = mappingColor(
value,
defaultBgColor,
field,
'backgroundColor',
filedValueMap,
rowData
)
if (isTransparent(fill)) {
return null
}
@ -566,13 +574,28 @@ export function getConditions(chart: Chart) {
return res
}
export function mappingColor(value, defaultColor, field, type) {
export function mappingColor(value, defaultColor, field, type, filedValueMap, rowData) {
let color
for (let i = 0; i < field.conditions.length; i++) {
let flag = false
const t = field.conditions[i]
if (field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) {
const tv = parseFloat(t.value)
let tv, max, min
if (t.type === 'dynamic') {
if (t.term === 'between') {
max = parseFloat(getValue(t.dynamicMaxField, filedValueMap, rowData))
min = parseFloat(getValue(t.dynamicMinField, filedValueMap, rowData))
} else {
tv = parseFloat(getValue(t.dynamicField, filedValueMap, rowData))
}
} else {
if (t.term === 'between') {
min = parseFloat(t.min)
max = parseFloat(t.max)
} else {
tv = parseFloat(t.value)
}
}
if (t.term === 'eq') {
if (value === tv) {
color = t[type]
@ -604,8 +627,6 @@ export function mappingColor(value, defaultColor, field, type) {
flag = true
}
} else if (t.term === 'between') {
const min = parseFloat(t.min)
const max = parseFloat(t.max)
if (min <= value && value <= max) {
color = t[type]
flag = true
@ -708,6 +729,24 @@ export function mappingColor(value, defaultColor, field, type) {
return color
}
function getFieldValueMap(view) {
const fieldValueMap = {}
if (view.data && view.data.dynamicAssistLines && view.data.dynamicAssistLines.length > 0) {
view.data.dynamicAssistLines.forEach(ele => {
fieldValueMap[ele.summary + '-' + ele.fieldId] = ele.value
})
}
return fieldValueMap
}
function getValue(field, filedValueMap, rowData) {
if (field.summary === 'value') {
return rowData ? rowData[field.field?.dataeaseName] : undefined
} else {
return filedValueMap[field.summary + '-' + field.fieldId]
}
}
export function handleTableEmptyStrategy(chart: Chart) {
let newData = (chart.data?.tableRow || []) as Record<string, any>[]
let intersectionArr = []

View File

@ -0,0 +1,19 @@
package io.dataease.extensions.view.dto;
import lombok.Data;
import java.util.List;
@Data
public class ChartSeniorThresholdCfgDTO {
/**
* 是否启用
*/
private boolean enable;
/**
* 表格阈值
*/
private List<TableThresholdDTO> tableThreshold;
}

View File

@ -0,0 +1,31 @@
package io.dataease.extensions.view.dto;
import lombok.Data;
/**
* @author jianneng
* @date 2024/9/19 18:34
**/
@Data
public class ChartSeniorThresholdDTO {
/**
* 对比方式
*/
private String term;
/**
* 类型固定值动态值
*/
private String type;
/**
* 动态值字段
*/
private ThresholdDynamicFieldDTO dynamicField;
/**
* 动态值最小值字段 仅当term为between时使用
*/
private ThresholdDynamicFieldDTO dynamicMinField;
/**
* 动态值最大值字段 仅当term为between时使用
*/
private ThresholdDynamicFieldDTO dynamicMaxField;
}

View File

@ -0,0 +1,25 @@
package io.dataease.extensions.view.dto;
import lombok.Data;
import java.util.List;
/**
* @author jianneng
* @date 2024/9/19 18:31
**/
@Data
public class TableThresholdDTO {
/**
* 字段id
*/
private String fieldId;
/**
* 字段
*/
private ChartViewFieldDTO field;
/**
* 条件
*/
private List<ChartSeniorThresholdDTO> conditions;
}

View File

@ -0,0 +1,23 @@
package io.dataease.extensions.view.dto;
import lombok.Data;
/**
* @author jianneng
* @date 2024/9/19 18:31
**/
@Data
public class ThresholdDynamicFieldDTO {
/**
* 字段id
*/
private String fieldId;
/**
* 字段
*/
private ChartViewFieldDTO field;
/**
* 条件
*/
private String summary;
}