forked from github/dataease
parent
5196158c33
commit
2c20d8a76c
@ -312,7 +312,6 @@ public class ChartDataManage {
|
||||
ChartViewFieldDTO nextDrillField = drill.get(i + 1);
|
||||
if (!fields.contains(nextDrillField.getId())) {
|
||||
nextDrillField.setSource(FieldSource.DRILL);
|
||||
nextDrillField.setSort(getDrillSort(xAxis, drill.get(0)));
|
||||
xAxis.add(nextDrillField);
|
||||
dillAxis.add(nextDrillField);
|
||||
fields.add(nextDrillField.getId());
|
||||
@ -791,4 +790,24 @@ public class ChartDataManage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getDrillFieldData(ChartViewDTO view, Long fieldId) throws Exception {
|
||||
List<ChartViewFieldDTO> drillField = view.getDrillFields();
|
||||
ChartViewFieldDTO targetField = null;
|
||||
for (int i = 0; i < drillField.size(); i++) {
|
||||
ChartViewFieldDTO tmp = drillField.get(i);
|
||||
if (tmp.getId().equals(fieldId)) {
|
||||
targetField = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (targetField == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
view.setXAxis(Collections.singletonList(targetField));
|
||||
|
||||
List<String[]> sqlData = sqlData(view, view.getChartExtRequest(), fieldId);
|
||||
List<String[]> result = customSort(Optional.ofNullable(targetField.getCustomSort()).orElse(new ArrayList<>()), sqlData, 0);
|
||||
return result.stream().map(i -> i[0]).distinct().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
@ -303,4 +303,9 @@ public class ChartDataServer implements ChartDataApi {
|
||||
public List<String> getFieldData(ChartViewDTO view, Long fieldId, String fieldType) throws Exception {
|
||||
return chartDataManage.getFieldData(view, fieldId, fieldType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDrillFieldData(ChartViewDTO view, Long fieldId) throws Exception {
|
||||
return chartDataManage.getDrillFieldData(view, fieldId);
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ export const saveChart = async (data): Promise<IResponse> => {
|
||||
}
|
||||
|
||||
// 获取单个字段枚举值
|
||||
export const getFieldData = async (fieldId, fieldType, data): Promise<IResponse> => {
|
||||
export const getFieldData = async ({ fieldId, fieldType, data }): Promise<IResponse> => {
|
||||
delete data.data
|
||||
return request
|
||||
.post({ url: `/chartData/getFieldData/${fieldId}/${fieldType}`, data })
|
||||
@ -97,6 +97,14 @@ export const getFieldData = async (fieldId, fieldType, data): Promise<IResponse>
|
||||
})
|
||||
}
|
||||
|
||||
// 获取下钻字段枚举值
|
||||
export const getDrillFieldData = async ({ fieldId, data }): Promise<IResponse> => {
|
||||
delete data.data
|
||||
return request.post({ url: `/chartData/getDrillFieldData/${fieldId}`, data }).then(res => {
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
export const getChartDetail = async (id: string): Promise<IResponse> => {
|
||||
return request.post({ url: `chart/getDetail/${id}`, data: {} }).then(res => {
|
||||
return res
|
||||
|
@ -58,6 +58,7 @@ declare type AxisType =
|
||||
| 'flowMapStartName'
|
||||
| 'flowMapEndName'
|
||||
| 'extColor'
|
||||
| 'drillFields'
|
||||
/**
|
||||
* 轴配置
|
||||
*/
|
||||
|
@ -1,10 +1,15 @@
|
||||
<script lang="tsx" setup>
|
||||
import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg'
|
||||
import icon_down_outlined1 from '@/assets/svg/icon_down_outlined-1.svg'
|
||||
import icon_sortAToZ_outlined from '@/assets/svg/icon_sort-a-to-z_outlined.svg'
|
||||
import icon_sortZToA_outlined from '@/assets/svg/icon_sort-z-to-a_outlined.svg'
|
||||
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 { useI18n } from '@/hooks/web/useI18n'
|
||||
import { onMounted, ref, toRefs, watch } from 'vue'
|
||||
import { getItemType } from '@/views/chart/components/editor/drag-item/utils'
|
||||
import { Delete } from '@element-plus/icons-vue'
|
||||
import { fieldType } from '@/utils/attr'
|
||||
import { iconFieldMap } from '@/components/icon-group/field-list'
|
||||
|
||||
@ -43,7 +48,12 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['onDimensionItemRemove'])
|
||||
const emit = defineEmits([
|
||||
'onDimensionItemRemove',
|
||||
'onCustomSort',
|
||||
'onDimensionItemChange',
|
||||
'onNameEdit'
|
||||
])
|
||||
|
||||
const { item } = toRefs(props)
|
||||
|
||||
@ -60,6 +70,9 @@ const clickItem = param => {
|
||||
return
|
||||
}
|
||||
switch (param.type) {
|
||||
case 'rename':
|
||||
showRename()
|
||||
break
|
||||
case 'remove':
|
||||
removeItem()
|
||||
break
|
||||
@ -79,6 +92,35 @@ const removeItem = () => {
|
||||
const getItemTagType = () => {
|
||||
tagType.value = getItemType(props.dimensionData, props.quotaData, props.item)
|
||||
}
|
||||
|
||||
const showRename = () => {
|
||||
item.value.index = props.index
|
||||
item.value.renameType = 'drillFields'
|
||||
emit('onNameEdit', item.value)
|
||||
}
|
||||
|
||||
const sort = param => {
|
||||
if (param.type === 'custom_sort') {
|
||||
const item = {
|
||||
index: props.index,
|
||||
sort: param.type
|
||||
}
|
||||
emit('onCustomSort', item)
|
||||
} else {
|
||||
item.value.index = props.index
|
||||
item.value.sort = param.type
|
||||
item.value.customSort = []
|
||||
delete item.value.axisType
|
||||
emit('onDimensionItemChange', item.value)
|
||||
}
|
||||
}
|
||||
|
||||
const beforeSort = type => {
|
||||
return {
|
||||
type: type
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getItemTagType()
|
||||
})
|
||||
@ -89,21 +131,42 @@ onMounted(() => {
|
||||
<el-dropdown :effect="themes" trigger="click" size="mini" @command="clickItem">
|
||||
<el-tag
|
||||
class="item-axis father"
|
||||
:class="'editor-' + props.themes"
|
||||
:class="['editor-' + props.themes, `${themes}_icon-right`]"
|
||||
:style="{ backgroundColor: tagType + '0a', border: '1px solid ' + tagType }"
|
||||
>
|
||||
<span style="display: flex">
|
||||
<span style="display: flex; color: #646a73">
|
||||
<el-icon v-if="'asc' === item.sort">
|
||||
<Icon name="icon_sort-a-to-z_outlined"
|
||||
><icon_sortAToZ_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</el-icon>
|
||||
<el-icon v-if="'desc' === item.sort">
|
||||
<Icon name="icon_sort-z-to-a_outlined"
|
||||
><icon_sortZToA_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</el-icon>
|
||||
<el-icon v-if="'custom_sort' === item.sort">
|
||||
<Icon name="icon_sort_outlined"><icon_sort_outlined class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<el-icon>
|
||||
<Icon :className="`field-icon-${fieldType[item.deType]}`"
|
||||
<Icon :className="`field-icon-${fieldType[[2, 3].includes(item.deType) ? 2 : 0]}`"
|
||||
><component
|
||||
class="svg-icon"
|
||||
:class="`field-icon-${fieldType[item.deType]}`"
|
||||
:class="`field-icon-${fieldType[[2, 3].includes(item.deType) ? 2 : 0]}`"
|
||||
:is="iconFieldMap[fieldType[item.deType]]"
|
||||
></component
|
||||
></Icon>
|
||||
</el-icon>
|
||||
</span>
|
||||
<span class="item-span-style" :title="item.name">{{ item.name }}</span>
|
||||
<el-tooltip
|
||||
:effect="themes === 'dark' ? 'ndark' : 'dark'"
|
||||
placement="top"
|
||||
:content="item.chartShowName ? item.chartShowName : item.name"
|
||||
>
|
||||
<span class="item-span-style">
|
||||
<span class="item-name">{{ item.chartShowName ? item.chartShowName : item.name }}</span>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
<el-icon class="child remove-icon" size="14px">
|
||||
<Icon name="icon_delete-trash_outlined" class-name="inner-class"
|
||||
><icon_deleteTrash_outlined @click="removeItem" class="svg-icon inner-class"
|
||||
@ -117,8 +180,105 @@ onMounted(() => {
|
||||
</el-icon>
|
||||
</el-tag>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu :effect="themes" class="drop-style">
|
||||
<el-dropdown-item :icon="Delete" :command="beforeClickItem('remove')">
|
||||
<el-dropdown-menu
|
||||
:effect="themes"
|
||||
class="drop-style"
|
||||
:class="themes === 'dark' ? 'dark-dimension-quota' : ''"
|
||||
>
|
||||
<el-dropdown-item @click.prevent>
|
||||
<el-dropdown
|
||||
:effect="themes"
|
||||
popper-class="data-dropdown_popper_mr9"
|
||||
placement="right-start"
|
||||
style="width: 100%; height: 100%"
|
||||
@command="sort"
|
||||
>
|
||||
<span class="inner-dropdown-menu menu-item-padding">
|
||||
<span class="menu-item-content">
|
||||
<el-icon>
|
||||
<Icon name="icon_sort_outlined"><icon_sort_outlined class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<span>{{ t('chart.sort') }}</span>
|
||||
<span class="summary-span-item">({{ t('chart.' + props.item.sort) }})</span>
|
||||
</span>
|
||||
<el-icon>
|
||||
<Icon name="icon_right_outlined"><icon_right_outlined class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu
|
||||
:effect="themes"
|
||||
class="drop-style sub"
|
||||
:class="themes === 'dark' ? 'dark-dimension-quota' : ''"
|
||||
>
|
||||
<el-dropdown-item class="menu-item-padding" :command="beforeSort('none')">
|
||||
<span
|
||||
class="sub-menu-content"
|
||||
:class="'none' === item.sort ? 'content-active' : ''"
|
||||
>
|
||||
{{ t('chart.none') }}
|
||||
<el-icon class="sub-menu-content--icon">
|
||||
<Icon name="icon_done_outlined" v-if="'none' === item.sort"
|
||||
><icon_done_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item class="menu-item-padding" :command="beforeSort('asc')">
|
||||
<span
|
||||
class="sub-menu-content"
|
||||
:class="'asc' === item.sort ? 'content-active' : ''"
|
||||
>
|
||||
{{ t('chart.asc') }}
|
||||
<el-icon class="sub-menu-content--icon">
|
||||
<Icon name="icon_done_outlined" v-if="'asc' === item.sort"
|
||||
><icon_done_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item class="menu-item-padding" :command="beforeSort('desc')">
|
||||
<span
|
||||
class="sub-menu-content"
|
||||
:class="'desc' === item.sort ? 'content-active' : ''"
|
||||
>
|
||||
{{ t('chart.desc') }}
|
||||
<el-icon class="sub-menu-content--icon">
|
||||
<Icon name="icon_done_outlined" v-if="'desc' === item.sort"
|
||||
><icon_done_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item class="menu-item-padding" :command="beforeSort('custom_sort')">
|
||||
<span
|
||||
class="sub-menu-content"
|
||||
:class="'custom_sort' === item.sort ? 'content-active' : ''"
|
||||
>
|
||||
{{ t('chart.custom_sort') }}{{ t('chart.sort') }}
|
||||
<el-icon class="sub-menu-content--icon">
|
||||
<Icon name="icon_done_outlined" v-if="'custom_sort' === item.sort"
|
||||
><icon_done_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</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>
|
||||
</el-icon>
|
||||
<span>{{ t('chart.show_name_set') }}</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item class="menu-item-padding" divided :command="beforeClickItem('remove')">
|
||||
<el-icon>
|
||||
<icon name="icon_delete-trash_outlined"
|
||||
><icon_deleteTrash_outlined class="svg-icon"
|
||||
/></icon>
|
||||
</el-icon>
|
||||
<span>{{ t('chart.delete') }}</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@ -128,6 +288,16 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ed-dropdown-menu__item) {
|
||||
padding: 0;
|
||||
}
|
||||
:deep(.ed-dropdown-menu__item.menu-item-padding) {
|
||||
padding: 5px 16px;
|
||||
}
|
||||
|
||||
.menu-item-padding {
|
||||
padding: 5px 16px;
|
||||
}
|
||||
.item-style {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
@ -145,7 +315,6 @@ onMounted(() => {
|
||||
.item-axis {
|
||||
padding: 1px 8px;
|
||||
margin: 0 3px 2px 3px;
|
||||
text-align: left;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
display: flex;
|
||||
@ -169,7 +338,9 @@ span {
|
||||
|
||||
.summary-span {
|
||||
margin-left: 4px;
|
||||
color: #a6a6a6;
|
||||
color: #878d9f;
|
||||
position: absolute;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.inner-dropdown-menu {
|
||||
@ -177,6 +348,27 @@ span {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.menu-item-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-menu-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
&.content-active {
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
|
||||
.sub-menu-content--icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.item-span-drop {
|
||||
@ -185,13 +377,17 @@ span {
|
||||
}
|
||||
|
||||
.item-span-style {
|
||||
display: inline-block;
|
||||
width: 115px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
max-width: 180px;
|
||||
color: #1f2329;
|
||||
margin-left: 4px;
|
||||
|
||||
.item-name {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-dark {
|
||||
@ -200,29 +396,113 @@ span {
|
||||
}
|
||||
}
|
||||
|
||||
.summary-span-item {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.drop-style {
|
||||
:deep(.ed-dropdown-menu__item) {
|
||||
height: 32px;
|
||||
min-width: 218px;
|
||||
}
|
||||
&.sub {
|
||||
:deep(.ed-dropdown-menu__item) {
|
||||
min-width: 118px;
|
||||
}
|
||||
}
|
||||
:deep(.ed-dropdown-menu__item:not(.is_disabled):focus) {
|
||||
color: inherit;
|
||||
background-color: rgba(31, 35, 41, 0.1);
|
||||
}
|
||||
&.dark-dimension-quota {
|
||||
.inner-dropdown-menu {
|
||||
color: rgba(235, 235, 235, 1);
|
||||
}
|
||||
:deep(.ed-dropdown-menu__item) {
|
||||
color: rgba(235, 235, 235, 1);
|
||||
}
|
||||
:deep(.ed-dropdown-menu__item.is-disabled) {
|
||||
color: #a6a6a6;
|
||||
}
|
||||
:deep(.ed-dropdown-menu__item:not(.is_disabled):focus) {
|
||||
background-color: rgba(235, 235, 235, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remove-icon {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 24px;
|
||||
color: #646a73;
|
||||
right: 26px;
|
||||
cursor: pointer;
|
||||
|
||||
.inner-class {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.father .child {
|
||||
visibility: hidden;
|
||||
.father {
|
||||
&.dark_icon-right {
|
||||
.child {
|
||||
color: #a6a6a6;
|
||||
}
|
||||
}
|
||||
|
||||
&.light_icon-right {
|
||||
.child {
|
||||
color: #646a73;
|
||||
}
|
||||
}
|
||||
.child {
|
||||
font-size: 14px;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.father:hover .child {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.father:hover .item-span-style {
|
||||
max-width: 150px;
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.data-dropdown_popper_mr9 {
|
||||
margin-left: -9px !important;
|
||||
}
|
||||
.menu-item-padding {
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: #1f2329;
|
||||
}
|
||||
.ed-icon {
|
||||
color: #646a73;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.sub-menu-content--icon {
|
||||
color: var(--ed-color-primary);
|
||||
margin-right: -7px;
|
||||
}
|
||||
:nth-child(1).ed-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.menu-item-content {
|
||||
:nth-child(1).ed-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dark-dimension-quota {
|
||||
span {
|
||||
color: #ebebeb;
|
||||
}
|
||||
.ed-icon {
|
||||
color: #a6a6a6;
|
||||
}
|
||||
|
||||
.sub-menu-content--icon {
|
||||
color: var(--ed-color-primary);
|
||||
margin-right: -7px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="tsx" setup>
|
||||
<script lang="ts" setup>
|
||||
import icon_drag_outlined from '@/assets/svg/icon_drag_outlined.svg'
|
||||
import draggable from 'vuedraggable'
|
||||
import { getFieldData } from '@/api/chart'
|
||||
import { getFieldData, getDrillFieldData } from '@/api/chart'
|
||||
import { reactive, watch, ref } from 'vue'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
|
||||
@ -45,7 +45,13 @@ const init = () => {
|
||||
user: wsCache.get('user.uid')
|
||||
}
|
||||
}
|
||||
getFieldData(props.field.id, props.fieldType, chart)
|
||||
const param = {
|
||||
fieldId: props.field.id,
|
||||
fieldType: props.fieldType,
|
||||
data: chart
|
||||
}
|
||||
let reqMethod = props.fieldType === 'drillFields' ? getDrillFieldData : getFieldData
|
||||
reqMethod(param)
|
||||
.then(response => {
|
||||
const strArr = response.data
|
||||
state.sortList = strArr.map(ele => {
|
||||
|
@ -531,6 +531,12 @@ const onCustomExtColorSort = item => {
|
||||
customSortAxis.value = 'extColor'
|
||||
customSort()
|
||||
}
|
||||
const onDrillCustomSort = item => {
|
||||
recordSnapshotInfo('render')
|
||||
state.customSortField = view.value.drillFields[item.index]
|
||||
customSortAxis.value = 'drillFields'
|
||||
customSort()
|
||||
}
|
||||
const onMove = e => {
|
||||
recordSnapshotInfo('calcData')
|
||||
state.moveId = e.draggedContext.element.id
|
||||
@ -1256,6 +1262,10 @@ const saveRename = ref => {
|
||||
axis = view.value.extColor[index]
|
||||
view.value.extColor[index].chartShowName = chartShowName
|
||||
break
|
||||
case 'drillFields':
|
||||
axisType = 'drillFields'
|
||||
axis = view.value.drillFields[index]
|
||||
view.value.drillFields[index].chartShowName = chartShowName
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -2826,6 +2836,8 @@ const deleteChartFieldItem = id => {
|
||||
:themes="props.themes"
|
||||
@onDimensionItemChange="drillItemChange"
|
||||
@onDimensionItemRemove="drillItemRemove"
|
||||
@onNameEdit="showRename"
|
||||
@onCustomSort="onDrillCustomSort"
|
||||
/>
|
||||
</template>
|
||||
</draggable>
|
||||
|
@ -29,4 +29,8 @@ public interface ChartDataApi {
|
||||
@Operation(summary = "获取字段值")
|
||||
@PostMapping("getFieldData/{fieldId}/{fieldType}")
|
||||
List<String> getFieldData(@RequestBody ChartViewDTO view, @PathVariable Long fieldId, @PathVariable String fieldType) throws Exception;
|
||||
|
||||
@Operation(summary = "获取下钻字段值")
|
||||
@PostMapping("getDrillFieldData/{fieldId}")
|
||||
List<String> getDrillFieldData(@RequestBody ChartViewDTO view, @PathVariable Long fieldId) throws Exception;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user