[Core] [Visual] Set to dialog

This commit is contained in:
qianmoQ 2024-04-15 01:16:11 +08:00
parent 00641f2749
commit 8b81125e68
7 changed files with 146 additions and 101 deletions

View File

@ -83,6 +83,7 @@ export default {
visualConfigureDataBreakpointContinuous: 'Continuous',
visualConfigureDataBreakpointZero: 'Zero',
visualConfigureDataBreakpointIgnore: 'Ignore',
visualConfigureGeneralGroup: 'General Configure',
columnExpressionMax: 'Maximum',
columnExpressionMin: 'Minimum',
columnExpressionSum: 'Sum',

View File

@ -83,6 +83,7 @@ export default {
visualConfigureDataBreakpointContinuous: '连续',
visualConfigureDataBreakpointZero: '补 0',
visualConfigureDataBreakpointIgnore: '忽略',
visualConfigureGeneralGroup: '通用配置',
columnExpressionMax: '最大值',
columnExpressionMin: '最小值',
columnExpressionSum: '总和',

View File

@ -4,8 +4,7 @@
<AlertDialogHeader>
<AlertDialogTitle class="border-b -mt-4 pb-2">SQL</AlertDialogTitle>
</AlertDialogHeader>
<VAceEditor v-if="configure" lang="mysql" :theme="configure.theme" :style="{height: '200px', fontSize: configure.fontSize + 'px'}" :value="localContent"
:options="editorOptions"/>
<AceEditor :value="content as string" read-only/>
<AlertDialogFooter class="-mb-4 border-t pt-2">
<Button @click="handlerCancel">{{ $t('common.cancel') }}</Button>
</AlertDialogFooter>
@ -26,18 +25,15 @@ import {
AlertDialogTitle,
AlertDialogTrigger
} from '@/components/ui/alert-dialog'
import CommonUtils from '@/views/components/echarts/utils/CommonUtils'
import { UserEditor } from '@/model/user'
import { Button } from '@/components/ui/button'
import { VAceEditor } from 'vue3-ace-editor'
import '@/ace-editor-theme'
import AceEditor from '@/views/components/editor/AceEditor.vue'
export default defineComponent({
name: 'SqlInfo',
components: {
AceEditor,
Button,
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger,
VAceEditor
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger
},
props: {
isVisible: {
@ -48,25 +44,7 @@ export default defineComponent({
type: String as PropType<string | null>
}
},
data()
{
return {
localContent: this.content as string,
configure: null as UserEditor | null,
editorOptions: {readOnly: true}
}
},
created()
{
this.handlerInitialize()
},
methods: {
handlerInitialize()
{
const localEditorConfigure = localStorage.getItem(CommonUtils.userEditorConfigure)
const defaultEditorConfigure: UserEditor = {fontSize: 12, theme: 'chrome'}
this.configure = localEditorConfigure ? JSON.parse(localEditorConfigure) : defaultEditorConfigure
},
handlerCancel()
{
this.visible = false

View File

@ -36,6 +36,12 @@ export interface IChart
endAngle?: number[]
}
export interface ChartFieldGroup
{
label?: string
fields?: ChartField[]
}
export interface ChartField
{
label?: string

View File

@ -169,13 +169,15 @@
<Card body-class="p-2">
<template #title>{{ $t('dataset.common.visualConfigure') }}</template>
<CircularLoading v-if="loading" :show="loading"/>
<div v-else-if="configuration">
<div v-else-if="configuration" class="flex items-center justify-center">
<Alert v-if="configuration.type === Type.TABLE" :title="$t('dataset.common.visualConfigureNotSpecified')"/>
<VisualConfigure v-else :configuration="configuration" :fields="forwardFiled(configuration.type)" @change="configuration.chartConfigure = $event"/>
<Button v-else size="sm" class="w-[80%]" @click="configureVisible = true">{{ $t('common.configure') }}</Button>
</div>
</Card>
</div>
</div>
<VisualConfigure v-if="configureVisible && configuration" :is-visible="configureVisible" :configuration="configuration" :field-group="forwardFiled(configuration.type)"
@close="configureVisible = $event" @change="configuration.chartConfigure = $event"/>
</template>
<script lang="ts">
import { Type } from '@/views/components/visual/Type'
@ -183,7 +185,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 { ChartField, Configuration } from './Configuration'
import { ChartField, ChartFieldGroup, 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'
@ -200,6 +202,7 @@ import VisualConfigure from '@/views/components/visual/components/VisualConfigur
import VisualRadar from '@/views/components/visual/components/VisualRadar.vue'
import VisualFunnel from '@/views/components/visual/components/VisualFunnel.vue'
import VisualGauge from '@/views/components/visual/components/VisualGauge.vue'
import Button from '@/views/ui/button'
export default defineComponent({
name: 'VisualEditor',
@ -210,11 +213,7 @@ export default defineComponent({
}
},
components: {
VisualGauge,
VisualFunnel,
VisualRadar,
VisualConfigure,
VisualScatter,
VisualGauge, VisualFunnel, VisualRadar, VisualConfigure, VisualScatter,
Card,
Tooltip,
RadioGroup, RadioGroupItem,
@ -222,6 +221,7 @@ export default defineComponent({
Table,
CircularLoading,
Alert,
Button,
VisualWordCloud, VisualHistogram, VisualPie, VisualArea, VisualBar, VisualLine, VisualTable
},
props: {
@ -233,14 +233,20 @@ export default defineComponent({
type: Object as PropType<Configuration | null>
}
},
data()
{
return {
configureVisible: false
}
},
methods: {
handlerCommit(value: any)
{
this.$emit('commitOptions', value)
},
forwardFiled(type: Type): ChartField[]
forwardFiled(type: Type): ChartFieldGroup[]
{
const fields: Array<ChartField> = new Array<ChartField>()
const fieldGroups: Array<ChartFieldGroup> = new Array<ChartFieldGroup>()
const categoryField: ChartField = { label: this.$t('dataset.common.visualConfigureCategoryField'), field: 'xAxis' }
const valueField: ChartField = { label: this.$t('dataset.common.visualConfigureValueField'), field: 'yAxis' }
const seriesField: ChartField = { label: this.$t('dataset.common.visualConfigureSeriesField'), field: 'series' }
@ -261,33 +267,43 @@ export default defineComponent({
}
const leftField: ChartField = { label: this.$t('dataset.common.visualConfigureCategoryLeftField'), field: 'leftField' }
const rightField: ChartField = { label: this.$t('dataset.common.visualConfigureCategoryRightField'), field: 'rightField' }
switch (type) {
case Type.RADAR:
case Type.BAR:
case Type.AREA:
case Type.SCATTER:
fields.push(categoryField, valueField)
break
case Type.FUNNEL:
fields.push(categoryField, valueField, showLegend)
break
case Type.GAUGE:
fields.push(categoryField, valueField, outerRadius, innerRadius, startAngle, endAngle)
break
case Type.PIE:
fields.push(categoryField, valueField, outerRadius)
break
case Type.LINE:
fields.push(categoryField, valueField, seriesField, dataBreakpoint)
break
case Type.HISTOGRAM:
fields.push(leftField, rightField, valueField)
break
case Type.WORDCLOUD:
fields.push(categoryField, valueField, seriesField)
break
if (type === Type.AREA || type === Type.BAR || type === Type.RADAR || type === Type.SCATTER) {
const fields: Array<ChartField> = [categoryField, valueField]
const generalField: ChartFieldGroup = { label: this.$t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
fieldGroups.push(generalField)
}
return fields
else if (type === Type.FUNNEL) {
const fields: Array<ChartField> = [categoryField, valueField, showLegend]
const generalField: ChartFieldGroup = { label: this.$t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
fieldGroups.push(generalField)
}
else if (type === Type.GAUGE) {
const fields: Array<ChartField> = [categoryField, valueField, outerRadius, innerRadius, startAngle, endAngle]
const generalField: ChartFieldGroup = { label: this.$t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
fieldGroups.push(generalField)
}
else if (type === Type.PIE) {
const fields: Array<ChartField> = [categoryField, valueField, outerRadius]
const generalField: ChartFieldGroup = { label: this.$t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
fieldGroups.push(generalField)
}
else if (type === Type.LINE) {
const fields: Array<ChartField> = [categoryField, valueField, seriesField, dataBreakpoint]
const generalField: ChartFieldGroup = { label: this.$t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
fieldGroups.push(generalField)
}
else if (type === Type.HISTOGRAM) {
const fields: Array<ChartField> = [leftField, rightField, valueField]
const generalField: ChartFieldGroup = { label: this.$t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
fieldGroups.push(generalField)
}
else if (type === Type.WORDCLOUD) {
const fields: Array<ChartField> = [categoryField, valueField, seriesField]
const generalField: ChartFieldGroup = { label: this.$t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
fieldGroups.push(generalField)
}
return fieldGroups
}
}
})

View File

@ -1,67 +1,103 @@
<template>
<div v-if="configuration && formState" class="space-y-2">
<FormField v-for="item in fields" :name="item.field as string">
<FormItem>
<FormLabel>{{ item.label }}</FormLabel>
<FormControl>
<Switch v-if="item.type === 'SWITCH'" class="ml-2" :default-checked="formState[item.field as keyof IChart] as any"
@update:checked="formState[item.field as keyof IChart] = $event as any"/>
<Tooltip v-else-if="item.type === 'SLIDER'" :content="formState[item.field as keyof IChart] ? formState[item.field as keyof IChart] : [item.value] as any">
<Slider v-model="formState[item.field as keyof IChart] as any" class="ml-2 w-[95%]" :default-value="[item.value]" :min="item.min" :max="item.max" :step="item.step"
@update:modelValue="formState[item.field as keyof IChart] = $event as any"/>
</Tooltip>
<Select v-else 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.values" class="cursor-pointer" v-for="data in item.values" :value="data.value as string">{{ data.label }}</SelectItem>
<SelectItem v-else v-for="item in configuration.headers" class="cursor-pointer" :value="item as string">{{ item }}</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
</FormField>
</div>
<Dialog :is-visible="visible" :title="$t('common.configure')" width="40%" @close="handlerCancel">
<div v-if="configuration && formState" class="space-y-2 pl-3 pr-3">
<Tabs v-model="activeGroup">
<TabsList class="grid w-full grid-cols-2">
<TabsTrigger v-for="group in fieldGroup" :key="group.label" :value="group.label as string">{{ group.label }}</TabsTrigger>
</TabsList>
<TabsContent :value="activeGroup as any" class="grid grid-cols-3 gap-4">
<FormField v-for="item in fieldGroup.find(value => value.label === activeGroup)?.fields" :name="item.field as string">
<FormItem>
<FormLabel>{{ item.label }}</FormLabel>
<FormControl>
<Switch v-if="item.type === 'SWITCH'" class="ml-2" :default-checked="formState[item.field as keyof IChart] as any"
@update:checked="formState[item.field as keyof IChart] = $event as any"/>
<Tooltip v-else-if="item.type === 'SLIDER'" :content="formState[item.field as keyof IChart] ? formState[item.field as keyof IChart] : [item.value] as any">
<Slider v-model="formState[item.field as keyof IChart] as any" class="ml-2 w-[95%]" :default-value="[item.value]" :min="item.min" :max="item.max"
:step="item.step"
@update:modelValue="formState[item.field as keyof IChart] = $event as any"/>
</Tooltip>
<Select v-else 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.values" class="cursor-pointer" v-for="data in item.values" :value="data.value as string">{{ data.label }}</SelectItem>
<SelectItem v-else v-for="item in configuration.headers" class="cursor-pointer" :value="item as string">{{ item }}</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
</FormField>
</TabsContent>
</Tabs>
</div>
<template #footer>
<div class="space-x-5">
<Button variant="outline" size="sm" @click="handlerCancel">
{{ $t('common.cancel') }}
</Button>
<Button size="sm" @click="handlerSubmit">
{{ $t('common.apply') }}
</Button>
</div>
</template>
</Dialog>
</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 { ChartFieldGroup, Configuration, IChart } from '@/views/components/visual/Configuration.ts'
import { cloneDeep, keys } from 'lodash'
import { Switch } from '@/components/ui/switch'
import { Slider } from '@/components/ui/slider'
import Tooltip from '@/views/ui/tooltip'
import Dialog from '@/views/ui/dialog'
import Button from '@/views/ui/button'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
export default defineComponent({
name: 'VisualConfigure',
components: {
Tabs, TabsTrigger, TabsList, TabsContent,
Button,
Dialog,
Slider,
Switch,
SelectGroup, SelectTrigger, SelectContent, SelectItem, Select, SelectLabel, SelectValue,
FormDescription, FormControl, FormLabel, FormField, FormItem,
Tooltip
},
computed: {
visible: {
get(): boolean
{
return this.isVisible
},
set(value: boolean)
{
this.$emit('close', value)
}
}
},
props: {
configuration: {
type: Object as () => Configuration
},
fields: {
type: Array as () => ChartField[],
fieldGroup: {
type: Array as () => ChartFieldGroup[],
default: []
}
},
watch: {
formState: {
handler: 'handlerCommit',
deep: true
},
isVisible: {
type: Boolean
}
},
data()
{
return {
activeGroup: this.fieldGroup[0]?.label,
formState: null as IChart | null
}
},
@ -72,18 +108,25 @@ export default defineComponent({
}
else {
const obj = {} as any
this.fields.forEach(field => {
if (field.field) {
obj[field.field] = undefined
}
this.fieldGroup.forEach(group => {
group?.fields?.forEach(field => {
if (field.field) {
obj[field.field] = undefined
}
})
})
this.formState = obj
}
},
methods: {
handlerCommit()
handlerCancel()
{
this.visible = false
},
handlerSubmit()
{
this.$emit('change', this.formState)
this.handlerCancel()
}
}
})

View File

@ -2,7 +2,7 @@
<div class="w-full">
<Loader2 v-if="loading" class="w-full justify-center animate-spin mt-10"/>
<DashboardView v-else-if="data" :layouts="JSON.parse(data.configure as string)"/>
<Alert v-else type="error" :title="$t('dashboard.tip.notFound').replace('$VALUE', $router.currentRoute?.value?.params['code'])"/>
<Alert v-else type="error" :title="$t('dashboard.tip.notFound').replace('$VALUE', $router.currentRoute?.value?.params['code'] as string)"/>
</div>
</template>