[Core] [Visual] Add radar

This commit is contained in:
qianmoQ 2024-04-14 17:59:25 +08:00
parent c96280aa91
commit 12e3f6897c
7 changed files with 203 additions and 1 deletions

View File

@ -61,6 +61,7 @@ export default {
visualTypeHistogram: 'Histogram',
visualTypeWordCloud: 'Word Cloud',
visualTypeScatter: 'Scatter',
visualTypeRadar: 'Radar',
visualConfigure: 'Visual Configure',
visualConfigureNotSpecified: 'No configuration items are available',
visualConfigureXAxis: 'X Axis',

View File

@ -61,6 +61,7 @@ export default {
visualTypeHistogram: '直方图',
visualTypeWordCloud: '词云图',
visualTypeScatter: '散点图',
visualTypeRadar: '雷达图',
visualConfigure: '可视化配置',
visualConfigureNotSpecified: '暂无可用配置项',
visualConfigureXAxis: 'X轴',

View File

@ -31,3 +31,12 @@ export interface IChart
outerRadius?: number[]
invalidType?: string
}
export interface ChartField
{
label?: string
field?: string
value?: any
type?: string
defaultValues?: any[]
}

View File

@ -8,4 +8,5 @@ export enum Type
HISTOGRAM = ('HISTOGRAM'),
WORDCLOUD = ('WORDCLOUD'),
SCATTER = ('SCATTER'),
RADAR = ('RADAR')
}

View File

@ -14,6 +14,7 @@
<VisualHistogram v-else-if="configuration?.type === Type.HISTOGRAM" :configuration="configuration" @change="handlerCommit"/>
<VisualWordCloud v-else-if="configuration?.type === Type.WORDCLOUD" :configuration="configuration" @change="handlerCommit"/>
<VisualScatter v-else-if="configuration?.type === Type.SCATTER" :configuration="configuration" @change="handlerCommit"/>
<VisualRadar v-else-if="configuration?.type === Type.RADAR" :configuration="configuration" @change="handlerCommit"/>
</div>
</div>
</div>
@ -125,6 +126,18 @@
</svg>
</Tooltip>
</ToggleGroupItem>
<ToggleGroupItem :value="Type.RADAR">
<Tooltip :content="$t('dataset.common.visualTypeRadar')">
<svg width="22" height="21" viewBox="0 0 22 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M1.57045 8.16646L10.3949 1.45986C10.7525 1.18808 11.2475 1.18808 11.6051 1.45986L20.4296 8.16646C20.7706 8.42565 20.9087 8.87328 20.7729 9.27956L17.4187 19.3169C17.2824 19.7249 16.9004 20 16.4703 20H5.52971C5.09956 20 4.7176 19.7249 4.58127 19.3169L1.22709 9.27956C1.09132 8.87328 1.2294 8.42565 1.57045 8.16646Z"
stroke="#21252C" stroke-width="1.7" stroke-linecap="round"></path>
<path
d="M6.11243 9.82863L10.8826 6.2478C10.951 6.19642 11.0446 6.19428 11.1153 6.24249L16.3379 9.80252C16.4279 9.8639 16.4522 9.98606 16.3926 10.0773L13.5468 14.4281C13.5169 14.4739 13.4696 14.5054 13.4158 14.5153L7.91428 15.5328C7.81444 15.5513 7.71661 15.492 7.68675 15.395L6.04134 10.0474C6.01654 9.96678 6.04498 9.87927 6.11243 9.82863Z"
stroke="#21252C" stroke-width="1.7" stroke-linecap="round"></path>
</svg>
</Tooltip>
</ToggleGroupItem>
</div>
</ToggleGroup>
</div>
@ -140,6 +153,8 @@
<VisualHistogramConfigure v-else-if="configuration.type === Type.HISTOGRAM" :configuration="configuration" @change="configuration.chartConfigure = $event"/>
<VisualWordCloudConfigure v-else-if="configuration.type === Type.WORDCLOUD" :configuration="configuration" @change="configuration.chartConfigure = $event"/>
<VisualScatterConfigure v-else-if="configuration.type === Type.SCATTER" :configuration="configuration" @change="configuration.chartConfigure = $event"/>
<VisualConfigure v-else-if="configuration.type === Type.RADAR" :configuration="configuration" :fields="forwardFiled(configuration.type)"
@change="configuration.chartConfigure = $event"/>
<Alert v-else :title="$t('dataset.common.visualConfigureNotSpecified')"/>
</div>
</Card>
@ -152,7 +167,7 @@ import VisualWordCloud from '@/views/components/visual/components/VisualWordClou
import VisualHistogram from '@/views/components/visual/components/VisualHistogram.vue'
import VisualPie from '@/views/components/visual/components/VisualPie.vue'
import VisualArea from '@/views/components/visual/components/VisualArea.vue'
import { Configuration } from './Configuration'
import { ChartField, Configuration } from './Configuration'
import VisualBar from '@/views/components/visual/components/VisualBar.vue'
import VisualLine from '@/views/components/visual/components/VisualLine.vue'
import VisualTable from '@/views/components/visual/components/VisualTable.vue'
@ -172,6 +187,8 @@ import VisualHistogramConfigure from '@/views/components/visual/components/Visua
import VisualWordCloudConfigure from '@/views/components/visual/components/VisualWordCloudConfigure.vue'
import VisualScatter from '@/views/components/visual/components/VisualScatter.vue'
import VisualScatterConfigure from '@/views/components/visual/components/VisualScatterConfigure.vue'
import VisualConfigure from '@/views/components/visual/components/VisualConfigure.vue'
import VisualRadar from '@/views/components/visual/components/VisualRadar.vue'
export default defineComponent({
name: 'VisualEditor',
@ -182,6 +199,8 @@ export default defineComponent({
}
},
components: {
VisualRadar,
VisualConfigure,
VisualScatterConfigure,
VisualScatter,
VisualWordCloudConfigure,
@ -212,6 +231,18 @@ export default defineComponent({
handlerCommit(value: any)
{
this.$emit('commitOptions', value)
},
forwardFiled(type: Type): ChartField[]
{
const fields: Array<ChartField> = new Array<ChartField>()
const categoryField: ChartField = { label: this.$t('dataset.common.visualConfigureCategoryField'), field: 'xAxis', value: undefined }
const valueField: ChartField = { label: this.$t('dataset.common.visualConfigureValueField'), field: 'yAxis', value: undefined }
switch (type) {
case Type.RADAR:
fields.push(categoryField, valueField)
break
}
return fields
}
}
})

View File

@ -0,0 +1,80 @@
<template>
<div v-if="configuration && formState" class="space-y-2">
<FormField v-for="item in fields" class="flex items-center" :name="item.field as string">
<FormItem class="flex-1">
<div class="flex items-center">
<FormLabel class="mr-1 w-2/3 text-right">{{ item.label }}</FormLabel>
<FormControl>
<Select v-model="formState[item.field as keyof IChart] as string" :disabled="configuration.headers.length === 0">
<SelectTrigger class="w-full">
<SelectValue :placeholder="`Select ${item.label}`"/>
</SelectTrigger>
<SelectContent>
<SelectItem v-if="item.defaultValues" v-for="data in item.defaultValues" :value="data as string">{{ data }}</SelectItem>
<SelectItem v-else v-for="item in configuration.headers" :value="item as string">{{ item }}</SelectItem>
</SelectContent>
</Select>
</FormControl>
</div>
</FormItem>
</FormField>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from '@/components/ui/form'
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from '@/components/ui/select'
import { ChartField, Configuration, IChart } from '@/views/components/visual/Configuration.ts'
import { cloneDeep, keys } from 'lodash'
export default defineComponent({
name: 'VisualConfigure',
components: {
SelectGroup, SelectTrigger, SelectContent, SelectItem, Select, SelectLabel, SelectValue,
FormDescription, FormControl, FormLabel, FormField, FormItem
},
props: {
configuration: {
type: Object as () => Configuration
},
fields: {
type: Array as () => ChartField[],
default: []
}
},
watch: {
formState: {
handler: 'handlerCommit',
deep: true
}
},
data()
{
return {
formState: null as IChart | null
}
},
created()
{
if (this.configuration && keys(this.configuration.chartConfigure).length > 0) {
this.formState = cloneDeep(this.configuration.chartConfigure) as IChart
}
else {
const obj = {} as any
this.fields.forEach(field => {
if (field.field) {
obj[field.field] = undefined
}
})
this.formState = obj
}
},
methods: {
handlerCommit()
{
this.$emit('change', this.formState)
}
}
})
</script>

View File

@ -0,0 +1,79 @@
<template>
<div ref="content" :style="{width: width, height: height}"/>
</template>
<script lang="ts">
import VChart from '@visactor/vchart'
import { cloneDeep } from 'lodash'
import { Configuration } from '../Configuration'
import { defineComponent } from 'vue'
let instance: VChart
export default defineComponent({
name: 'VisualRadar',
props: {
configuration: {
type: Object as () => Configuration
},
submitted: {
type: Boolean,
default: true
},
width: {
type: String,
default: () => '100%'
},
height: {
type: String,
default: () => '400px'
}
},
watch: {
configuration: {
handler: 'handlerReset',
deep: true
}
},
created()
{
this.handlerInitialize(false)
},
methods: {
handlerInitialize(reset: boolean)
{
setTimeout(() => {
try {
if (this.configuration) {
const options = {
type: 'radar',
data: { values: this.configuration.columns },
categoryField: this.configuration.chartConfigure?.xAxis,
valueField: this.configuration.chartConfigure?.yAxis
} as any
if (!reset) {
instance = new VChart(options, { dom: this.$refs.content as HTMLElement })
instance.renderAsync()
}
else {
instance.updateSpec(options, true)
}
if (this.submitted) {
const cloneOptions = cloneDeep(this.configuration)
cloneOptions.headers = []
cloneOptions.columns = []
this.$emit('change', cloneOptions)
}
}
}
catch (e) {
console.warn(e)
}
})
},
handlerReset()
{
this.handlerInitialize(true)
}
}
})
</script>