mirror of
https://gitee.com/devlive-community/datacap.git
synced 2024-12-04 04:59:00 +08:00
[Core] Add some feature (#741)
This commit is contained in:
commit
5e9456c8ec
@ -178,3 +178,6 @@ ALTER TABLE `datacap_source_query`
|
||||
|
||||
ALTER TABLE `datacap_report`
|
||||
ADD COLUMN `description` VARCHAR(2000);
|
||||
|
||||
ALTER TABLE `datacap_dashboard`
|
||||
CHANGE `version` `description` VARCHAR(1000) DEFAULT NULL COMMENT 'Description';
|
||||
|
@ -37,8 +37,8 @@ public class DashboardEntity
|
||||
@Column(name = "configure")
|
||||
private String configure;
|
||||
|
||||
@Column(name = "version")
|
||||
private String version;
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@ManyToOne
|
||||
@JoinTable(name = "datacap_dashboard_user_relation",
|
||||
|
@ -30,7 +30,6 @@
|
||||
"axios": "^1.6.7",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"echarts": "^5.5.0",
|
||||
"embla-carousel": "^8.0.2",
|
||||
"embla-carousel-autoplay": "^8.0.2",
|
||||
"embla-carousel-vue": "^8.0.2",
|
||||
@ -38,7 +37,6 @@
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-vue-next": "^0.356.0",
|
||||
"md-editor-v3": "^4.12.1",
|
||||
"naive-ui": "^2.38.1",
|
||||
"radix-vue": "^1.5.2",
|
||||
"tailwind-merge": "^2.2.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
|
BIN
core/datacap-ui/public/static/images/dashboard.png
Normal file
BIN
core/datacap-ui/public/static/images/dashboard.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { AspectRatio, type AspectRatioProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<AspectRatioProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AspectRatio v-bind="props">
|
||||
<slot />
|
||||
</AspectRatio>
|
||||
</template>
|
1
core/datacap-ui/src/components/ui/aspect-ratio/index.ts
Normal file
1
core/datacap-ui/src/components/ui/aspect-ratio/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as AspectRatio } from './AspectRatio.vue'
|
@ -12,5 +12,6 @@ export default {
|
||||
deleteTip2: 'Warning: This cannot be undone. ',
|
||||
deleteTip3: 'To confirm, type [ $NAME ] in the box below',
|
||||
publishSuccess: 'Dashboard [ $VALUE ] published successfully',
|
||||
notFound: 'Dashboard [ $VALUE ] not found',
|
||||
}
|
||||
}
|
@ -83,6 +83,20 @@ export default {
|
||||
visualConfigureDataBreakpointContinuous: 'Continuous',
|
||||
visualConfigureDataBreakpointZero: 'Zero',
|
||||
visualConfigureDataBreakpointIgnore: 'Ignore',
|
||||
visualConfigureGeneralGroup: 'General Configure',
|
||||
visualConfigureTitleGroup: 'Title Configure',
|
||||
visualConfigureTitleGroupVisible: 'Visible',
|
||||
visualConfigureTitleGroupText: 'Title',
|
||||
visualConfigureTitleGroupSubText: 'Sub Title',
|
||||
visualConfigureTitleGroupPosition: 'Position',
|
||||
visualConfigureTitleGroupPositionLeft: 'Left',
|
||||
visualConfigureTitleGroupPositionRight: 'Right',
|
||||
visualConfigureTitleGroupPositionTop: 'Top',
|
||||
visualConfigureTitleGroupPositionBottom: 'Bottom',
|
||||
visualConfigureTitleGroupAlign: 'Align',
|
||||
visualConfigureTitleGroupAlignLeft: 'Left',
|
||||
visualConfigureTitleGroupAlignCenter: 'Center',
|
||||
visualConfigureTitleGroupAlignRight: 'Right',
|
||||
columnExpressionMax: 'Maximum',
|
||||
columnExpressionMin: 'Minimum',
|
||||
columnExpressionSum: 'Sum',
|
||||
|
@ -12,5 +12,6 @@ export default {
|
||||
deleteTip2: '警告:此操作无法撤消。 ',
|
||||
deleteTip3: '要确认,请在下面的框中键入 [ $NAME ]',
|
||||
publishSuccess: '仪表板 [ $VALUE ] 发布成功',
|
||||
notFound: '仪表板 [ $VALUE ] 不存在',
|
||||
}
|
||||
}
|
@ -83,6 +83,20 @@ export default {
|
||||
visualConfigureDataBreakpointContinuous: '连续',
|
||||
visualConfigureDataBreakpointZero: '补 0',
|
||||
visualConfigureDataBreakpointIgnore: '忽略',
|
||||
visualConfigureGeneralGroup: '通用配置',
|
||||
visualConfigureTitleGroup: '标题配置',
|
||||
visualConfigureTitleGroupVisible: '是否展示',
|
||||
visualConfigureTitleGroupText: '标题',
|
||||
visualConfigureTitleGroupSubText: '子标题',
|
||||
visualConfigureTitleGroupPosition: '位置',
|
||||
visualConfigureTitleGroupPositionLeft: '左',
|
||||
visualConfigureTitleGroupPositionRight: '右',
|
||||
visualConfigureTitleGroupPositionTop: '顶部',
|
||||
visualConfigureTitleGroupPositionBottom: '底部',
|
||||
visualConfigureTitleGroupAlign: '对齐方式',
|
||||
visualConfigureTitleGroupAlignLeft: '左对齐',
|
||||
visualConfigureTitleGroupAlignCenter: '居中对齐',
|
||||
visualConfigureTitleGroupAlignRight: '右对齐',
|
||||
columnExpressionMax: '最大值',
|
||||
columnExpressionMin: '最小值',
|
||||
columnExpressionSum: '总和',
|
||||
@ -114,12 +128,12 @@ export default {
|
||||
info: '查看详情',
|
||||
lifeCycleColumn: '生命周期列',
|
||||
lifeCycleNumber: '生命周期数',
|
||||
continuousBuild: '连续构建',
|
||||
continuousBuild: '连续构建'
|
||||
},
|
||||
validator: {
|
||||
duplicateColumn: '列名 [ $VALUE ] 已存在',
|
||||
specifiedColumn: '排序键或主键必须指定',
|
||||
specifiedName: '数据集名必须指定',
|
||||
specifiedName: '数据集名必须指定'
|
||||
},
|
||||
tip: {
|
||||
selectExpression: '请选择表达式',
|
||||
@ -131,6 +145,6 @@ export default {
|
||||
rebuildProgress: '重建只会进行未完成进度',
|
||||
lifeCycleMustDateColumn: '生命周期必须包含一个日期列',
|
||||
modifyNotSupportDataPreview: '修改暂不支持数据预览',
|
||||
publishSuccess: '数据集 [ $VALUE ] 发布成功',
|
||||
publishSuccess: '数据集 [ $VALUE ] 发布成功'
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ export interface DashboardModel
|
||||
updateTime?: string
|
||||
user?: UserModel
|
||||
reports?: ReportModel[]
|
||||
code?: string
|
||||
}
|
||||
|
||||
export class DashboardRequest
|
||||
|
@ -129,7 +129,7 @@ const createAdminRouter = (router: any) => {
|
||||
component: () => import('@/views/pages/admin/dashboard/DashboardHome.vue')
|
||||
},
|
||||
{
|
||||
path: 'dashboard/info/:id/preview',
|
||||
path: 'dashboard/preview/:code',
|
||||
meta: {
|
||||
title: 'common.dashboard',
|
||||
isRoot: false
|
||||
@ -137,7 +137,7 @@ const createAdminRouter = (router: any) => {
|
||||
component: () => import('@/views/pages/admin/dashboard/DashboardPreview.vue')
|
||||
},
|
||||
{
|
||||
path: 'dashboard/info/:id?',
|
||||
path: 'dashboard/info/:code?',
|
||||
meta: {
|
||||
title: 'common.dashboard',
|
||||
isRoot: false
|
||||
|
@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Returns an array of unique values extracted from the specified key in each object within the provided array.
|
||||
*
|
||||
* @param {string} key - The key to extract values from each object in the array.
|
||||
* @param {any[]} columns - The array of objects to extract values from.
|
||||
* @return {any[]} An array containing unique values extracted from the specified key in each object.
|
||||
*/
|
||||
export function getValueByKey(key: string, columns: never[]): any[]
|
||||
{
|
||||
const container: any[] = []
|
||||
columns.forEach(column => {
|
||||
if (container.indexOf(column[key]) === -1) {
|
||||
container.push(column[key])
|
||||
}
|
||||
});
|
||||
return container
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { EchartsType } from '@/views/components/echarts/EchartsType'
|
||||
|
||||
export class EchartsConfigure
|
||||
{
|
||||
headers: [] | undefined
|
||||
types: [] | undefined
|
||||
columns: [] | undefined
|
||||
type: EchartsType = EchartsType.LINE
|
||||
}
|
@ -1,264 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<Modal v-model="visible"
|
||||
placement="right"
|
||||
:title="$t('common.visualization')"
|
||||
:mask-closable="false"
|
||||
:width="'90%'"
|
||||
:transfer="false">
|
||||
<Form :inline="true">
|
||||
<FormItem :label="$t('common.name')"
|
||||
:label-width="80">
|
||||
<Input v-model="formState.name"/>
|
||||
</FormItem>
|
||||
<FormItem :label="$t('common.realtime')"
|
||||
:label-width="80">
|
||||
<Switch v-model="formState.realtime"/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<Layout v-if="configure">
|
||||
<Layout>
|
||||
<Content>
|
||||
<EchartsPreview :key="referKey" :height="'500px'" :configure="chartOptions"/>
|
||||
</Content>
|
||||
<Sider style="background-color: #FFFFFF;"
|
||||
hide-trigger>
|
||||
<Form label-position="left"
|
||||
:label-width="50">
|
||||
<Card padding="0"
|
||||
dis-hover
|
||||
:bordered="false">
|
||||
<template #title>
|
||||
<RadioGroup v-model="chartType"
|
||||
type="button"
|
||||
@on-change="handlerChangeValue('Type')">
|
||||
<Space>
|
||||
<Radio label="line">
|
||||
<FontAwesomeIcon icon="chart-line">
|
||||
</FontAwesomeIcon>
|
||||
</Radio>
|
||||
<Radio label="bar">
|
||||
<FontAwesomeIcon icon="chart-bar">
|
||||
</FontAwesomeIcon>
|
||||
</Radio>
|
||||
</Space>
|
||||
</RadioGroup>
|
||||
</template>
|
||||
<Collapse v-if="chartType"
|
||||
v-model="collapseValue"
|
||||
accordion>
|
||||
<div v-if="chartOptions">
|
||||
<Panel name="xAxis">
|
||||
{{ $t('common.xAxis') }}
|
||||
<template #content>
|
||||
<FormItem :label="$t('common.column')">
|
||||
<Select v-model="defaultConfigure.xAxis" @on-change="handlerChangeValue('xAxis')">
|
||||
<Option v-for="value of configure.headers" :value="value" v-bind:key="value">{{ value }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem v-if="chartOptions.xAxis" :label="$t('common.type')">
|
||||
<RadioGroup v-model="chartOptions.xAxis.type" type="button" size="small" @on-change="handlerChangeValue">
|
||||
<Radio label="value">{{ $t('common.column') }}</Radio>
|
||||
<Radio label="category">{{ $t('common.tag') }}</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
</template>
|
||||
</Panel>
|
||||
<Panel v-if="chartOptions.xAxis && chartOptions.yAxis && !chartOptions.yAxis.disabled" disabled name="yAxis">
|
||||
{{ $t('common.yAxis') }}
|
||||
<template #content>
|
||||
<FormItem label="Value">
|
||||
<Select v-model="defaultConfigure.yAxis">
|
||||
<Option v-for="value of configure.headers" :value="value" v-bind:key="value">{{ value }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="Type">
|
||||
<RadioGroup v-model="chartOptions.yAxis.type" @on-change="handlerChangeValue">
|
||||
<Radio label="value"></Radio>
|
||||
<Radio label="category"></Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
</template>
|
||||
</Panel>
|
||||
</div>
|
||||
<div v-if="chartOptions">
|
||||
<Panel v-if="chartOptions.xAxis" name="Series">
|
||||
{{ $t('common.data') }}
|
||||
<template #content>
|
||||
<FormItem :label="$t('common.column')">
|
||||
<Select v-model="defaultConfigure.series" @on-change="handlerChangeValue('Series')">
|
||||
<Option v-for="value of configure.headers" :value="value" v-bind:key="value">{{ value }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</template>
|
||||
</Panel>
|
||||
</div>
|
||||
</Collapse>
|
||||
</Card>
|
||||
</Form>
|
||||
</Sider>
|
||||
</Layout>
|
||||
</Layout>
|
||||
<Result v-else
|
||||
type="warning">
|
||||
<template #desc>
|
||||
</template>
|
||||
<template #actions>
|
||||
</template>
|
||||
</Result>
|
||||
<template #footer>
|
||||
<Button type="primary"
|
||||
:loading="loading"
|
||||
@click="handlerPublish">
|
||||
{{ $t('common.publish') }}
|
||||
</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { ChartConfigure } from '@/views/components/echarts/configure/ChartConfigure'
|
||||
import { getValueByKey } from './DataUtils'
|
||||
import { SeriesConfigure } from '@/views/components/echarts/configure/SeriesConfigure'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { EchartsConfigure } from '@/views/components/echarts/EchartsConfigure'
|
||||
import EchartsPreview from '@/views/components/echarts/EchartsPreview.vue'
|
||||
import { AxisConfigure } from '@/views/components/echarts/configure/AxisConfigure'
|
||||
import ReportService from '@/services/report'
|
||||
import { ObjectUtils } from '@/utils/object'
|
||||
import { ToastUtils } from '@/utils/toast'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'EchartsEditor',
|
||||
components: {EchartsPreview},
|
||||
props: {
|
||||
isVisible: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
configure: {
|
||||
type: Object as () => EchartsConfigure,
|
||||
default: () => null
|
||||
},
|
||||
sourceId: {
|
||||
type: Number
|
||||
},
|
||||
query: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data()
|
||||
{
|
||||
return {
|
||||
collapseValue: 'xAxis',
|
||||
referKey: 0,
|
||||
defaultConfigure: {
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
series: ''
|
||||
},
|
||||
chartOptions: null as ChartConfigure | null,
|
||||
chartType: null,
|
||||
formState: {
|
||||
name: null as string | null,
|
||||
realtime: null,
|
||||
type: 'QUERY',
|
||||
configure: null as string | null,
|
||||
source: {id: null as number | null},
|
||||
query: null as string | null
|
||||
},
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
created()
|
||||
{
|
||||
this.handlerInitialize()
|
||||
},
|
||||
methods: {
|
||||
handlerInitialize()
|
||||
{
|
||||
this.chartOptions = new ChartConfigure()
|
||||
},
|
||||
handlerChangeValue(type: string)
|
||||
{
|
||||
this.referKey = ObjectUtils.getTimestamp()
|
||||
|
||||
switch (type) {
|
||||
case 'xAxis':
|
||||
if (this.chartOptions && this.configure) {
|
||||
this.chartOptions.xAxis = new AxisConfigure()
|
||||
this.chartOptions.xAxis.data = getValueByKey(this.defaultConfigure.xAxis, this.configure.columns as never[]) as never[]
|
||||
this.chartOptions.xAxis.meta.column = this.defaultConfigure.xAxis
|
||||
this.chartOptions.yAxis = new AxisConfigure()
|
||||
this.chartOptions.yAxis.type = 'value'
|
||||
this.chartOptions.yAxis.data = getValueByKey(this.defaultConfigure.yAxis, this.configure.columns as never[]) as never[]
|
||||
this.chartOptions.yAxis.disabled = true
|
||||
this.chartOptions.yAxis.meta.column = this.defaultConfigure.yAxis
|
||||
}
|
||||
break
|
||||
case 'Series': {
|
||||
if (this.chartOptions && this.configure) {
|
||||
const series: SeriesConfigure = new SeriesConfigure()
|
||||
series.data = getValueByKey(this.defaultConfigure.series, this.configure.columns as never[])
|
||||
series.type = this.chartType as any
|
||||
series.meta.column = this.defaultConfigure.series
|
||||
this.chartOptions.series = []
|
||||
this.chartOptions.series.push(series)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Type': {
|
||||
if (this.chartOptions) {
|
||||
const array = this.chartOptions.series as never[]
|
||||
if (array.length > 0) {
|
||||
(array as any[])[0].type = this.chartType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.handlerSetDefaultValue();
|
||||
},
|
||||
handlerSetDefaultValue()
|
||||
{
|
||||
if (this.chartOptions) {
|
||||
if (isEmpty(this.defaultConfigure.xAxis)) {
|
||||
this.chartOptions.xAxis = new AxisConfigure()
|
||||
}
|
||||
if (isEmpty(this.defaultConfigure.yAxis)) {
|
||||
this.chartOptions.yAxis = new AxisConfigure()
|
||||
}
|
||||
}
|
||||
},
|
||||
handlerPublish()
|
||||
{
|
||||
if (this.formState) {
|
||||
this.loading = true
|
||||
this.formState.configure = JSON.stringify(this.chartOptions)
|
||||
this.formState.source.id = this.sourceId as number
|
||||
this.formState.query = this.query as string
|
||||
ReportService.saveOrUpdate(this.formState)
|
||||
.then(response => {
|
||||
if (response.status) {
|
||||
ToastUtils.success(this.$t('report.publishSuccess').replace('REPLACE_NAME', this.formState.name as string))
|
||||
this.visible = false
|
||||
}
|
||||
})
|
||||
.finally(() => this.loading = false)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
visible: {
|
||||
get(): boolean
|
||||
{
|
||||
return this.isVisible;
|
||||
},
|
||||
set(value: boolean)
|
||||
{
|
||||
this.$emit('close', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,107 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<CircularLoading v-if="loading" :show="loading"/>
|
||||
<div v-else :style="{width: width, height: height, padding: '0'}" :id="key"/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import CircularLoading from '@/views/components/loading/CircularLoading.vue'
|
||||
import { ChartConfigure } from './configure/ChartConfigure'
|
||||
import ReportService from '@/services/report'
|
||||
import { getValueByKey } from './DataUtils'
|
||||
import { SeriesConfigure } from './configure/SeriesConfigure'
|
||||
import { ExecuteModel } from '@/model/execute'
|
||||
import ExecuteService from '@/services/execute'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'EchartsPreview',
|
||||
components: {CircularLoading},
|
||||
props: {
|
||||
width: {
|
||||
type: String,
|
||||
default: () => '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: () => '300px'
|
||||
},
|
||||
configure: {
|
||||
type: Object as () => ChartConfigure | null
|
||||
},
|
||||
id: {
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
width: 'handlerInitialize',
|
||||
height: 'handlerInitialize'
|
||||
},
|
||||
created()
|
||||
{
|
||||
this.handlerInitialize()
|
||||
},
|
||||
data()
|
||||
{
|
||||
return {
|
||||
loading: false,
|
||||
key: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlerInitialize()
|
||||
{
|
||||
this.key = uuidv4()
|
||||
setTimeout(() => {
|
||||
try {
|
||||
if (this.key) {
|
||||
const echartsContainer = document.getElementById(this.key)
|
||||
const echartsChart = echarts.init(echartsContainer)
|
||||
echartsChart.resize()
|
||||
if (this.id) {
|
||||
this.loading = true
|
||||
ReportService.getById(this.id)
|
||||
.then(response => {
|
||||
if (response.status && response.data.realtime) {
|
||||
if (response.data.source) {
|
||||
const queryConfigure: ExecuteModel = {
|
||||
name: response.data.source.id,
|
||||
content: response.data.query,
|
||||
format: 'JSON',
|
||||
mode: 'REPORT'
|
||||
}
|
||||
ExecuteService.execute(queryConfigure, null)
|
||||
.then(response => {
|
||||
const configure: any = this.configure as ChartConfigure;
|
||||
configure.xAxis.data = getValueByKey(configure.xAxis.meta.column, response.data.columns)
|
||||
configure.yAxis.data = getValueByKey(configure.yAxis.meta.column, response.data.columns)
|
||||
configure.series.forEach((item: SeriesConfigure) => {
|
||||
const series: SeriesConfigure = item as SeriesConfigure
|
||||
series.data = getValueByKey(series.meta.column as string, response.data.columns)
|
||||
})
|
||||
echartsChart.setOption(configure)
|
||||
})
|
||||
.finally(() => this.loading = false)
|
||||
}
|
||||
}
|
||||
else {
|
||||
echartsChart.setOption(this.configure as any)
|
||||
}
|
||||
})
|
||||
.finally(() => this.loading = false)
|
||||
}
|
||||
else {
|
||||
echartsChart.setOption(this.configure as any)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,5 +0,0 @@
|
||||
export enum EchartsType
|
||||
{
|
||||
LINE = ('line'),
|
||||
BAR = ('bar')
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
export class AxisConfigure
|
||||
{
|
||||
type = 'category'
|
||||
data: never[] = []
|
||||
disabled = false
|
||||
meta = {
|
||||
column: null as string | null
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import { SeriesConfigure } from '@/views/components/echarts/configure/SeriesConfigure'
|
||||
import { AxisConfigure } from '@/views/components/echarts/configure/AxisConfigure'
|
||||
import { TooltipConfigure } from '@/views/components/echarts/configure/TooltipConfigure'
|
||||
|
||||
export class ChartConfigure
|
||||
{
|
||||
xAxis: AxisConfigure | undefined
|
||||
yAxis: AxisConfigure | undefined
|
||||
series: Array<SeriesConfigure> | undefined
|
||||
tooltip: TooltipConfigure = new TooltipConfigure()
|
||||
}
|
||||
|
||||
export default function toOptions(options: ChartConfigure): any {
|
||||
return JSON.stringify(options)
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { EchartsType } from '@/views/components/echarts/EchartsType'
|
||||
|
||||
export class SeriesConfigure
|
||||
{
|
||||
data: any[] = []
|
||||
type: EchartsType = EchartsType.LINE
|
||||
smooth = true
|
||||
meta = {
|
||||
column: null as string | null
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export class TooltipConfigure {
|
||||
trigger = 'axis'
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
class CommonUtils
|
||||
{
|
||||
getXAxis(type = 'value', items: any[])
|
||||
{
|
||||
return {
|
||||
type: type,
|
||||
data: items
|
||||
}
|
||||
}
|
||||
|
||||
userEditorConfigure = 'DataCapUserEditorConfigure'
|
||||
}
|
||||
|
||||
export default new CommonUtils()
|
@ -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
|
||||
|
@ -33,7 +33,18 @@ export interface IChart
|
||||
invalidType?: string
|
||||
showLegend?: boolean
|
||||
startAngle?: number[]
|
||||
endAngle?: number[]
|
||||
endAngle?: number[],
|
||||
titleVisible?: boolean
|
||||
titleText?: string
|
||||
titleSubText?: string
|
||||
titlePosition?: string
|
||||
titleAlign?: string
|
||||
}
|
||||
|
||||
export interface ChartFieldGroup
|
||||
{
|
||||
label?: string
|
||||
fields?: ChartField[]
|
||||
}
|
||||
|
||||
export interface ChartField
|
||||
@ -46,4 +57,12 @@ export interface ChartField
|
||||
min?: number
|
||||
max?: number
|
||||
step?: number
|
||||
disabled?: ChartFieldItem
|
||||
}
|
||||
|
||||
export interface ChartFieldItem
|
||||
{
|
||||
field?: string
|
||||
value?: any
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { Type } from '@/views/components/visual/Type.ts'
|
||||
import { ChartField, ChartFieldGroup } from '@/views/components/visual/Configuration.ts'
|
||||
|
||||
/**
|
||||
* Generates a table header based on the given data array.
|
||||
*
|
||||
@ -14,6 +17,108 @@ const createdTableHeader = (data: never[]): object[] => {
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
createdTableHeader
|
||||
const createdConfigure = (type: Type, i18n: any): Array<ChartFieldGroup> => {
|
||||
const fieldGroups: Array<ChartFieldGroup> = new Array<ChartFieldGroup>()
|
||||
const categoryField: ChartField = { label: i18n.t('dataset.common.visualConfigureCategoryField'), field: 'xAxis' }
|
||||
const valueField: ChartField = { label: i18n.t('dataset.common.visualConfigureValueField'), field: 'yAxis' }
|
||||
const seriesField: ChartField = { label: i18n.t('dataset.common.visualConfigureSeriesField'), field: 'series' }
|
||||
const showLegend: ChartField = { label: i18n.t('dataset.common.visualConfigureShowLegend'), field: 'showLegend', type: 'SWITCH' }
|
||||
const outerRadius: ChartField = { label: i18n.t('dataset.common.visualConfigureOuterRadius'), field: 'outerRadius', type: 'SLIDER', value: 0.8, min: 0.1, max: 1, step: 0.1 }
|
||||
const innerRadius: ChartField = { label: i18n.t('dataset.common.visualConfigureInnerRadius'), field: 'innerRadius', type: 'SLIDER', value: 0.5, min: 0.1, max: 1, step: 0.1 }
|
||||
const startAngle: ChartField = { label: i18n.t('dataset.common.visualConfigureStartAngle'), field: 'startAngle', type: 'SLIDER', value: -180, min: -360, max: 360, step: 1 }
|
||||
const endAngle: ChartField = { label: i18n.t('dataset.common.visualConfigureEndAngle'), field: 'endAngle', type: 'SLIDER', value: 0, min: -360, max: 360, step: 1 }
|
||||
const dataBreakpoint: ChartField = {
|
||||
label: i18n.t('dataset.common.visualConfigureDataBreakpoint'),
|
||||
field: 'dataBreakpoint',
|
||||
values: [
|
||||
{ label: i18n.t('dataset.common.visualConfigureDataBreakpointBreak'), value: 'break' },
|
||||
{ label: i18n.t('dataset.common.visualConfigureDataBreakpointContinuous'), value: 'link' },
|
||||
{ label: i18n.t('dataset.common.visualConfigureDataBreakpointZero'), value: 'zero' },
|
||||
{ label: i18n.t('dataset.common.visualConfigureDataBreakpointIgnore'), value: 'ignore' }
|
||||
]
|
||||
}
|
||||
const leftField: ChartField = { label: i18n.t('dataset.common.visualConfigureCategoryLeftField'), field: 'leftField' }
|
||||
const rightField: ChartField = { label: i18n.t('dataset.common.visualConfigureCategoryRightField'), field: 'rightField' }
|
||||
|
||||
const titleVisible: ChartField = { label: i18n.t('dataset.common.visualConfigureTitleGroupVisible'), field: 'titleVisible', type: 'SWITCH', value: true }
|
||||
const titleText: ChartField = {
|
||||
label: i18n.t('dataset.common.visualConfigureTitleGroupText'),
|
||||
field: 'titleText',
|
||||
type: 'TEXT',
|
||||
disabled: { field: 'titleVisible', value: false }
|
||||
}
|
||||
const titleSubText: ChartField = {
|
||||
label: i18n.t('dataset.common.visualConfigureTitleGroupSubText'),
|
||||
field: 'titleSubText',
|
||||
type: 'TEXT',
|
||||
disabled: { field: 'titleVisible', value: false }
|
||||
}
|
||||
const titlePosition: ChartField = {
|
||||
label: i18n.t('dataset.common.visualConfigureTitleGroupPosition'), field: 'titlePosition',
|
||||
values: [
|
||||
{ label: i18n.t('dataset.common.visualConfigureTitleGroupPositionLeft'), value: 'left' },
|
||||
{ label: i18n.t('dataset.common.visualConfigureTitleGroupPositionTop'), value: 'top' },
|
||||
{ label: i18n.t('dataset.common.visualConfigureTitleGroupPositionRight'), value: 'right' },
|
||||
{ label: i18n.t('dataset.common.visualConfigureTitleGroupPositionBottom'), value: 'bottom' }
|
||||
],
|
||||
value: 'top',
|
||||
disabled: { field: 'titleVisible', value: false }
|
||||
}
|
||||
const titleAlign: ChartField = {
|
||||
label: i18n.t('dataset.common.visualConfigureTitleGroupAlign'), field: 'titleAlign',
|
||||
values: [
|
||||
{ label: i18n.t('dataset.common.visualConfigureTitleGroupAlignLeft'), value: 'left' },
|
||||
{ label: i18n.t('dataset.common.visualConfigureTitleGroupAlignCenter'), value: 'center' },
|
||||
{ label: i18n.t('dataset.common.visualConfigureTitleGroupAlignRight'), value: 'right' }
|
||||
],
|
||||
value: 'left',
|
||||
disabled: { field: 'titleVisible', value: false }
|
||||
}
|
||||
|
||||
if (type === Type.LINE) {
|
||||
const fields: Array<ChartField> = [categoryField, valueField, seriesField, dataBreakpoint]
|
||||
const generalField: ChartFieldGroup = { label: i18n.t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
|
||||
|
||||
const titleGroup: ChartFieldGroup = {
|
||||
label: i18n.t('dataset.common.visualConfigureTitleGroup'),
|
||||
fields: [titleVisible, titleText, titleSubText, titlePosition, titleAlign]
|
||||
}
|
||||
fieldGroups.push(generalField, titleGroup)
|
||||
}
|
||||
else if (type === Type.AREA || type === Type.BAR || type === Type.RADAR || type === Type.SCATTER) {
|
||||
const fields: Array<ChartField> = [categoryField, valueField]
|
||||
const generalField: ChartFieldGroup = { label: i18n.t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
|
||||
fieldGroups.push(generalField)
|
||||
}
|
||||
else if (type === Type.FUNNEL) {
|
||||
const fields: Array<ChartField> = [categoryField, valueField, showLegend]
|
||||
const generalField: ChartFieldGroup = { label: i18n.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 generalGroup: ChartFieldGroup = { label: i18n.t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
|
||||
fieldGroups.push(generalGroup)
|
||||
}
|
||||
else if (type === Type.PIE) {
|
||||
const fields: Array<ChartField> = [categoryField, valueField, outerRadius]
|
||||
const generalField: ChartFieldGroup = { label: i18n.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: i18n.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: i18n.t('dataset.common.visualConfigureGeneralGroup'), fields: fields }
|
||||
fieldGroups.push(generalField)
|
||||
}
|
||||
return fieldGroups
|
||||
}
|
||||
|
||||
export {
|
||||
createdTableHeader,
|
||||
createdConfigure
|
||||
}
|
||||
|
@ -27,12 +27,12 @@
|
||||
<div v-else-if="configuration">
|
||||
<ToggleGroup v-model="configuration.type" type="single">
|
||||
<div class="grid grid-cols-4 items-center space-x-1 space-y-1">
|
||||
<ToggleGroupItem class="mt-1" :value="Type.TABLE">
|
||||
<ToggleGroupItem class="mt-1" :disabled="configuration.headers.length === 0" :value="Type.TABLE">
|
||||
<Tooltip :content="$t('dataset.common.visualTypeTable')">
|
||||
<Table :size="20"/>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.LINE">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :value="Type.LINE">
|
||||
<Tooltip :content="$t('dataset.common.visualTypeLine')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="24" viewBox="0 0 24 24" fill="none" class="vchart-dropdown-content-item-icon" id="lineChart"
|
||||
style="width: 24px;">
|
||||
@ -42,7 +42,7 @@
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.BAR">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :value="Type.BAR">
|
||||
<Tooltip :content="$t('dataset.common.visualTypeBar')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" class="vchart-dropdown-content-item-icon" id="barChart"
|
||||
style="width: 24px;">
|
||||
@ -53,7 +53,7 @@
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.AREA">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :value="Type.AREA">
|
||||
<Tooltip :content="$t('dataset.common.visualTypeArea')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" class="vchart-dropdown-content-item-icon" id="areaChart"
|
||||
style="width: 24px;">
|
||||
@ -64,7 +64,7 @@
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.PIE">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :value="Type.PIE">
|
||||
<Tooltip :content="$t('dataset.common.visualTypePie')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none" class="vchart-dropdown-content-item-icon" id="pieChart"
|
||||
style="width: 24px;">
|
||||
@ -76,7 +76,7 @@
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.HISTOGRAM">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :value="Type.HISTOGRAM">
|
||||
<Tooltip :content="$t('dataset.common.visualTypeHistogram')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="20" viewBox="0 0 21 20" fill="none">
|
||||
<g clip-path="url(#clip0_1700_69225)">
|
||||
@ -98,7 +98,7 @@
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.WORDCLOUD">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :value="Type.WORDCLOUD">
|
||||
<Tooltip :content="$t('dataset.common.visualTypeWordCloud')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" class="vchart-dropdown-content-item-icon" id="wordCloud"
|
||||
style="width: 24px;">
|
||||
@ -116,7 +116,7 @@
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.SCATTER">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :value="Type.SCATTER">
|
||||
<Tooltip :content="$t('dataset.common.visualTypeScatter')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" class="vchart-dropdown-content-item-icon" id="scatterChart"
|
||||
style="width: 24px;">
|
||||
@ -128,7 +128,7 @@
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.RADAR">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :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
|
||||
@ -140,7 +140,7 @@
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.FUNNEL">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :value="Type.FUNNEL">
|
||||
<Tooltip :content="$t('dataset.common.visualTypeFunnel')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" class="vchart-dropdown-content-item-icon" id="funnelChart"
|
||||
style="width: 24px;">
|
||||
@ -152,7 +152,7 @@
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem :value="Type.GAUGE">
|
||||
<ToggleGroupItem :disabled="configuration.headers.length === 0" :value="Type.GAUGE">
|
||||
<Tooltip :content="$t('dataset.common.visualTypeGauge')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" class="vchart-dropdown-content-item-icon" id="gauge"
|
||||
style="width: 24px;">
|
||||
@ -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 { 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,21 +202,14 @@ 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'
|
||||
import { createdConfigure } from '@/views/components/visual/Utils.ts'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VisualEditor',
|
||||
computed: {
|
||||
Type()
|
||||
{
|
||||
return Type
|
||||
}
|
||||
},
|
||||
components: {
|
||||
VisualGauge,
|
||||
VisualFunnel,
|
||||
VisualRadar,
|
||||
VisualConfigure,
|
||||
VisualScatter,
|
||||
VisualGauge, VisualFunnel, VisualRadar, VisualConfigure, VisualScatter,
|
||||
Card,
|
||||
Tooltip,
|
||||
RadioGroup, RadioGroupItem,
|
||||
@ -222,8 +217,15 @@ export default defineComponent({
|
||||
Table,
|
||||
CircularLoading,
|
||||
Alert,
|
||||
Button,
|
||||
VisualWordCloud, VisualHistogram, VisualPie, VisualArea, VisualBar, VisualLine, VisualTable
|
||||
},
|
||||
computed: {
|
||||
Type()
|
||||
{
|
||||
return Type
|
||||
}
|
||||
},
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
@ -233,61 +235,28 @@ export default defineComponent({
|
||||
type: Object as PropType<Configuration | null>
|
||||
}
|
||||
},
|
||||
setup()
|
||||
{
|
||||
const i18n = useI18n()
|
||||
|
||||
return {
|
||||
i18n
|
||||
}
|
||||
},
|
||||
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 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' }
|
||||
const showLegend: ChartField = { label: this.$t('dataset.common.visualConfigureShowLegend'), field: 'showLegend', type: 'SWITCH' }
|
||||
const outerRadius: ChartField = { label: this.$t('dataset.common.visualConfigureOuterRadius'), field: 'outerRadius', type: 'SLIDER', value: 0.8, min: 0.1, max: 1, step: 0.1 }
|
||||
const innerRadius: ChartField = { label: this.$t('dataset.common.visualConfigureInnerRadius'), field: 'innerRadius', type: 'SLIDER', value: 0.5, min: 0.1, max: 1, step: 0.1 }
|
||||
const startAngle: ChartField = { label: this.$t('dataset.common.visualConfigureStartAngle'), field: 'startAngle', type: 'SLIDER', value: -180, min: -360, max: 360, step: 1 }
|
||||
const endAngle: ChartField = { label: this.$t('dataset.common.visualConfigureEndAngle'), field: 'endAngle', type: 'SLIDER', value: 0, min: -360, max: 360, step: 1 }
|
||||
const dataBreakpoint: ChartField = {
|
||||
label: this.$t('dataset.common.visualConfigureDataBreakpoint'),
|
||||
field: 'dataBreakpoint',
|
||||
values: [
|
||||
{ label: this.$t('dataset.common.visualConfigureDataBreakpointBreak'), value: 'break' },
|
||||
{ label: this.$t('dataset.common.visualConfigureDataBreakpointContinuous'), value: 'link' },
|
||||
{ label: this.$t('dataset.common.visualConfigureDataBreakpointZero'), value: 'zero' },
|
||||
{ label: this.$t('dataset.common.visualConfigureDataBreakpointIgnore'), value: 'ignore' }
|
||||
]
|
||||
}
|
||||
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
|
||||
}
|
||||
return fields
|
||||
return createdConfigure(type, this.i18n)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,67 +1,110 @@
|
||||
<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-4 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>
|
||||
<div v-if="item.type === 'SWITCH'">
|
||||
<Switch class="mt-2" :value="item.value" :default-checked="formState[item.field as keyof IChart] ? formState[item.field as keyof IChart] as boolean : item.value"
|
||||
@update:checked="formState[item.field as keyof IChart] = $event as any"/>
|
||||
</div>
|
||||
<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="pt-3" :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>
|
||||
<Input v-else-if="item.type === 'TEXT'" v-model="formState[item.field as keyof IChart] as string" :placeholder="item.label"
|
||||
:disabled="item.disabled?.field ? formState[item.disabled?.field as keyof IChart] === item.disabled?.value : false"/>
|
||||
<Select v-else v-model="formState[item.field as keyof IChart] as string" :default-value="item.value"
|
||||
:disabled="item.disabled?.field ? formState[item.disabled?.field as keyof IChart] === item.disabled?.value : false">
|
||||
<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'
|
||||
import { Input } from '@/components/ui/input'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VisualConfigure',
|
||||
components: {
|
||||
Input,
|
||||
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 +115,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()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -53,7 +53,18 @@ export default defineComponent({
|
||||
yField: this.configuration.chartConfigure?.yAxis,
|
||||
seriesField: this.configuration.chartConfigure?.series,
|
||||
invalidType: this.configuration.chartConfigure?.invalidType
|
||||
} as any
|
||||
|
||||
if (this.configuration.chartConfigure) {
|
||||
options.title = {
|
||||
visible: this.configuration.chartConfigure?.titleVisible,
|
||||
text: this.configuration.chartConfigure?.titleText,
|
||||
subtext: this.configuration.chartConfigure?.titleSubText,
|
||||
orient: this.configuration.chartConfigure?.titlePosition,
|
||||
align: this.configuration.chartConfigure?.titleAlign
|
||||
}
|
||||
}
|
||||
|
||||
if (!reset) {
|
||||
instance = new VChart(options, { dom: this.$refs.content as HTMLElement })
|
||||
instance.renderAsync()
|
||||
|
@ -11,9 +11,18 @@
|
||||
<div v-else class="hidden flex-col md:flex">
|
||||
<div class="flex-1 space-y-4 pt-6">
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-6">
|
||||
<Card title-class="p-2 pl-4 pr-4" v-for="item in data">
|
||||
<Card title-class="p-2 pl-4 pr-4" footer-class="p-2 pl-2 pr-4" body-class="p-4" v-for="item in data">
|
||||
<template #title>
|
||||
<RouterLink :to="`/admin/dashboard/info/${item.id}/preview`" target="_blank">{{ item.name }}</RouterLink>
|
||||
<div class="flex space-x-1">
|
||||
<div>
|
||||
<RouterLink :to="`/admin/dashboard/preview/${item.code}`" target="_blank">{{ item.name }}</RouterLink>
|
||||
</div>
|
||||
<div>
|
||||
<Tooltip :content="item.description">
|
||||
<Info :size="18" class="cursor-pointer"/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #extra>
|
||||
<DropdownMenu class="justify-items-end">
|
||||
@ -22,7 +31,7 @@
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem class="cursor-pointer">
|
||||
<RouterLink :to="`/admin/dashboard/info/${item.id}`" target="_blank" class="flex items-center">
|
||||
<RouterLink :to="`/admin/dashboard/info/${item.code}`" target="_blank" class="flex items-center">
|
||||
<Pencil :size="15" class="mr-1"/>
|
||||
{{ $t('dashboard.common.modify') }}
|
||||
</RouterLink>
|
||||
@ -34,8 +43,14 @@
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
<div class="text-2xl font-bold"></div>
|
||||
<p class="text-xs text-muted-foreground mt-2">{{ item.createTime }}</p>
|
||||
<div class="shadow-blackA7 w-full overflow-hidden rounded-md">
|
||||
<AspectRatio :ratio="16 / 11">
|
||||
<img class="h-full w-full object-cover" src="/static/images/dashboard.png" :alt="item.name"/>
|
||||
</AspectRatio>
|
||||
</div>
|
||||
<template #footer>
|
||||
<p class="text-xs text-muted-foreground text-right">{{ item.createTime }}</p>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
<div v-if="data.length === 0" class="text-center">
|
||||
@ -53,7 +68,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { Cog, Loader2, Pencil, Trash } from 'lucide-vue-next'
|
||||
import { Cog, Info, Loader2, Pencil, Trash } from 'lucide-vue-next'
|
||||
import DashboardService from '@/services/dashboard'
|
||||
import { FilterModel } from '@/model/filter'
|
||||
import { PaginationModel, PaginationRequest } from '@/model/pagination'
|
||||
@ -64,15 +79,19 @@ import Card from '@/views/ui/card'
|
||||
import Pagination from '@/views/ui/pagination'
|
||||
import Button from '@/views/ui/button'
|
||||
import { TableCaption } from '@/components/ui/table'
|
||||
import Tooltip from '@/views/ui/tooltip'
|
||||
import { AspectRatio } from 'radix-vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DashboardHome',
|
||||
components: {
|
||||
AspectRatio,
|
||||
Tooltip,
|
||||
TableCaption,
|
||||
Pagination,
|
||||
DashboardDelete,
|
||||
DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel, DropdownMenuContent, DropdownMenuTrigger, DropdownMenu,
|
||||
Loader2, Cog, Trash, Pencil,
|
||||
Loader2, Cog, Trash, Pencil, Info,
|
||||
Card,
|
||||
Button
|
||||
},
|
||||
|
@ -15,7 +15,6 @@ import { ToastUtils } from '@/utils/toast'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { DashboardModel } from '@/model/dashboard'
|
||||
import DashboardEditor from '@/views/pages/admin/dashboard/components/DashboardEditor.vue'
|
||||
import { toNumber } from 'lodash'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DashboardInfo',
|
||||
@ -37,10 +36,10 @@ export default defineComponent({
|
||||
{
|
||||
const router = useRouter()
|
||||
const params = router.currentRoute.value.params
|
||||
const id = params['id']
|
||||
if (id) {
|
||||
const code = params['code'] as string
|
||||
if (code) {
|
||||
this.loading = true
|
||||
DashboardService.getById(toNumber(id))
|
||||
DashboardService.getByCode(code)
|
||||
.then(response => {
|
||||
if (response.status) {
|
||||
this.dataInfo = response.data
|
||||
|
@ -2,6 +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'] as string)"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -12,10 +13,12 @@ import DashboardService from '@/services/dashboard'
|
||||
import { useRouter } from 'vue-router'
|
||||
import DashboardView from '@/views/pages/admin/dashboard/components/DashboardView.vue'
|
||||
import { DashboardModel } from '@/model/dashboard'
|
||||
import Alert from '@/views/ui/alert'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DashboardPreview',
|
||||
components: {
|
||||
Alert,
|
||||
DashboardView,
|
||||
Loader2
|
||||
},
|
||||
@ -36,7 +39,7 @@ export default defineComponent({
|
||||
this.loading = true
|
||||
const router = useRouter()
|
||||
const params = router.currentRoute.value.params
|
||||
DashboardService.getById(params['id'] as unknown as number)
|
||||
DashboardService.getByCode(params['code'] as string)
|
||||
.then(response => {
|
||||
if (response.status) {
|
||||
this.data = response.data
|
||||
|
@ -1,71 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<CircularLoading v-if="loading" :show="loading"/>
|
||||
<Card v-else class="mt-3" v-for="node in data">
|
||||
<CardHeader>
|
||||
<CardTitle>{{ node.name }}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div :draggable="true" @dragstart="onDragStart($event, node)">
|
||||
<EchartsPreview :key="node.id" :height="'200px'" :id="node.id" :configure="JSON.parse(node.configure)"/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import '../style.css'
|
||||
import { defineComponent } from 'vue'
|
||||
import ReportService from '@/services/report'
|
||||
import CircularLoading from '@/views/components/loading/CircularLoading.vue'
|
||||
import { FilterModel } from '@/model/filter'
|
||||
import EchartsPreview from '@/views/components/echarts/EchartsPreview.vue'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
|
||||
interface Node {
|
||||
id: number
|
||||
name: string
|
||||
configure: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DashboardChart',
|
||||
components: {
|
||||
CardContent, CardHeader, CardTitle, Card,
|
||||
EchartsPreview,
|
||||
CircularLoading
|
||||
},
|
||||
setup()
|
||||
{
|
||||
const onDragStart = (event: any, node: any) => {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.setData('application/vueflow', JSON.stringify(node))
|
||||
event.dataTransfer.effectAllowed = 'move'
|
||||
}
|
||||
}
|
||||
return {
|
||||
onDragStart
|
||||
}
|
||||
},
|
||||
data()
|
||||
{
|
||||
return {
|
||||
loading: false,
|
||||
data: [] as Node[]
|
||||
}
|
||||
},
|
||||
created()
|
||||
{
|
||||
this.loading = true
|
||||
const filter: FilterModel = new FilterModel()
|
||||
ReportService.getAll(filter)
|
||||
.then((response) => {
|
||||
if (response.status) {
|
||||
this.data = response.data.content
|
||||
}
|
||||
})
|
||||
.finally(() => this.loading = false)
|
||||
}
|
||||
});
|
||||
</script>
|
@ -32,21 +32,28 @@
|
||||
</div>
|
||||
</Card>
|
||||
<Dialog :is-visible="configureVisible" :title="$t('common.configure')">
|
||||
<div v-if="formState" class="p-6">
|
||||
<div v-if="formState" class="pl-3 pr-4">
|
||||
<FormField name="name">
|
||||
<FormItem class="space-y-1">
|
||||
<FormItem class="space-y-2">
|
||||
<FormLabel>{{ $t('common.name') }}</FormLabel>
|
||||
<FormMessage/>
|
||||
<Input v-model="formState.name"/>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FormField name="description">
|
||||
<FormItem class="space-y-2">
|
||||
<FormLabel>{{ $t('common.description') }}</FormLabel>
|
||||
<FormMessage/>
|
||||
<Textarea v-model="formState.description"/>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="space-x-5">
|
||||
<Button variant="outline" size="sm" @click="configureVisible = false">
|
||||
{{ $t('common.cancel') }}
|
||||
</Button>
|
||||
<Button :loading="loading" size="sm" @click="handlerSave">
|
||||
<Button :loading="loading" :disabled="loading" size="sm" @click="handlerSave">
|
||||
{{ $t('common.save') }}
|
||||
</Button>
|
||||
</div>
|
||||
@ -72,10 +79,12 @@ import Button from '@/views/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DashboardEditor',
|
||||
components: {
|
||||
Textarea,
|
||||
Input,
|
||||
ChartContainer,
|
||||
VisualView,
|
||||
@ -149,8 +158,8 @@ export default defineComponent({
|
||||
.then(response => {
|
||||
if (response.status) {
|
||||
ToastUtils.success(this.$t('dashboard.tip.publishSuccess').replace('$VALUE', <string>this.formState?.name))
|
||||
if (response.data?.id) {
|
||||
this.$router.push(`/admin/dashboard/info/${ response.data.id }/preview`)
|
||||
if (response.data) {
|
||||
this.$router.push(`/admin/dashboard/preview/${ response.data?.code }`)
|
||||
}
|
||||
else {
|
||||
this.$router.push('/console/dashboard')
|
||||
|
@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<NodeResizer :min-width="100" :min-height="30"/>
|
||||
<EchartsPreview :width="'250px'" :height="'200px'" :id="id" :configure="configure"/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { NodeResizer } from '@vue-flow/node-resizer'
|
||||
import EchartsPreview from '@/views/components/echarts/EchartsPreview.vue'
|
||||
import { ChartConfigure } from '@/views/components/echarts/configure/ChartConfigure'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DashboardNode',
|
||||
components: {NodeResizer, EchartsPreview},
|
||||
props: {
|
||||
configure: {
|
||||
type: Object as () => ChartConfigure | null
|
||||
},
|
||||
id: {
|
||||
type: Number
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -293,6 +293,7 @@ export default defineComponent({
|
||||
.then(response => {
|
||||
if (response.status) {
|
||||
this.formState.name = response.data.name
|
||||
this.formState.description = response.data.description
|
||||
const query = JSON.parse(response.data.query)
|
||||
this.mergeColumns(query.columns, this.metrics, ColumnType.METRIC)
|
||||
this.mergeColumns(query.columns, this.dimensions, ColumnType.DIMENSION)
|
||||
|
@ -3,8 +3,7 @@
|
||||
<template #title>{{ title }}</template>
|
||||
<CircularLoading v-if="loading" :show="loading"/>
|
||||
<div v-else-if="info">
|
||||
<EchartsPreview v-if="info.type === 'QUERY'" :width="'100%'" :height="'300px'" :id="info.id" :configure="JSON.parse(info.configure as string)"/>
|
||||
<VisualView v-else-if="info.type === 'DATASET'" :code="info.dataset?.code" :configuration="JSON.parse(info.configure as string)" :query="JSON.parse(info.query as string)"/>
|
||||
<VisualView :code="info.dataset?.code" :configuration="JSON.parse(info.configure as string)" :query="JSON.parse(info.query as string)"/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button variant="outline" size="sm" @click="handlerCancel">
|
||||
@ -20,14 +19,12 @@ import { ReportModel } from '@/model/report'
|
||||
import Dialog from '@/views/ui/dialog'
|
||||
import Button from '@/views/ui/button'
|
||||
import CircularLoading from '@/views/components/loading/CircularLoading.vue'
|
||||
import EchartsPreview from '@/views/components/echarts/EchartsPreview.vue'
|
||||
import VisualView from '@/views/components/visual/VisualView.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ReportView',
|
||||
components: {
|
||||
VisualView,
|
||||
EchartsPreview,
|
||||
CircularLoading,
|
||||
Dialog,
|
||||
Button
|
||||
|
@ -14,7 +14,7 @@
|
||||
<CardContent :class="`${bodyClass}`">
|
||||
<slot/>
|
||||
</CardContent>
|
||||
<CardFooter v-if="$slots.footer">
|
||||
<CardFooter v-if="$slots.footer" :class="`border-t ${footerClass}`">
|
||||
<slot name="footer"/>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
@ -39,6 +39,9 @@ export default defineComponent({
|
||||
bodyClass: {
|
||||
type: String
|
||||
},
|
||||
footerClass: {
|
||||
type: String
|
||||
},
|
||||
hiddenTitle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
Loading…
Reference in New Issue
Block a user