Merge branch 'dev-v2' into pr@dev-v2_export_data

This commit is contained in:
taojinlong 2024-05-29 15:24:49 +08:00
commit 563f23dd07
20 changed files with 279 additions and 79 deletions

View File

@ -7,7 +7,7 @@ import java.util.Optional;
*/
public class SQLUtils {
public static String transKeyword(String value) {
return Optional.ofNullable(value).orElse("").replaceAll("'", "\\\\'");
return Optional.ofNullable(value).orElse("").replaceAll("'", "''");
}
public static String buildOriginPreviewSql(String sql, int limit, int offset) {

View File

@ -1391,6 +1391,7 @@ onMounted(() => {
if (isMainCanvas(canvasId.value)) {
initSnapshotTimer()
initWatermark()
dvMainStore.setEditMode('edit')
}
//
composeStore.getEditor(canvasId.value)

View File

@ -319,13 +319,13 @@ const scrollSpeedList = [
{ name: '1', value: 80 },
{ name: '2', value: 60 },
{ name: '3', value: 40 },
{ name: '4', value: 20 },
{ name: '5', value: 10 },
{ name: '6', value: 8 },
{ name: '7', value: 6 },
{ name: '8', value: 4 },
{ name: '9', value: 2 },
{ name: '10', value: 1 }
{ name: '4', value: 30 },
{ name: '5', value: 20 },
{ name: '6', value: 15 },
{ name: '7', value: 10 },
{ name: '8', value: 8 },
{ name: '9', value: 6 },
{ name: '10', value: 3 }
]
const opacitySizeList = [

View File

@ -78,8 +78,6 @@ const clearStyle = e => {
if (text !== '') {
document.execCommand('insertText', false, text)
}
emit('input', element.value, e.target.innerHTML)
}
const handleBlur = e => {
@ -141,11 +139,12 @@ const textStyle = computed(() => {
@mousedown="handleMousedown"
@blur="handleBlur"
@input="handleInput"
v-html="element['propValue']"
></div>
>
{{ element['propValue'] }}
</div>
</div>
<div v-else class="v-text preview">
<div class="marquee-txt" :style="textStyle" v-html="element['propValue']"></div>
<div v-else class="v-text preview" :style="varStyle">
<div class="marquee-txt" :style="textStyle">{{ element['propValue'] }}</div>
</div>
</template>

View File

@ -1165,7 +1165,9 @@ export default {
top_n_input_2: ', 其余合并至其他',
top_n_label: '其他项名称',
progress_target: '目标值',
progress_current: '实际值'
progress_current: '实际值',
gauge_axis_label: '显示刻度',
gauge_percentage_tick: '百分比刻度'
},
dataset: {
scope_edit: '仅编辑时生效',

View File

@ -104,6 +104,14 @@ declare interface ChartBasicStyle {
* 仪表盘样式
*/
gaugeStyle: string
/**
* 仪表盘刻度显示
*/
gaugeAxisLine: boolean
/**
* 仪表盘百分比刻度
*/
gaugePercentLabel: boolean
/**
* 配色方案
*/

View File

@ -33,9 +33,9 @@ const winMsgHandle = event => {
const msgInfo = event.data
// targetSourceId
if (msgInfo && msgInfo.type === 'attachParams' && msgInfo.targetSourceId === state.chartId + '') {
const attachParam = msgInfo.params
if (attachParam) {
dvMainStore.addOuterParamsFilter(attachParam, state.canvasDataPreview, 'outer')
const attachParams = msgInfo.params
if (attachParams) {
dvMainStore.addOuterParamsFilter(attachParams, state.canvasDataPreview, 'outer')
}
}
}
@ -58,7 +58,7 @@ onBeforeMount(async () => {
window.addEventListener('message', winMsgHandle)
//
let attachParam
let attachParams
await getOuterParamsInfo(embeddedStore.dvId).then(rsp => {
dvMainStore.setNowPanelOuterParamsInfo(rsp.data)
})
@ -67,7 +67,7 @@ onBeforeMount(async () => {
if (embeddedStore.outerParams) {
try {
const outerPramsParse = JSON.parse(embeddedStore.outerParams)
attachParam = outerPramsParse.attachParam
attachParams = outerPramsParse.attachParams
dvMainStore.setEmbeddedCallBack(outerPramsParse.callBackFlag || 'no')
} catch (e) {
console.error(e)
@ -90,8 +90,8 @@ onBeforeMount(async () => {
state.canvasViewInfoPreview = canvasViewInfoPreview
state.dvInfo = dvInfo
state.curPreviewGap = curPreviewGap
if (attachParam) {
dvMainStore.addOuterParamsFilter(attachParam, canvasDataResult)
if (attachParams) {
dvMainStore.addOuterParamsFilter(attachParams, canvasDataResult)
}
viewInfo.value = canvasViewInfoPreview[embeddedStore.chartId]

View File

@ -33,7 +33,7 @@ export const dvMainStore = defineStore('dataVisualization', {
datasetAreaCollapse: false
},
embeddedCallBack: 'no', // 嵌入模式是否允许反馈参数
editMode: 'edit', // 编辑器模式 edit preview
editMode: 'preview', // 编辑器模式 edit preview
mobileInPc: false,
firstLoadMap: [],
canvasStyleData: { ...deepCopy(DEFAULT_CANVAS_STYLE_DATA_DARK), backgroundColor: null },

View File

@ -231,7 +231,12 @@ init()
<div @keydown.stop @keyup.stop style="width: 100%; margin-bottom: 16px">
<!--仪表盘-->
<el-col v-show="showProperty('gaugeThreshold')">
<el-form ref="thresholdForm" :model="state.thresholdForm" label-position="top">
<el-form
:model="state.thresholdForm"
ref="thresholdForm"
label-position="top"
@submit.prevent
>
<el-form-item
:label="t('chart.threshold_range') + '(%)'"
class="form-item"
@ -261,7 +266,12 @@ init()
</el-form>
</el-col>
<el-col v-show="showProperty('liquidThreshold')">
<el-form ref="thresholdForm" :model="state.thresholdForm" label-position="top">
<el-form
:model="state.thresholdForm"
ref="thresholdForm"
label-position="top"
@submit.prevent
>
<el-form-item
:label="t('chart.threshold_range') + '(%)'"
class="form-item"

View File

@ -777,6 +777,34 @@ onMounted(() => {
/>
</el-select>
</el-form-item>
<el-form-item
v-if="showProperty('gaugeAxisLine')"
class="form-item"
:class="'form-item-' + themes"
>
<el-checkbox
v-model="state.basicStyleForm.gaugeAxisLine"
:effect="themes"
size="small"
@change="changeBasicStyle('gaugeAxisLine')"
>
{{ t('chart.gauge_axis_label') }}</el-checkbox
>
</el-form-item>
<el-form-item
v-if="showProperty('gaugePercentLabel') && state.basicStyleForm.gaugeAxisLine"
class="form-item"
:class="'form-item-' + themes"
>
<el-checkbox
v-model="state.basicStyleForm.gaugePercentLabel"
:effect="themes"
size="small"
@change="changeBasicStyle('gaugePercentLabel')"
>
{{ t('chart.gauge_percentage_tick') }}</el-checkbox
>
</el-form-item>
<!--gauge end-->
<!--bar start-->
<el-form-item

View File

@ -3291,7 +3291,7 @@ span {
}
:deep(.ed-tabs__content) {
height: calc(100% - 33px);
height: calc(100% - 35px);
overflow-y: auto;
overflow-x: hidden;
}

View File

@ -1435,7 +1435,9 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
tableLayoutMode: 'grid',
calcTopN: false,
topN: 5,
topNLabel: '其他'
topNLabel: '其他',
gaugeAxisLine: true,
gaugePercentLabel: true
}
export const BASE_VIEW_CONFIG = {

View File

@ -95,7 +95,8 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
// 禁用线上地图数据
customFetchGeoData: () => null
}
options = this.setupOptions(chart, options, drawOption, geoJson)
const context = { drawOption, geoJson }
options = this.setupOptions(chart, options, context)
const view = new Choropleth(container, options)
const dotLayer = this.getDotLayer(chart, geoJson, drawOption)
this.configZoomButton(chart, view)
@ -170,10 +171,10 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
private configBasicStyle(
chart: Chart,
options: ChoroplethOptions,
extra: any[]
context: Record<string, any>
): ChoroplethOptions {
const { areaId }: L7PlotDrawOptions<any> = extra[0]
const geoJson: FeatureCollection = extra[1]
const { areaId }: L7PlotDrawOptions<any> = context.drawOption
const geoJson: FeatureCollection = context.geoJson
const { basicStyle, label } = parseJson(chart.customAttr)
const senior = parseJson(chart.senior)
const curAreaNameMapping = senior.areaMapping?.[areaId]
@ -208,7 +209,7 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
protected setupOptions(
chart: Chart,
options: ChoroplethOptions,
...extra: any[]
context: Record<string, any>
): ChoroplethOptions {
return flow(
this.configEmptyDataStrategy,
@ -216,6 +217,6 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
this.configStyle,
this.configTooltip,
this.configBasicStyle
)(chart, options, extra)
)(chart, options, context)
}
}

View File

@ -96,7 +96,8 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
// 禁用线上地图数据
customFetchGeoData: () => null
}
options = this.setupOptions(chart, options, drawOption, geoJson)
const context = { drawOption, geoJson }
options = this.setupOptions(chart, options, context)
const view = new Choropleth(container, options)
this.configZoomButton(chart, view)
view.once('loaded', () => {
@ -120,10 +121,10 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
private configBasicStyle(
chart: Chart,
options: ChoroplethOptions,
extra: any[]
context: Record<string, any>
): ChoroplethOptions {
const { areaId }: L7PlotDrawOptions<any> = extra[0]
const geoJson: FeatureCollection = extra[1]
const { areaId }: L7PlotDrawOptions<any> = context.drawOption
const geoJson: FeatureCollection = context.geoJson
const { basicStyle, label } = parseJson(chart.customAttr)
const senior = parseJson(chart.senior)
const curAreaNameMapping = senior.areaMapping?.[areaId]
@ -176,7 +177,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
protected setupOptions(
chart: Chart,
options: ChoroplethOptions,
...extra: any[]
context: Record<string, any>
): ChoroplethOptions {
return flow(
this.configEmptyDataStrategy,
@ -185,6 +186,6 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
this.configTooltip,
this.configBasicStyle,
this.configLegend
)(chart, options, extra)
)(chart, options, context)
}
}

View File

@ -13,6 +13,7 @@ import {
import { valueFormatter } from '@/views/chart/components/js/formatter'
import { getPadding, setGradientColor } from '@/views/chart/components/js/panel/common/common_antv'
import { useI18n } from '@/hooks/web/useI18n'
import { merge } from 'lodash-es'
const { t } = useI18n()
@ -28,7 +29,7 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
]
propertyInner: EditorPropertyInner = {
'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha', 'gaugeStyle', 'gradient'],
'basic-style-selector': ['colors', 'alpha', 'gradient', 'gaugeAxisLine', 'gaugePercentLabel'],
'label-selector': ['fontSize', 'color', 'labelFormatter'],
'title-selector': [
'title',
@ -77,10 +78,6 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
label: {
style: {
fontSize: getScaleValue(12, scale) // 刻度值字体大小
},
formatter: function (v) {
const r = parseFloat(v)
return v === '0' || !r ? v : r * 100 + '%'
}
},
tickLine: {
@ -98,11 +95,15 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
}
}
}
const options = this.setupOptions(chart, initOptions, scale)
const options = this.setupOptions(chart, initOptions, { scale })
return new G2Gauge(container, options)
}
protected configMisc(chart: Chart, options: GaugeOptions): GaugeOptions {
protected configMisc(
chart: Chart,
options: GaugeOptions,
context: Record<string, any>
): GaugeOptions {
const customAttr = parseJson(chart.customAttr)
const data = chart.data.series[0].data[0]
let min, max, startAngle, endAngle
@ -123,6 +124,8 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
}
startAngle = (misc.gaugeStartAngle * Math.PI) / 180
endAngle = (misc.gaugeEndAngle * Math.PI) / 180
context.min = min
context.max = max
}
const percent = (parseFloat(data) - parseFloat(min)) / (parseFloat(max) - parseFloat(min))
const tmp = {
@ -133,8 +136,12 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
return { ...options, ...tmp }
}
private configRange(chart: Chart, options: GaugeOptions, extra: any[]): GaugeOptions {
const [scale] = extra
private configRange(
chart: Chart,
options: GaugeOptions,
context: Record<string, any>
): GaugeOptions {
const { scale } = context
const range = [0]
let index = 0
let flag = false
@ -216,36 +223,55 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
return { ...options, ...rangOptions }
}
protected configLabel(chart: Chart, options: GaugeOptions): GaugeOptions {
protected configLabel(
chart: Chart,
options: GaugeOptions,
context?: Record<string, any>
): GaugeOptions {
const customAttr = parseJson(chart.customAttr)
const data = chart.data.series[0].data[0]
let labelContent
if (customAttr.label) {
const label = customAttr.label
const labelFormatter = label.labelFormatter ?? DEFAULT_LABEL.labelFormatter
if (label.show) {
labelContent = {
style: () => ({
fontSize: label.fontSize,
color: label.color
}),
formatter: function () {
let value
if (labelFormatter.type === 'percent') {
value = options.percent
} else {
value = data
}
return valueFormatter(value, labelFormatter)
let labelContent: GaugeOptions['statistic']['content'] = false
const label = customAttr.label
const labelFormatter = label.labelFormatter ?? DEFAULT_LABEL.labelFormatter
if (label.show) {
labelContent = {
style: {
fontSize: `${label.fontSize}`,
color: label.color
},
formatter: function () {
let value
if (labelFormatter.type === 'percent') {
value = options.percent
} else {
value = data
}
return valueFormatter(value, labelFormatter)
}
} else {
labelContent = false
}
} as GaugeOptions['statistic']['content']
}
const statistic = {
content: labelContent
}
const { gaugeAxisLine, gaugePercentLabel } = customAttr.basicStyle
const { min, max } = context
const tmp = {
axis: {
label: {
formatter: v => {
if (gaugeAxisLine === false) {
return ''
}
if (gaugePercentLabel === false) {
const val = v === '0' ? min : v === '1' ? max : min + (max - min) * v
return valueFormatter(val, labelFormatter)
}
return v === '0' ? v : v * 100 + '%'
}
}
}
}
options = merge(options, tmp)
return { ...options, statistic }
}
@ -263,13 +289,17 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
return chart
}
protected setupOptions(chart: Chart, options: GaugeOptions, ...extra: any[]): GaugeOptions {
protected setupOptions(
chart: Chart,
options: GaugeOptions,
context: Record<string, any>
): GaugeOptions {
return flow(
this.configTheme,
this.configMisc,
this.configLabel,
this.configRange
)(chart, options, extra)
)(chart, options, context)
}
constructor() {
super('gauge', DEFAULT_DATA)

View File

@ -139,7 +139,7 @@ export abstract class G2PlotChartView<
* @param chart 数据库图表对象
* @param options 各个图表的参数泛化的 Options可以自行扩展比如加个扩展 X 轴或者扩展 Y 轴字段
*/
protected abstract setupOptions(chart: Chart, options: O): O
protected abstract setupOptions(chart: Chart, options: O, context?: Record<string, any>): O
protected constructor(name: string, defaultData: any[]) {
super(ChartLibraryType.G2_PLOT, name, defaultData)
}

View File

@ -81,5 +81,5 @@ export abstract class L7PlotChartView<
super(ChartLibraryType.L7_PLOT, name)
this.defaultData = defaultData
}
protected abstract setupOptions(chart: Chart, options: O): O
protected abstract setupOptions(chart: Chart, options: O, context?: Record<string, any>): O
}

View File

@ -388,12 +388,12 @@ export function parseJson<T>(str: T | JSONString<T>): T {
return JSON.parse(str) as T
}
type FlowFunction<P, R> = (param: P, result: R, extra?: any[]) => R
type FlowFunction<P, R> = (param: P, result: R, context?: Record<string, any>) => R
export function flow<P, R>(...flows: FlowFunction<P, R>[]): FlowFunction<P, R> {
return (param: P, result: R, extra?: any[]) => {
return (param: P, result: R, context?: Record<string, any>) => {
return flows.reduce((result: R, flow: FlowFunction<P, R>) => {
return flow(param, result, extra)
return flow(param, result, context)
}, result)
}
}

View File

@ -1,16 +1,19 @@
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue'
import { ref, computed, onMounted, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { useCache } from '@/hooks/web/useCache'
import treeSort from '@/utils/treeSortUtils'
import { BusiTreeRequest } from '@/models/tree/TreeNode'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
import DashboardCell from '@/views/mobile/components/DashboardCell.vue'
import { useI18n } from '@/hooks/web/useI18n'
import { useRouter } from 'vue-router'
import VanSticky from 'vant/es/sticky'
import VanNavBar from 'vant/es/nav-bar'
import 'vant/es/nav-bar/style'
import 'vant/es/sticky/style'
import { cloneDeep } from 'lodash-es'
const anyManage = ref(false)
const rootManage = ref(false)
const tableData = ref([])
@ -21,6 +24,7 @@ const interactiveStore = interactiveStoreWithOut()
const dvMainStore = dvMainStoreWithOut()
const { dvInfo } = storeToRefs(dvMainStore)
const { wsCache } = useCache('sessionStorage')
const { t } = useI18n()
const dfsTree = (ids, arr) => {
const id = ids.shift()
@ -36,6 +40,8 @@ const dfsTree = (ids, arr) => {
}, [])
}
let rawTableData = []
const activeTableData = computed(() => {
return directId.value.length ? dfsTree([...directId.value], tableData.value) : tableData.value
})
@ -46,6 +52,7 @@ const onClickLeft = () => {
activeDirectName.value = directName.value[directName.value.length - 1]
directId.value.pop()
if (!!directName.value.length) {
tableData.value = cloneDeep(rawTableData)
emits('hiddenTabbar', false)
}
}
@ -70,7 +77,50 @@ const handleCellClick = ele => {
})
}
const filterText = ref('')
const curSortType = ref('time_desc')
const sortList = [
{
name: '按创建时间升序',
value: 'time_asc'
},
{
name: '按创建时间降序',
value: 'time_desc',
divided: true
},
{
name: '按照名称升序',
value: 'name_asc'
},
{
name: '按照名称降序',
value: 'name_desc'
}
]
const sortTypeChange = sortType => {
tableData.value = treeSort(cloneDeep(rawTableData), sortType)
curSortType.value = sortType
}
const searchTree = (tree, val) => {
return tree.filter(ele => {
if (ele.name?.toLocaleLowerCase().includes(val.toLocaleLowerCase())) {
return true
} else if (!!ele.children?.length) {
ele.children = searchTree(ele.children, val)
return !!ele.children.length
}
return false
})
}
watch(filterText, val => {
tableData.value = searchTree(cloneDeep(rawTableData), val)
})
const dataClick = val => {
filterText.value = ''
if (val.leaf) {
emits('hiddenTabbar', true)
handleCellClick(val)
@ -109,9 +159,11 @@ const getTree = async () => {
}
if (nodeData.length && nodeData[0]['id'] === '0' && nodeData[0]['name'] === 'root') {
tableData.value = dfsTableData(nodeData[0]['children'] || [])
rawTableData = cloneDeep(tableData.value)
return
}
tableData.value = dfsTableData(nodeData)
rawTableData = cloneDeep(tableData.value)
}
onMounted(() => {
@ -154,6 +206,41 @@ onMounted(() => {
</div>
</van-sticky>
<div :class="!!directName.length && 'dashboard-cell-group-tab'" class="dashboard-cell-group">
<div class="dashboard-cell-group_filter" v-if="!directName.length">
<el-input
:placeholder="t('commons.search')"
v-model="filterText"
clearable
class="search-bar"
>
<template #prefix>
<el-icon>
<Icon name="icon_search-outline_outlined"></Icon>
</el-icon>
</template>
</el-input>
<el-dropdown @command="sortTypeChange" trigger="click">
<el-icon class="filter-icon-span">
<Icon v-if="curSortType.includes('asc')" name="dv-sort-asc" class="opt-icon"></Icon>
<Icon v-show="curSortType.includes('desc')" name="dv-sort-desc" class="opt-icon"></Icon>
</el-icon>
<template #dropdown>
<el-dropdown-menu style="width: 246px">
<template :key="ele.value" v-for="ele in sortList">
<el-dropdown-item
class="ed-select-dropdown__item"
:class="ele.value === curSortType && 'selected'"
:command="ele.value"
>
{{ ele.name }}
</el-dropdown-item>
<li v-if="ele.divided" class="ed-dropdown-menu__item--divided"></li>
</template>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<DashboardCell
v-for="ele in activeTableData"
:key="ele.id"
@ -175,6 +262,37 @@ onMounted(() => {
height: calc(100vh - 102px);
margin-top: 8px;
.dashboard-cell-group_filter {
padding: 0 8px;
}
.search-bar {
padding-bottom: 8px;
width: calc(100% - 40px);
}
.filter-icon-span {
border: 1px solid #bbbfc4;
width: 32px;
height: 32px;
border-radius: 4px;
color: #1f2329;
padding: 8px;
margin-left: 8px;
font-size: 16px;
cursor: pointer;
.opt-icon:focus {
outline: none !important;
}
&:hover {
background: #f5f6f7;
}
&:active {
background: #eff0f1;
}
}
&.dashboard-cell-group-tab {
margin-top: 0;
height: calc(100vh - 146px);

@ -1 +1 @@
Subproject commit bb62e69151febc7a98ed7865298cddbadb072248
Subproject commit 4f83c4df67befbd90eb219b2d6d04c04e213179f