feat(图表): 支持设置字段排序优先级 #12454 #12551

This commit is contained in:
wisonic
2024-12-13 14:58:34 +08:00
committed by wisonic-s
parent 590b995cde
commit 2a0c7df773
26 changed files with 494 additions and 111 deletions

View File

@@ -121,7 +121,7 @@ public class DefaultChartHandler extends AbstractChartPlugin {
logger.debug("calcite chart sql: " + querySql);
List<String[]> data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
//自定义排序
data = ChartDataUtil.resultCustomSort(xAxis, data);
data = ChartDataUtil.resultCustomSort(xAxis, yAxis, view.getSortPriority(), data);
//快速计算
quickCalc(xAxis, yAxis, data);
//数据重组逻辑可重载

View File

@@ -107,7 +107,7 @@ public class SymbolicMapHandler extends GroupChartHandler {
detailData = (List<String[]>) provider.fetchResultField(datasourceRequest1).get("data");
}
//自定义排序
data = ChartDataUtil.resultCustomSort(xAxis, data);
data = ChartDataUtil.resultCustomSort(xAxis, yAxis, view.getSortPriority(), data);
//数据重组逻辑可重载
var result = customBuildResult(view, formatResult, filterResult, data, detailFields, detailData);
T calcResult = (T) new ChartCalcDataResult();

View File

@@ -74,7 +74,7 @@ public class TableHeatmapHandler extends DefaultChartHandler {
logger.debug("calcite chart sql: " + querySql);
List<String[]> data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
//自定义排序
data = ChartDataUtil.resultCustomSort(xAxis, data);
data = ChartDataUtil.resultCustomSort(xAxis, yAxis, view.getSortPriority(), data);
//数据重组逻辑可重载
var result = this.buildResult(view, formatResult, filterResult, data);
T calcResult = (T) new ChartCalcDataResult();

View File

@@ -19,10 +19,7 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
@Component
@@ -129,7 +126,7 @@ public class TableInfoHandler extends DefaultChartHandler {
logger.debug("calcite chart sql: " + querySql);
List<String[]> data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
//自定义排序
data = ChartDataUtil.resultCustomSort(xAxis, data);
data = ChartDataUtil.resultCustomSort(xAxis, Collections.emptyList(), view.getSortPriority(), data);
//数据重组逻辑可重载
var result = this.buildResult(view, formatResult, filterResult, data);
T calcResult = (T) new ChartCalcDataResult();

View File

@@ -9,7 +9,7 @@ import java.io.Serializable;
* </p>
*
* @author fit2cloud
* @since 2024-10-23
* @since 2024-12-12
*/
@TableName("core_chart_view")
public class CoreChartView implements Serializable {
@@ -101,21 +101,11 @@ public class CoreChartView implements Serializable {
*/
private String customAttr;
/**
* 图形属性_移动端
*/
private String customAttrMobile;
/**
* 组件样式
*/
private String customStyle;
/**
* 组件样式_移动端
*/
private String customStyleMobile;
/**
* 结果过滤
*/
@@ -231,6 +221,21 @@ public class CoreChartView implements Serializable {
*/
private String extColor;
/**
* 图形属性_移动端
*/
private String customAttrMobile;
/**
* 组件样式_移动端
*/
private String customStyleMobile;
/**
* 字段排序优先级
*/
private String sortPriority;
public Long getId() {
return id;
}
@@ -367,14 +372,6 @@ public class CoreChartView implements Serializable {
this.customAttr = customAttr;
}
public String getCustomAttrMobile() {
return customAttrMobile;
}
public void setCustomAttrMobile(String customAttrMobile) {
this.customAttrMobile = customAttrMobile;
}
public String getCustomStyle() {
return customStyle;
}
@@ -383,14 +380,6 @@ public class CoreChartView implements Serializable {
this.customStyle = customStyle;
}
public String getCustomStyleMobile() {
return customStyleMobile;
}
public void setCustomStyleMobile(String customStyleMobile) {
this.customStyleMobile = customStyleMobile;
}
public String getCustomFilter() {
return customFilter;
}
@@ -575,6 +564,30 @@ public class CoreChartView implements Serializable {
this.extColor = extColor;
}
public String getCustomAttrMobile() {
return customAttrMobile;
}
public void setCustomAttrMobile(String customAttrMobile) {
this.customAttrMobile = customAttrMobile;
}
public String getCustomStyleMobile() {
return customStyleMobile;
}
public void setCustomStyleMobile(String customStyleMobile) {
this.customStyleMobile = customStyleMobile;
}
public String getSortPriority() {
return sortPriority;
}
public void setSortPriority(String sortPriority) {
this.sortPriority = sortPriority;
}
@Override
public String toString() {
return "CoreChartView{" +
@@ -595,9 +608,7 @@ public class CoreChartView implements Serializable {
", extLabel = " + extLabel +
", extTooltip = " + extTooltip +
", customAttr = " + customAttr +
", customAttrMobile = " + customAttrMobile +
", customStyle = " + customStyle +
", customStyleMobile = " + customStyleMobile +
", customFilter = " + customFilter +
", drillFields = " + drillFields +
", senior = " + senior +
@@ -621,6 +632,9 @@ public class CoreChartView implements Serializable {
", flowMapStartName = " + flowMapStartName +
", flowMapEndName = " + flowMapEndName +
", extColor = " + extColor +
", customAttrMobile = " + customAttrMobile +
", customStyleMobile = " + customStyleMobile +
", sortPriority = " + sortPriority +
"}";
}
}

View File

@@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Mapper;
* </p>
*
* @author fit2cloud
* @since 2024-10-23
* @since 2024-12-12
*/
@Mapper
public interface CoreChartViewMapper extends BaseMapper<CoreChartView> {

View File

@@ -4,6 +4,8 @@ import io.dataease.engine.constant.SQLConstants;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.SortAxis;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.stringtemplate.v4.ST;
@@ -11,6 +13,7 @@ import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.STGroupString;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
@@ -116,6 +119,25 @@ public class SQLProvider {
List<SQLObj> orders = new ArrayList<>();
if (ObjectUtils.isNotEmpty(xOrders)) orders.addAll(xOrders);
if (ObjectUtils.isNotEmpty(yOrders)) orders.addAll(yOrders);
if (!orders.isEmpty() && CollectionUtils.isNotEmpty(view.getSortPriority())) {
var sortPriority = view.getSortPriority();
var tmp = new ArrayList<SQLObj>();
var ids = new HashSet<Long>();
for (SortAxis sortAxis : sortPriority) {
for (SQLObj order : orders) {
if (sortAxis.getId().equals(order.getId())){
tmp.add(order);
ids.add(order.getId());
}
}
}
for (SQLObj order : orders) {
if (!ids.contains(order.getId())) {
tmp.add(order);
}
}
orders = tmp;
}
// check datasource 是否需要排序
if (needOrder && ObjectUtils.isEmpty(orders)) {
if (ObjectUtils.isNotEmpty(xFields) || ObjectUtils.isNotEmpty(yFields)) {

View File

@@ -76,6 +76,7 @@ public class Dimension2SQLObj {
.orderField(originField)
.orderAlias(fieldAlias)
.orderDirection(x.getSort())
.id(x.getId())
.build());
}
}

View File

@@ -85,6 +85,7 @@ public class Quota2SQLObj {
.orderField(originField)
.orderAlias(fieldAlias)
.orderDirection(y.getSort())
.id(y.getId())
.build());
}
}

View File

@@ -2,3 +2,4 @@ UPDATE `visualization_background` SET `name` = 'Board10' WHERE `id` = 'dark_1';
UPDATE `visualization_subject` SET `name` = 'chart.light_theme' WHERE `id` = '10001';
UPDATE `visualization_subject` SET `name` = 'chart.dark_theme' WHERE `id` = '10002';
CREATE INDEX idx_dataset_table_task_log_A ON core_datasource_task_log(ds_id, table_name, start_time);
ALTER TABLE core_chart_view ADD sort_priority longtext null comment '字段排序优先级';

View File

@@ -0,0 +1 @@
<svg t="1733888045901" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4579" width="200" height="200"><path d="M474.87 408.26v26.88l25.31-18.67V566.8h25.31V389.59h-25.31l-25.31 18.67zM294.69 606.57a36.74 36.74 0 0 0 2.48-8 65.81 65.81 0 0 0 1-8.34c0.16-2.9 0.25-6.51 0.25-10.82a65.87 65.87 0 0 0-2.11-16.92 42.84 42.84 0 0 0-6.57-14.18 33.85 33.85 0 0 0-11.78-9.83 37.07 37.07 0 0 0-17-3.61 36 36 0 0 0-14.9 3.13 37.24 37.24 0 0 0-11.91 8.33 38.62 38.62 0 0 0-8.18 12.2 37.54 37.54 0 0 0-3 14.93v13.94h25.33V573q0-6.22 3.35-9.45t9.06-3.24c3.14 0 5.54 0.63 7.19 1.86a11.85 11.85 0 0 1 3.72 4.61 16.8 16.8 0 0 1 1.36 6.34q0.13 3.61 0.13 6.84 0 7.22-0.49 12.07a31.78 31.78 0 0 1-4 11.33L223 689.7v23.89h75.43V688.2h-45.66L290 616.27q3-5.71 4.72-9.7M797.63 687.7a31.28 31.28 0 0 0 1.86-9.45q0.37-5.73 0.37-14.94 0-16.18-2.23-24.39a29 29 0 0 0-3.1-7.22 54.2 54.2 0 0 0-4.34-6 39.82 39.82 0 0 0-10.3-8.34q-6.82-4.1-17.24-4.1a37.31 37.31 0 0 0-14.88 3 40.71 40.71 0 0 0-12.16 8 35.68 35.68 0 0 0-8.19 11.94 36.94 36.94 0 0 0-3 14.68v14.69h25.3v-14.45a14.73 14.73 0 0 1 2.86-8.22q2.86-4.23 9.55-4.23a12 12 0 0 1 9.06 3.49 12.39 12.39 0 0 1 3.35 9V674q0 10-4.34 12.69t-15.75 2.74v22.4a81.93 81.93 0 0 1 10 0.49 11 11 0 0 1 6.08 2.49q4 3.23 4 13.69v25.4q0 6.48-3.35 10.33a12.57 12.57 0 0 1-18.11 0 14.45 14.45 0 0 1-3.35-9.83v-13.19h-25.3v14.44q0 10.45 3.6 17.67a35.08 35.08 0 0 0 9.06 11.7 34.38 34.38 0 0 0 12.16 6.47 44.32 44.32 0 0 0 12.66 2q11.91 0 19.23-4.61a40 40 0 0 0 12-11.57 40.83 40.83 0 0 0 3.23-5.72 29.61 29.61 0 0 0 2-6.72 73.78 73.78 0 0 0 1.11-9.46q0.37-5.72 0.37-14.43 0-9.46-0.25-15.56a38.6 38.6 0 0 0-1.74-10.57 20.42 20.42 0 0 0-4.59-7.46 48.4 48.4 0 0 0-9.06-7 57.48 57.48 0 0 0 8.56-6 18.85 18.85 0 0 0 4.84-6.72" fill="#231F20" p-id="4580"></path><path d="M825.86 444.52h-129a83.55 83.55 0 0 0-36 8.1v-304C660.87 102 623 64 576.51 64h-129c-46.51 0-84.36 38-84.36 84.63v143.61a83.53 83.53 0 0 0-36-8.1h-129c-46.52 0-84.36 38-84.36 84.62v506.62c0 46.66 37.84 84.62 84.36 84.62h129a84 84 0 0 0 60.17-25.38A84 84 0 0 0 447.49 960h129a84 84 0 0 0 60.17-25.38A84 84 0 0 0 696.84 960h129c46.52 0 84.36-38 84.36-84.62V529.14c0.02-46.66-37.82-84.62-84.34-84.62z m-464 430.86a34.83 34.83 0 0 1-34.74 34.85h-129a34.83 34.83 0 0 1-34.74-34.85V368.76a34.83 34.83 0 0 1 34.74-34.84h129a34.83 34.83 0 0 1 34.74 34.84z m249.36 0a34.83 34.83 0 0 1-34.74 34.85h-129a34.83 34.83 0 0 1-34.74-34.85V148.63a34.84 34.84 0 0 1 34.74-34.85h129a34.84 34.84 0 0 1 34.74 34.85z m249.35 0a34.83 34.83 0 0 1-34.74 34.85h-129a34.83 34.83 0 0 1-34.74-34.85V529.14a34.83 34.83 0 0 1 34.74-34.84h129a34.83 34.83 0 0 1 34.74 34.84z" p-id="4581"></path></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -1917,7 +1917,9 @@ Scatter chart (bubble) chart: {a} (series name), {b} (data name), {c} (value arr
'When Customizing, Supports SVG, JPG, JPEG, and PNG files up to 1MB',
size_range: 'Size Range',
x_axis_constant_line: 'X-axis Constant Line',
y_axis_constant_line: 'Y-axis Constant Line'
y_axis_constant_line: 'Y-axis Constant Line',
sort_priority: 'Sort Priority Setting',
sort_priority_tip: 'Top-down, sorting priority from highest to lowest'
},
dataset: {
scope_edit: 'Only effective when editing',

View File

@@ -1873,7 +1873,9 @@ export default {
symbolic_map_symbol_shape_tip: '自訂時, 支援 1MB 以內的 SVG, JPG, JPEG, PNG 檔案',
size_range: '大小區間',
x_axis_constant_line: 'X 軸恆線',
y_axis_constant_line: 'Y 軸恆線'
y_axis_constant_line: 'Y 軸恆線',
sort_priority: '排序優先級設置',
sort_priority_tip: '自上而下,排序優先級從高到低'
},
dataset: {
scope_edit: '僅編輯時生效',

View File

@@ -1875,7 +1875,9 @@ export default {
symbolic_map_symbol_shape_tip: '自定义时, 支持 1MB 以内的 SVG, JPG, JPEG, PNG 文件',
size_range: '大小区间',
x_axis_constant_line: 'X 轴恒线',
y_axis_constant_line: 'Y 轴恒线'
y_axis_constant_line: 'Y 轴恒线',
sort_priority: '排序优先级设置',
sort_priority_tip: '自上而下,排序优先级从高到低'
},
dataset: {
scope_edit: '仅编辑时生效',

View File

@@ -50,6 +50,7 @@ declare interface Chart {
customStyleMobile: CustomStyle
drillFields: ChartViewField[]
drillFilters: Filter[]
sortPriority: ChartViewField[]
datasetMode: 0 | 1
datasourceType: string
totalItems: number

View File

@@ -7,6 +7,7 @@ import icon_down_outlined1 from '@/assets/svg/icon_down_outlined-1.svg'
import icon_right_outlined from '@/assets/svg/icon_right_outlined.svg'
import icon_done_outlined from '@/assets/svg/icon_done_outlined.svg'
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
import icon_sort_priority from '@/assets/svg/icon_sort_priority.svg'
import { useI18n } from '@/hooks/web/useI18n'
import { computed, onMounted, ref, toRefs, watch } from 'vue'
import { getItemType } from '@/views/chart/components/editor/drag-item/utils'
@@ -59,7 +60,8 @@ const emit = defineEmits([
'onDimensionItemChange',
'onNameEdit',
'valueFormatter',
'onToggleHide'
'onToggleHide',
'editSortPriority'
])
const { item } = toRefs(props)
@@ -101,6 +103,9 @@ const clickItem = param => {
case 'toggleHide':
toggleHide()
break
case 'sortPriority':
emit('editSortPriority')
break
default:
break
}
@@ -258,16 +263,18 @@ onMounted(() => {
<el-tooltip :effect="toolTip" placement="top">
<template #content>
<table>
<tr>
<td>{{ t('dataset.field_origin_name') }}</td>
<td>:</td>
<td>{{ item.name }}</td>
</tr>
<tr>
<td>{{ t('chart.show_name') }}</td>
<td>:</td>
<td>{{ item.chartShowName ? item.chartShowName : item.name }}</td>
</tr>
<tbody>
<tr>
<td>{{ t('dataset.field_origin_name') }}</td>
<td>:</td>
<td>{{ item.name }}</td>
</tr>
<tr>
<td>{{ t('chart.show_name') }}</td>
<td>:</td>
<td>{{ item.chartShowName ? item.chartShowName : item.name }}</td>
</tr>
</tbody>
</table>
</template>
<span class="item-span-style">
@@ -383,7 +390,14 @@ onMounted(() => {
</template>
</el-dropdown>
</el-dropdown-item>
<el-dropdown-item
v-if="showSort()"
:icon="icon_sort_priority"
:command="beforeClickItem('sortPriority')"
class="menu-item-padding"
>
<span>{{ t('chart.sort_priority') }}</span>
</el-dropdown-item>
<el-dropdown-item
@click.prevent
v-if="item.deType === 1"

View File

@@ -7,6 +7,7 @@ import icon_sort_outlined from '@/assets/svg/icon_sort_outlined.svg'
import icon_right_outlined from '@/assets/svg/icon_right_outlined.svg'
import icon_done_outlined from '@/assets/svg/icon_done_outlined.svg'
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
import icon_sort_priority from '@/assets/svg/icon_sort_priority.svg'
import { useI18n } from '@/hooks/web/useI18n'
import { onMounted, ref, toRefs, watch } from 'vue'
import { getItemType } from '@/views/chart/components/editor/drag-item/utils'
@@ -52,7 +53,8 @@ const emit = defineEmits([
'onDimensionItemRemove',
'onCustomSort',
'onDimensionItemChange',
'onNameEdit'
'onNameEdit',
'editSortPriority'
])
const { item } = toRefs(props)
@@ -76,6 +78,9 @@ const clickItem = param => {
case 'remove':
removeItem()
break
case 'sortPriority':
emit('editSortPriority')
break
default:
break
}
@@ -163,16 +168,18 @@ onMounted(() => {
<el-tooltip :effect="themes === 'dark' ? 'ndark' : 'dark'" placement="top">
<template #content>
<table>
<tr>
<td>{{ t('dataset.field_origin_name') }}</td>
<td>:</td>
<td>{{ item.name }}</td>
</tr>
<tr>
<td>{{ t('chart.show_name') }}</td>
<td>:</td>
<td>{{ item.chartShowName ? item.chartShowName : item.name }}</td>
</tr>
<tbody>
<tr>
<td>{{ t('dataset.field_origin_name') }}</td>
<td>:</td>
<td>{{ item.name }}</td>
</tr>
<tr>
<td>{{ t('chart.show_name') }}</td>
<td>:</td>
<td>{{ item.chartShowName ? item.chartShowName : item.name }}</td>
</tr>
</tbody>
</table>
</template>
<span class="item-span-style">
@@ -279,6 +286,14 @@ onMounted(() => {
</template>
</el-dropdown>
</el-dropdown-item>
<el-dropdown-item
v-if="index !== 0"
:icon="icon_sort_priority"
:command="beforeClickItem('sortPriority')"
class="menu-item-padding"
>
<span>{{ t('chart.sort_priority') }}</span>
</el-dropdown-item>
<el-dropdown-item class="menu-item-padding" :command="beforeClickItem('rename')">
<el-icon>
<icon name="icon_edit_outlined"><icon_edit_outlined class="svg-icon" /></icon>

View File

@@ -9,6 +9,7 @@ import icon_right_outlined from '@/assets/svg/icon_right_outlined.svg'
import icon_done_outlined from '@/assets/svg/icon_done_outlined.svg'
import icon_functions_outlined from '@/assets/svg/icon_functions_outlined.svg'
import icon_describe_outlined from '@/assets/svg/icon_describe_outlined.svg'
import icon_sort_priority from '@/assets/svg/icon_sort_priority.svg'
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
import { useI18n } from '@/hooks/web/useI18n'
import { computed, onMounted, reactive, ref, toRefs, watch } from 'vue'
@@ -73,7 +74,8 @@ const emit = defineEmits([
'editItemFilter',
'editItemCompare',
'valueFormatter',
'onToggleHide'
'onToggleHide',
'editSortPriority'
])
const { item, chart } = toRefs(props)
@@ -166,6 +168,9 @@ const clickItem = param => {
case 'toggleHide':
toggleHide()
break
case 'sortPriority':
emit('editSortPriority')
break
default:
break
}
@@ -298,6 +303,16 @@ const toggleHide = () => {
const showHideIcon = computed(() => {
return ['tale-info', 'table-normal'].includes(props.chart.type) && item.value.hide
})
const showSort = computed(() => {
return (
props.type !== 'extLabel' &&
props.type !== 'extTooltip' &&
props.type !== 'extBubble' &&
!['chart-mix', 'indicator', 'liquid', 'gauge'].includes(chart.value.type)
)
})
onMounted(() => {
isEnableCompare()
getItemTagType()
@@ -339,16 +354,18 @@ onMounted(() => {
<el-tooltip :effect="toolTip" placement="top">
<template #content>
<table>
<tr>
<td>{{ t('dataset.field_origin_name') }}</td>
<td>:</td>
<td>{{ item.name }}</td>
</tr>
<tr>
<td>{{ t('chart.show_name') }}</td>
<td>:</td>
<td>{{ item.chartShowName ? item.chartShowName : item.name }}</td>
</tr>
<tbody>
<tr>
<td>{{ t('dataset.field_origin_name') }}</td>
<td>:</td>
<td>{{ item.name }}</td>
</tr>
<tr>
<td>{{ t('chart.show_name') }}</td>
<td>:</td>
<td>{{ item.chartShowName ? item.chartShowName : item.name }}</td>
</tr>
</tbody>
</table>
</template>
<span class="item-span-style">
@@ -658,16 +675,7 @@ onMounted(() => {
</el-dropdown>
</el-dropdown-item>
<el-dropdown-item
@click.prevent
v-if="
props.type !== 'extLabel' &&
props.type !== 'extTooltip' &&
props.type !== 'extBubble' &&
!['chart-mix', 'indicator', 'liquid', 'gauge'].includes(chart.type)
"
:divided="chart.type !== 'table-info'"
>
<el-dropdown-item @click.prevent v-if="showSort" :divided="chart.type !== 'table-info'">
<el-dropdown
:effect="themes"
placement="right-start"
@@ -737,6 +745,15 @@ onMounted(() => {
</el-dropdown>
</el-dropdown-item>
<el-dropdown-item
v-if="showSort"
class="menu-item-padding"
:icon="icon_sort_priority"
:command="beforeClickItem('sortPriority')"
>
<span>{{ t('chart.sort_priority') }}</span>
</el-dropdown-item>
<el-dropdown-item
class="menu-item-padding"
v-if="

View File

@@ -0,0 +1,186 @@
<script lang="ts" setup>
import icon_drag_outlined from '@/assets/svg/icon_drag_outlined.svg'
import draggable from 'vuedraggable'
import { reactive, watch, ref } from 'vue'
import chartViewManager from '../../../js/panel'
const loading = ref(false)
const state = reactive({
sortList: []
})
const props = defineProps({
chart: {
type: Object,
required: true
}
})
const emit = defineEmits(['onPriorityChange'])
watch(
() => props.chart,
() => {
init()
},
{ deep: true }
)
const init = () => {
const chart = props.chart
if (!chart.sortPriority?.length) {
state.sortList.splice(0, state.sortList.length)
} else {
state.sortList.splice(0, state.sortList.length, ...chart.sortPriority)
}
const chartInstance = chartViewManager.getChartView(chart.render, chart.type)
if (chartInstance) {
const axis = chartInstance.axis
const axisMap = axis?.reduce((p, n) => {
let axisArr
switch (n) {
case 'xAxis':
axisArr = chart.xAxis
break
case 'yAxis':
axisArr = chart.yAxis
break
case 'xAxisExt':
axisArr = chart.xAxisExt
break
case 'yAxisExt':
axisArr = chart.yAxisExt
break
case 'extBubble':
axisArr = chart.extBubble
break
case 'flowMapEndName':
axisArr = chart.flowMapEndName
break
case 'flowMapStartName':
axisArr = chart.flowMapStartName
break
case 'extColor':
axisArr = chart.extColor
break
case 'extStack':
axisArr = chart.extStack
break
case 'drill':
axisArr = chart.drillFields
break
default:
break
}
axisArr?.forEach(ele => {
if (!p[ele.id]) {
p[ele.id] = ele.chartShowName ?? ele.name
}
})
return p
}, {})
state.sortList = state.sortList.reduce((p, n) => {
if (axisMap[n.id]) {
n.name = axisMap[n.id]
p.push(n)
}
return p
}, [])
Object.entries(axisMap).forEach(([key, value]) => {
if (!state.sortList.find(item => item.id === key)) {
state.sortList.push({ id: key, name: value })
}
})
}
}
const onUpdate = () => {
emit('onPriorityChange', state.sortList)
}
init()
</script>
<template>
<el-scrollbar height="100%" max-height="599px">
<draggable
v-loading="loading"
:list="state.sortList"
animation="300"
class="drag-list"
item-key="id"
@update="onUpdate"
>
<template #item="{ element }">
<span :key="element.id" class="item-dimension" :title="element.name">
<el-icon class="item-icon">
<Icon name="icon_drag_outlined"><icon_drag_outlined class="svg-icon" /></Icon>
</el-icon>
<span class="item-span">
{{ element.name }}
</span>
</span>
</template>
</draggable>
</el-scrollbar>
</template>
<style lang="less" scoped>
.drag-list {
height: 50vh;
}
.item-dimension {
padding: 2px;
margin: 2px;
border: 1px solid #dee0e3;
border-radius: 4px;
text-align: left;
color: #606266;
background-color: white;
display: flex;
align-items: center;
cursor: move;
}
.item-icon {
font-size: 16px;
margin: 0 4px;
color: #646a73;
}
.item-span {
display: inline-block;
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: #1f2329;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
.blackTheme .item-dimension {
border: solid 1px;
border-color: var(--TableBorderColor);
color: var(--TextPrimary);
background-color: var(--MainBG);
}
.item-dimension + .item-dimension {
margin-top: 6px;
}
.item-dimension:hover {
box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.1);
}
.blackTheme .item-dimension:hover {
color: var(--Main);
background: var(--ContentBG);
}
</style>

View File

@@ -51,6 +51,7 @@ import { useRouter, useRoute } from 'vue-router'
import CompareEdit from '@/views/chart/components/editor/drag-item/components/CompareEdit.vue'
import ValueFormatterEdit from '@/views/chart/components/editor/drag-item/components/ValueFormatterEdit.vue'
import CustomSortEdit from '@/views/chart/components/editor/drag-item/components/CustomSortEdit.vue'
import SortPriorityEdit from '@/views/chart/components/editor/drag-item/components/SortPriorityEdit.vue'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import CalcFieldEdit from '@/views/visualized/data/dataset/form/CalcFieldEdit.vue'
import { getFieldName, guid } from '@/views/visualized/data/dataset/form/util'
@@ -230,6 +231,8 @@ const state = reactive({
showValueFormatter: false,
valueFormatterItem: {},
showCustomSort: false,
showSortPriority: false,
sortPriority: [],
customSortList: [],
customSortField: {},
currEditField: {},
@@ -1521,6 +1524,19 @@ const onToggleHide = item => {
}
renderChart(view.value)
}
const editSortPriority = () => {
state.showSortPriority = true
}
const closeSortPriority = () => {
state.showSortPriority = false
}
const saveSortPriority = () => {
view.value.sortPriority = state.sortPriority as ChartViewField[]
closeSortPriority()
}
const onPriorityChange = val => {
state.sortPriority = val
}
const valueFormatter = item => {
recordSnapshotInfo('render')
state.valueFormatterItem = JSON.parse(JSON.stringify(item))
@@ -2159,6 +2175,7 @@ const deleteChartFieldItem = id => {
@onCustomSort="onCustomSort"
@valueFormatter="valueFormatter"
@onToggleHide="onToggleHide"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2221,6 +2238,7 @@ const deleteChartFieldItem = id => {
@onDimensionItemRemove="dimensionItemRemove"
@onNameEdit="showRename"
@onCustomSort="onExtCustomSort"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2285,6 +2303,7 @@ const deleteChartFieldItem = id => {
@onNameEdit="showRename"
@onCustomSort="onCustomFlowMapStartNameSort"
@valueFormatter="valueFormatter"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2349,6 +2368,7 @@ const deleteChartFieldItem = id => {
@onNameEdit="showRename"
@onCustomSort="onCustomFlowMapEndNameSort"
@valueFormatter="valueFormatter"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2411,6 +2431,7 @@ const deleteChartFieldItem = id => {
@onDimensionItemRemove="dimensionItemRemove"
@onNameEdit="showRename"
@onCustomSort="onStackCustomSort"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2475,6 +2496,7 @@ const deleteChartFieldItem = id => {
@onNameEdit="showRename"
@onCustomSort="onCustomExtColorSort"
@valueFormatter="valueFormatter"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2562,6 +2584,7 @@ const deleteChartFieldItem = id => {
@editItemCompare="showQuotaEditCompare"
@valueFormatter="valueFormatter"
@onToggleHide="onToggleHide"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2627,6 +2650,7 @@ const deleteChartFieldItem = id => {
@onDimensionItemRemove="dimensionItemRemove"
@onNameEdit="showRename"
@onCustomSort="onExtCustomRightSort"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2691,6 +2715,7 @@ const deleteChartFieldItem = id => {
@editItemFilter="showQuotaEditFilter"
@editItemCompare="showQuotaEditCompare"
@valueFormatter="valueFormatter"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2756,6 +2781,7 @@ const deleteChartFieldItem = id => {
@onDimensionItemRemove="dimensionItemRemove"
@onNameEdit="showRename"
@onCustomSort="onExtCustomSort"
@editSortPriority="editSortPriority"
/>
<quota-item
v-else-if="element.groupType === 'q'"
@@ -2772,6 +2798,7 @@ const deleteChartFieldItem = id => {
@editItemFilter="showQuotaEditFilter"
@editItemCompare="showQuotaEditCompare"
@valueFormatter="valueFormatter"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2835,6 +2862,7 @@ const deleteChartFieldItem = id => {
@onDimensionItemRemove="dimensionItemRemove"
@onNameEdit="showRename"
@onCustomSort="onExtCustomSort"
@editSortPriority="editSortPriority"
/>
<quota-item
v-else-if="element.groupType === 'q'"
@@ -2851,6 +2879,7 @@ const deleteChartFieldItem = id => {
@editItemFilter="showQuotaEditFilter"
@editItemCompare="showQuotaEditCompare"
@valueFormatter="valueFormatter"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -2936,6 +2965,7 @@ const deleteChartFieldItem = id => {
@editItemFilter="showQuotaEditFilter"
@editItemCompare="showQuotaEditCompare"
@valueFormatter="valueFormatter"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -3009,6 +3039,7 @@ const deleteChartFieldItem = id => {
@onDimensionItemRemove="drillItemRemove"
@onNameEdit="showRename"
@onCustomSort="onDrillCustomSort"
@editSortPriority="editSortPriority"
/>
</template>
</draggable>
@@ -3876,6 +3907,28 @@ const deleteChartFieldItem = id => {
</template>
</el-dialog>
<el-dialog
v-model="state.showSortPriority"
:close-on-click-modal="false"
width="372px"
class="dialog-css custom_sort_dialog"
destroy-on-close
>
<template #header>
<span style="font-size: 15px; font-weight: bold; color: black">
{{ t('chart.sort_priority') }}
</span>
<span>({{ t('chart.sort_priority_tip') }})</span>
</template>
<sort-priority-edit :chart="view" @on-priority-change="onPriorityChange" />
<template #footer>
<div class="dialog-footer">
<el-button @click="closeSortPriority">{{ t('chart.cancel') }} </el-button>
<el-button type="primary" @click="saveSortPriority">{{ t('chart.confirm') }} </el-button>
</div>
</template>
</el-dialog>
<!--图表计算字段-->
<el-dialog
v-model="editCalcField"

View File

@@ -1671,6 +1671,7 @@ export const BASE_VIEW_CONFIG = {
extLabel: [],
extTooltip: [],
customFilter: {},
sortPriority: [],
customAttr: {
basicStyle: DEFAULT_BASIC_STYLE,
misc: DEFAULT_MISC,

View File

@@ -6,6 +6,7 @@ const { t } = useI18n()
* 指标卡图表
*/
export class IndicatorChartView extends AbstractChartView {
selectorSpec: EditorSelectorSpec
properties: EditorProperty[] = [
'background-overall-component',
'border-style',

View File

@@ -33,4 +33,6 @@ public class SQLObj {
private String whereTermAndValue;
private String limitFiled;
private Long id;
}

View File

@@ -234,5 +234,9 @@ public class ChartViewBaseDTO implements Serializable {
private List<CalParam> calParams;
private List<ChartViewFieldDTO> extColor;
/**
* 字段排序优先级
*/
List<SortAxis> sortPriority;
}

View File

@@ -0,0 +1,12 @@
package io.dataease.extensions.view.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
@Data
public class SortAxis {
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
private String name;
}

View File

@@ -1,5 +1,7 @@
package io.dataease.extensions.view.util;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.view.dto.*;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@@ -10,13 +12,38 @@ import java.util.*;
public class ChartDataUtil {
// 对结果排序
public static List<String[]> resultCustomSort(List<ChartViewFieldDTO> xAxis, List<String[]> data) {
public static List<String[]> resultCustomSort(List<ChartViewFieldDTO> xAxis,List<ChartViewFieldDTO> yAxis, List<SortAxis> sortPriority, List<String[]> data) {
List<String[]> res = new ArrayList<>(data);
if (xAxis.size() > 0) {
var axisList = new ArrayList<ChartViewFieldDTO>();
axisList.addAll(xAxis);
axisList.addAll(yAxis);
var dataIndexMap = new HashMap<Long, Integer>(axisList.size());
for (int i = 0; i < axisList.size(); i++) {
dataIndexMap.put(axisList.get(i).getId(), i);
}
if (CollectionUtils.isNotEmpty(sortPriority)) {
var tmp = new ArrayList<ChartViewFieldDTO>();
var ids = new HashSet<Long>();
for (SortAxis sortAxis : sortPriority) {
for (ChartViewFieldDTO axis : axisList) {
if (sortAxis.getId().equals(axis.getId())){
tmp.add(axis);
ids.add(axis.getId());
}
}
}
for (ChartViewFieldDTO axis : axisList) {
if (!ids.contains(axis.getId())) {
tmp.add(axis);
}
}
axisList = tmp;
}
if (axisList.size() > 0) {
// 找到对应维度
for (int i = 0; i < xAxis.size(); i++) {
ChartViewFieldDTO item = xAxis.get(i);
if (StringUtils.equalsIgnoreCase(item.getSort(), "custom_sort")) {
for (int i = 0; i < axisList.size(); i++) {
ChartViewFieldDTO item = axisList.get(i);
if (StringUtils.equalsIgnoreCase(item.getSort(), "custom_sort") && Objects.equals(item.getGroupType(), "d")) {
// 获取自定义值与data对应列的结果
if (i > 0) {
// 首先根据优先级高的字段分类,在每个前置字段相同的组里排序
@@ -24,10 +51,10 @@ public class ChartDataUtil {
for (String[] d : res) {
StringBuilder stringBuilder = new StringBuilder();
for (int j = 0; j < i; j++) {
if (StringUtils.equalsIgnoreCase(xAxis.get(j).getSort(), "none")) {
if (StringUtils.equalsIgnoreCase(axisList.get(j).getSort(), "none")) {
continue;
}
stringBuilder.append(d[j]);
stringBuilder.append(d[dataIndexMap.get(axisList.get(j).getId())]);
}
if (ObjectUtils.isEmpty(map.get(stringBuilder.toString()))) {
map.put(stringBuilder.toString(), new ArrayList<>());
@@ -38,12 +65,16 @@ public class ChartDataUtil {
List<String[]> list = new ArrayList<>();
while (iterator.hasNext()) {
Map.Entry<String, List<String[]>> next = iterator.next();
list.addAll(customSort(Optional.ofNullable(item.getCustomSort()).orElse(new ArrayList<>()), next.getValue(), i));
if (next.getValue().size() == 1) {
list.addAll(next.getValue());
} else {
list.addAll(customSort(Optional.ofNullable(item.getCustomSort()).orElse(new ArrayList<>()), next.getValue(), dataIndexMap.get(axisList.get(i).getId())));
}
}
res.clear();
res.addAll(list);
} else {
res = customSort(Optional.ofNullable(item.getCustomSort()).orElse(new ArrayList<>()), res, i);
res = customSort(Optional.ofNullable(item.getCustomSort()).orElse(new ArrayList<>()), res, dataIndexMap.get(axisList.get(i).getId()));
}
}
}
@@ -53,37 +84,40 @@ public class ChartDataUtil {
public static List<String[]> customSort(List<String> custom, List<String[]> data, int index) {
List<String[]> res = new ArrayList<>();
List<Integer> indexArr = new ArrayList<>();
List<String[]> joinArr = new ArrayList<>();
// 数据行在自定义排序的范围内,记录该数据行的内容以及下标
List<Integer> indexInCustomSort = new ArrayList<>();
List<String[]> dataInCustomSort = new ArrayList<>();
for (int i = 0; i < custom.size(); i++) {
String ele = custom.get(i);
for (int j = 0; j < data.size(); j++) {
String[] d = data.get(j);
if (StringUtils.equalsIgnoreCase(ele, d[index])) {
joinArr.add(d);
indexArr.add(j);
dataInCustomSort.add(d);
indexInCustomSort.add(j);
}
}
}
// 取得 joinArr 就是两者的交集
List<Integer> indexArrData = new ArrayList<>();
// 记录总数据的下标
List<Integer> dataIndexArr = new ArrayList<>();
for (int i = 0; i < data.size(); i++) {
indexArrData.add(i);
dataIndexArr.add(i);
}
List<Integer> indexResult = new ArrayList<>();
for (int i = 0; i < indexArrData.size(); i++) {
if (!indexArr.contains(indexArrData.get(i))) {
indexResult.add(indexArrData.get(i));
// 记录不包含自定义排序行的下标
List<Integer> indexNotInCustomSort = new ArrayList<>();
for (int i = 0; i < dataIndexArr.size(); i++) {
if (!indexInCustomSort.contains(dataIndexArr.get(i))) {
indexNotInCustomSort.add(dataIndexArr.get(i));
}
}
List<String[]> subArr = new ArrayList<>();
for (int i = 0; i < indexResult.size(); i++) {
subArr.add(data.get(indexResult.get(i)));
List<String[]> dataNotInCustomSort = new ArrayList<>();
for (int i = 0; i < indexNotInCustomSort.size(); i++) {
dataNotInCustomSort.add(data.get(indexNotInCustomSort.get(i)));
}
res.addAll(joinArr);
res.addAll(subArr);
// 自定义排序行放到前面,剩下的放到后面
res.addAll(dataInCustomSort);
res.addAll(dataNotInCustomSort);
return res;
}