diff --git a/core/core-backend/src/main/java/io/dataease/chart/charts/impl/table/TableNormalHandler.java b/core/core-backend/src/main/java/io/dataease/chart/charts/impl/table/TableNormalHandler.java index 22bdc2fed3..bcd7846dc4 100644 --- a/core/core-backend/src/main/java/io/dataease/chart/charts/impl/table/TableNormalHandler.java +++ b/core/core-backend/src/main/java/io/dataease/chart/charts/impl/table/TableNormalHandler.java @@ -4,11 +4,14 @@ import com.fasterxml.jackson.core.type.TypeReference; import io.dataease.api.chart.dto.PageInfo; import io.dataease.api.dataset.union.DatasetGroupInfoDTO; import io.dataease.chart.charts.impl.DefaultChartHandler; +import io.dataease.constant.DeTypeConstants; +import io.dataease.engine.constant.ExtFieldConstant; import io.dataease.engine.sql.SQLProvider; import io.dataease.engine.trans.Dimension2SQLObj; import io.dataease.engine.trans.ExtWhere2Str; import io.dataease.engine.trans.Quota2SQLObj; import io.dataease.engine.utils.Utils; +import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO; import io.dataease.extensions.datasource.dto.DatasourceRequest; import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO; import io.dataease.extensions.datasource.model.SQLMeta; @@ -16,14 +19,18 @@ 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.IDUtils; import io.dataease.utils.JsonUtil; import lombok.Getter; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import java.math.BigDecimal; import java.util.*; +import java.util.stream.Collectors; /** * @author jianneng @@ -110,7 +117,7 @@ public class TableNormalHandler extends DefaultChartHandler { var tablePageMode = (String) filterResult.getContext().get("tablePageMode"); var totalPageSql = "SELECT COUNT(*) FROM (" + SQLProvider.createQuerySQLNoSort(sqlMeta, true, view) + ") COUNT_TEMP"; - if (StringUtils.isNotEmpty(totalPageSql) && StringUtils.equalsIgnoreCase(tablePageMode, "page")) { + if (StringUtils.equalsIgnoreCase(tablePageMode, "page")) { totalPageSql = provider.rebuildSQL(totalPageSql, sqlMeta, crossDs, dsMap); datasourceRequest.setQuery(totalPageSql); datasourceRequest.setTotalPageFlag(true); @@ -131,7 +138,6 @@ public class TableNormalHandler extends DefaultChartHandler { List data = (List) provider.fetchResultField(datasourceRequest).get("data"); //自定义排序 data = ChartDataUtil.resultCustomSort(xAxis, yAxis, view.getSortPriority(), data); - var yoyFiltered = filterResult.getContext().get("yoyFiltered") != null; if (yoyFiltered) { // 这里没加分页,因为加了分页参数可能会把原始数据挤出去 @@ -208,6 +214,64 @@ public class TableNormalHandler extends DefaultChartHandler { } catch (Exception e) { e.printStackTrace(); } + // 自定义汇总 + var basicStyle = (Map) view.getCustomAttr().get("basicStyle"); + var showSummary = BooleanUtils.isTrue((Boolean) basicStyle.get("showSummary")); + if (showSummary) { + var fieldList = (List) basicStyle.get("seriesSummary"); + if (CollectionUtils.isNotEmpty(fieldList)) { + var customCalcFields = new ArrayList(); + var seriesList = JsonUtil.parseList(JsonUtil.toJSONString(fieldList).toString(), new TypeReference>(){}); + var quotaIds = allFields.stream().map(DatasetTableFieldDTO::getDataeaseName).collect(Collectors.toSet()); + seriesList.forEach(field -> { + if (!BooleanUtils.isTrue(field.getShow()) || !"custom".equalsIgnoreCase(field.getSummary())) { + return; + } + if (StringUtils.isBlank(field.getOriginName())) { + return; + } + if (!quotaIds.contains(field.getField())) { + return; + } + field.setSummary(""); + field.setDeType(DeTypeConstants.DE_FLOAT); + field.setId(IDUtils.snowID()); + field.setExtField(ExtFieldConstant.EXT_CALC); + customCalcFields.add(field); + }); + if (!customCalcFields.isEmpty()) { + var xFields = sqlMeta.getXFields(); + // 清空维度值,获取完结果再设置回去 + sqlMeta.setXFields(Collections.emptyList()); + List tmpList = FieldUtil.transFields(allFields); + tmpList.addAll(customCalcFields); + Quota2SQLObj.quota2sqlObj(sqlMeta, customCalcFields, tmpList, crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage); + String customSumSql = SQLProvider.createQuerySQL(sqlMeta, false, !StringUtils.equalsIgnoreCase(dsMap.values().iterator().next().getType(), "es"), view); + customSumSql = provider.rebuildSQL(customSumSql, sqlMeta, crossDs, dsMap); + var customSumReq = new DatasourceRequest(); + customSumReq.setIsCross(crossDs); + customSumReq.setDsList(dsMap); + customSumReq.setQuery(customSumSql); + var customSumData = (List) provider.fetchResultField(customSumReq).get("data"); + if (CollectionUtils.isNotEmpty(customSumData)) { + var customSumResult = new HashMap(); + // 只取第一行结果 + var customSumArr = customSumData.get(0); + for (int i = 0; i < customSumArr.length; i++) { + if (customCalcFields.get(i) != null && customSumArr[i] != null) { + try { + customSumResult.put(customCalcFields.get(i).getField(), new BigDecimal(customSumArr[i])); + } catch (Exception e) { + customSumResult.put(customCalcFields.get(i).getField(), new BigDecimal(0)); + } + } + } + result.put("customSumResult", customSumResult); + } + sqlMeta.setXFields(xFields); + } + } + } return calcResult; } } diff --git a/core/core-frontend/src/components/data-visualization/canvas/DePreview.vue b/core/core-frontend/src/components/data-visualization/canvas/DePreview.vue index 265f59e1a3..bf75c5ddcd 100644 --- a/core/core-frontend/src/components/data-visualization/canvas/DePreview.vue +++ b/core/core-frontend/src/components/data-visualization/canvas/DePreview.vue @@ -511,8 +511,7 @@ const dataVPreview = computed( const linkOptBarShow = computed(() => { return Boolean( canvasStyleData.value.suspensionButtonAvailable && - !inMobile.value && - !mobileInPc.value && + ((!inMobile.value && !mobileInPc.value) || !isDashboard()) && showPopBar.value && !isDesktopFlag ) diff --git a/core/core-frontend/src/custom-component/indicator/DeIndicator.vue b/core/core-frontend/src/custom-component/indicator/DeIndicator.vue index 53e3fa9c30..bb8c53aa9a 100644 --- a/core/core-frontend/src/custom-component/indicator/DeIndicator.vue +++ b/core/core-frontend/src/custom-component/indicator/DeIndicator.vue @@ -255,6 +255,7 @@ const showSuffix = ref(DEFAULT_INDICATOR_STYLE.suffixEnable) const suffixContent = ref('') const indicatorNameShow = ref(false) +const indicatorNamePositionBottom = ref(true) const indicatorNameWrapperStyle = reactive({ 'margin-top': DEFAULT_INDICATOR_NAME_STYLE.nameValueSpacing + 'px' @@ -369,8 +370,10 @@ const renderChart = async view => { } indicatorNameWrapperStyle['margin-top'] = (indicatorName.nameValueSpacing ?? DEFAULT_INDICATOR_NAME_STYLE.nameValueSpacing) + 'px' + indicatorNamePositionBottom.value = indicatorName.namePosition !== 'top' } else { indicatorNameShow.value = false + indicatorNamePositionBottom.value = false } } } @@ -595,11 +598,16 @@ defineExpose({ @trackClick="trackClick" :is-data-v-mobile="dataVMobile" /> +
+ {{ resultName }} +
+
{{ formattedResult }} {{ suffixContent }}
-
+
+
{{ resultName }}
diff --git a/core/core-frontend/src/custom-component/v-query/Select.vue b/core/core-frontend/src/custom-component/v-query/Select.vue index fc91c77c5a..58c9d7b825 100644 --- a/core/core-frontend/src/custom-component/v-query/Select.vue +++ b/core/core-frontend/src/custom-component/v-query/Select.vue @@ -348,6 +348,11 @@ const handleFieldIdChange = (val: EnumValue) => { } }) customSort() + if (!res?.length) { + options.value = [] + selectValue.value = config.value.multiple ? [] : undefined + config.value.defaultValue = selectValue.value + } }) .finally(() => { loading.value = false diff --git a/core/core-frontend/src/locales/en.ts b/core/core-frontend/src/locales/en.ts index 62c7b2f92f..07bc1f3b2a 100644 --- a/core/core-frontend/src/locales/en.ts +++ b/core/core-frontend/src/locales/en.ts @@ -1821,6 +1821,9 @@ export default { dimension_text_style: 'Name style', dimension_letter_space: 'Name letter spacing', name_value_spacing: 'Name/value spacing', + name_position: 'Position', + name_position_top: 'Top', + name_position_bottom: 'Bottom', font_family: 'Font', letter_space: 'Letter spacing', font_shadow: 'Font shadow', @@ -2117,7 +2120,10 @@ export default { table_field_total_label: 'Field Alias', table_row_header_freeze: 'Row Header Freeze', value_formatter_total_out_percent: 'Show percentage', - enable_slider_tip: 'After enabling the slider, the carousel prompt will be disabled.' + enable_slider_tip: 'After enabling the slider, the carousel prompt will be disabled.', + liquid_show_border: 'Show Border', + liquid_border_width: 'Border Width', + liquid_border_distance: 'Border Distance' }, dataset: { field_value: 'Field Value', diff --git a/core/core-frontend/src/locales/tw.ts b/core/core-frontend/src/locales/tw.ts index 426959db27..19e2ac4ed0 100644 --- a/core/core-frontend/src/locales/tw.ts +++ b/core/core-frontend/src/locales/tw.ts @@ -1777,6 +1777,9 @@ export default { dimension_text_style: '名稱樣式', dimension_letter_space: '名稱字間距', name_value_spacing: '名稱/值間距', + name_position: '位置', + name_position_top: '上方', + name_position_bottom: '下方', font_family: '字體', letter_space: '字間距', font_shadow: '字體陰影', @@ -2058,7 +2061,10 @@ export default { table_field_total_label: '字段別名', table_row_header_freeze: '行頭凍結', value_formatter_total_out_percent: '顯示佔比', - enable_slider_tip: '開啟縮略軸後,輪播提示將會失效' + enable_slider_tip: '開啟縮略軸後,輪播提示將會失效', + liquid_show_border: '顯示邊框', + liquid_border_width: '邊框寬度', + liquid_border_distance: '邊框間距' }, dataset: { field_value: '欄位值', diff --git a/core/core-frontend/src/locales/zh-CN.ts b/core/core-frontend/src/locales/zh-CN.ts index 752315cd5d..c2bb280be6 100644 --- a/core/core-frontend/src/locales/zh-CN.ts +++ b/core/core-frontend/src/locales/zh-CN.ts @@ -1785,6 +1785,9 @@ export default { dimension_text_style: '名称样式', dimension_letter_space: '名称字间距', name_value_spacing: '名称/值间距', + name_position: '位置', + name_position_top: '上方', + name_position_bottom: '下方', font_family: '字体', letter_space: '字间距', font_shadow: '字体阴影', @@ -2067,7 +2070,10 @@ export default { table_field_total_label: '字段别名', table_row_header_freeze: '行头冻结', value_formatter_total_out_percent: '显示占比', - enable_slider_tip: '开启缩略轴后,轮播提示将会失效' + enable_slider_tip: '开启缩略轴后,轮播提示将会失效', + liquid_show_border: '显示边框', + liquid_border_width: '边框宽度', + liquid_border_distance: '边框间距' }, dataset: { field_value: '字段值', diff --git a/core/core-frontend/src/models/chart/chart-attr.d.ts b/core/core-frontend/src/models/chart/chart-attr.d.ts index 52d924d7ee..eeb7916a68 100644 --- a/core/core-frontend/src/models/chart/chart-attr.d.ts +++ b/core/core-frontend/src/models/chart/chart-attr.d.ts @@ -306,6 +306,7 @@ declare interface ChartBasicStyle { show: boolean field: string summary: string + originName?: string }> /** * 符号地图符号大小最小值 @@ -729,6 +730,18 @@ declare interface ChartMiscAttr { * 水波图形状 */ liquidShape: string + /** + * 水波图边框显示 + */ + liquidShowBorder: boolean + /** + * 水波图边框宽度 + */ + liquidBorderWidth: number + /** + * 水波图边框距离 + */ + liquidBorderDistance: number /** * 地图倾角 */ @@ -1301,6 +1314,10 @@ declare interface ChartIndicatorNameStyle { * 指标/名称间距 */ nameValueSpacing: number + /** + * 指标名称位置 + */ + namePosition?: 'top' | 'bottom' } /** diff --git a/core/core-frontend/src/models/chart/chart.d.ts b/core/core-frontend/src/models/chart/chart.d.ts index 544d413060..81f9aff462 100644 --- a/core/core-frontend/src/models/chart/chart.d.ts +++ b/core/core-frontend/src/models/chart/chart.d.ts @@ -33,6 +33,7 @@ declare interface Chart { tableRow: [] } customCalc: any + customSumResult?: Record } xAxis?: Axis[] xAxisExt?: Axis[] diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/ChartStyle.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/ChartStyle.vue index d0b2e5d59d..4b0d35df0e 100644 --- a/core/core-frontend/src/views/chart/components/editor/editor-style/ChartStyle.vue +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/ChartStyle.vue @@ -9,7 +9,6 @@ import YAxisSelector from '@/views/chart/components/editor/editor-style/componen import DualYAxisSelector from '@/views/chart/components/editor/editor-style/components/DualYAxisSelector.vue' import TitleSelector from '@/views/chart/components/editor/editor-style/components/TitleSelector.vue' import LegendSelector from '@/views/chart/components/editor/editor-style/components/LegendSelector.vue' -import SummarySelector from '@/views/chart/components/editor/editor-style/components/SummarySelector.vue' import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain' import { storeToRefs } from 'pinia' import CollapseSwitchItem from '@/components/collapse-switch-item/src/CollapseSwitchItem.vue' @@ -22,6 +21,7 @@ import BackgroundOverallCommon from '@/components/visualization/component-backgr import TableHeaderSelector from '@/views/chart/components/editor/editor-style/components/table/TableHeaderSelector.vue' import TableCellSelector from '@/views/chart/components/editor/editor-style/components/table/TableCellSelector.vue' import TableTotalSelector from '@/views/chart/components/editor/editor-style/components/table/TableTotalSelector.vue' +import SummarySelector from '@/views/chart/components/editor/editor-style/components/table/SummarySelector.vue' import MiscStyleSelector from '@/views/chart/components/editor/editor-style/components/MiscStyleSelector.vue' import IndicatorValueSelector from '@/views/chart/components/editor/editor-style/components/IndicatorValueSelector.vue' import IndicatorNameSelector from '@/views/chart/components/editor/editor-style/components/IndicatorNameSelector.vue' diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/components/IndicatorNameSelector.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/components/IndicatorNameSelector.vue index 4b84d16845..0d9d14df6c 100644 --- a/core/core-frontend/src/views/chart/components/editor/editor-style/components/IndicatorNameSelector.vue +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/components/IndicatorNameSelector.vue @@ -45,6 +45,11 @@ const fontFamily = CHART_FONT_FAMILY_ORIGIN.concat( ) const fontLetterSpace = CHART_FONT_LETTER_SPACE +const namePositionList = [ + { name: t('chart.name_position_top'), value: 'top' }, + { name: t('chart.name_position_bottom'), value: 'bottom' } +] + const state = reactive({ indicatorNameForm: JSON.parse(JSON.stringify(DEFAULT_INDICATOR_NAME_STYLE)), basicStyleForm: {} as ChartBasicStyle @@ -275,6 +280,27 @@ defineExpose({ getFormData }) @change="changeTitleStyle('nameValueSpacing')" /> + + + + + diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/components/MiscSelector.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/components/MiscSelector.vue index ee6ef95496..d01d2d5afe 100644 --- a/core/core-frontend/src/views/chart/components/editor/editor-style/components/MiscSelector.vue +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/components/MiscSelector.vue @@ -553,6 +553,59 @@ onMounted(() => { + + + {{ t('chart.liquid_show_border') }} + + + + + + + + + + + + + + import { useI18n } from '@/hooks/web/useI18n' -import { computed, onMounted, PropType, reactive, watch } from 'vue' +import { computed, onMounted, PropType, reactive, watch, ref, nextTick, inject } from 'vue' import { DEFAULT_BASIC_STYLE } from '@/views/chart/components/editor/util/chart' import { cloneDeep, defaultsDeep, filter, find } from 'lodash-es' import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain' import { storeToRefs } from 'pinia' +import CustomAggrEdit from './CustomAggrEdit.vue' + const dvMainStore = dvMainStoreWithOut() const { batchOptStatus } = storeToRefs(dvMainStore) const { t } = useI18n() @@ -30,6 +32,7 @@ const state = reactive({ show: boolean field: string summary: string + originName?: string } }) @@ -37,7 +40,12 @@ const emit = defineEmits(['onBasicStyleChange']) const changeBasicStyle = (prop?: string, requestData = false) => { emit('onBasicStyleChange', { data: state.basicStyleForm, requestData }, prop) } - +const changeSummaryType = () => { + if (state.currentAxisSummary.summary === 'custom' && !state.currentAxisSummary.originName) { + return + } + changeBasicStyle('seriesSummary') +} watch( [ () => props.chart.customAttr.basicStyle.showSummary, @@ -65,15 +73,38 @@ const summaryTypes = [ { key: 'sum', name: t('chart.sum') }, { key: 'avg', name: t('chart.avg') }, { key: 'max', name: t('chart.max') }, - { key: 'min', name: t('chart.min') } - // { key: 'stddev_pop', name: t('chart.stddev_pop') }, - // { key: 'var_pop', name: t('chart.var_pop') } + { key: 'min', name: t('chart.min') }, + { key: 'custom', name: t('commons.custom') } ] function onSelectAxis(value) { state.currentAxisSummary = find(state.basicStyleForm.seriesSummary, s => s.field === value) } +const calcEdit = ref() +const editCalcField = ref(false) +const dimension = inject('dimension', () => []) +const quota = inject('quota', () => []) +const editField = () => { + editCalcField.value = true + nextTick(() => { + calcEdit.value.initEdit( + state.currentAxisSummary, + quota().filter(ele => ele.id !== '-1') + ) + }) +} +const closeEditCalc = () => { + editCalcField.value = false +} +const confirmEditCalc = () => { + calcEdit.value.setFieldForm() + const obj = cloneDeep(calcEdit.value.fieldForm) + state.currentAxisSummary.originName = obj.originName + setFieldDefaultValue(state.currentAxisSummary) + closeEditCalc() + changeSummaryType() +} const init = () => { const basicStyle = cloneDeep(props.chart.customAttr.basicStyle) @@ -112,7 +143,14 @@ const init = () => { state.currentAxisSummary = undefined } } - +const setFieldDefaultValue = field => { + field.extField = 2 + field.chartId = props.chart.id + field.datasetGroupId = props.chart.tableId + field.lastSyncTime = null + field.columnIndex = dimension().length + quota().length + field.deExtractType = field.deType +} onMounted(() => { init() }) @@ -173,21 +211,41 @@ onMounted(() => {
- - - + + + + + + + + + +
+ + + + +