@liujitcn/kratos-admin-bi 0.0.0-stage → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -2
- package/dist/package/declarations/index.d.ts +1 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_card.d.ts +18 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_card.js +46 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_dashboard.d.ts +17 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_dashboard.js +30 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_query.d.ts +11 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_query.js +41 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_query_record.d.ts +10 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_query_record.js +14 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_source.d.ts +26 -0
- package/dist/package/declarations/src/api/bi/admin/v1/bi_source.js +46 -0
- package/dist/package/declarations/src/bi-core.d.ts +17 -0
- package/dist/package/declarations/src/bi-core.js +22 -0
- package/dist/package/declarations/src/bi-dashboard-schema.d.ts +31 -0
- package/dist/package/declarations/src/bi-dashboard-schema.js +39 -0
- package/dist/package/declarations/src/bi-messages.d.ts +247 -0
- package/dist/package/declarations/src/bi-messages.js +247 -0
- package/dist/package/declarations/src/bi-response.d.ts +10 -0
- package/dist/package/declarations/src/bi-response.js +33 -0
- package/dist/package/declarations/src/bi-vue.d.ts +10 -0
- package/dist/package/declarations/src/bi-vue.js +10 -0
- package/dist/package/declarations/src/card-schema.d.ts +30 -0
- package/dist/package/declarations/src/card-schema.js +87 -0
- package/dist/package/declarations/src/components/BiCardChartEditor.vue.d.ts +17 -0
- package/dist/package/declarations/src/components/BiCardChartEditor.vue.js +527 -0
- package/dist/package/declarations/src/components/BiCardFormDialog.vue.d.ts +20 -0
- package/dist/package/declarations/src/components/BiCardFormDialog.vue.js +495 -0
- package/dist/package/declarations/src/components/BiCardLibrary.vue.d.ts +3 -0
- package/dist/package/declarations/src/components/BiCardLibrary.vue.js +807 -0
- package/dist/package/declarations/src/components/BiCardVisualization.vue.d.ts +10 -0
- package/dist/package/declarations/src/components/BiCardVisualization.vue.js +97 -0
- package/dist/package/declarations/src/components/BiChart.vue.d.ts +20 -0
- package/dist/package/declarations/src/components/BiChart.vue.js +112 -0
- package/dist/package/declarations/src/components/BiDashboard.vue.d.ts +9 -0
- package/dist/package/declarations/src/components/BiDashboard.vue.js +81 -0
- package/dist/package/declarations/src/components/BiDashboardMenuDialog.vue.d.ts +12 -0
- package/dist/package/declarations/src/components/BiDashboardMenuDialog.vue.js +252 -0
- package/dist/package/declarations/src/components/BiDataCard.vue.d.ts +36 -0
- package/dist/package/declarations/src/components/BiDataCard.vue.js +372 -0
- package/dist/package/declarations/src/components/BiDesigner.vue.d.ts +17 -0
- package/dist/package/declarations/src/components/BiDesigner.vue.js +666 -0
- package/dist/package/declarations/src/components/BiSourceModelDialog.vue.d.ts +17 -0
- package/dist/package/declarations/src/components/BiSourceModelDialog.vue.js +2118 -0
- package/dist/package/declarations/src/components/BiWorkbenchPreview.vue.d.ts +7 -0
- package/dist/package/declarations/src/components/BiWorkbenchPreview.vue.js +176 -0
- package/dist/package/declarations/src/components/source-model-layout.d.ts +3 -0
- package/dist/package/declarations/src/components/source-model-layout.js +66 -0
- package/dist/package/declarations/src/dataset/index.d.ts +7 -0
- package/dist/package/declarations/src/dataset/index.js +25 -0
- package/dist/package/declarations/src/dataset/labels.d.ts +2 -0
- package/dist/package/declarations/src/dataset/labels.js +21 -0
- package/dist/package/declarations/src/dataset/transform.d.ts +20 -0
- package/dist/package/declarations/src/dataset/transform.js +173 -0
- package/dist/package/declarations/src/engine/chart-types.d.ts +14 -0
- package/dist/package/declarations/src/engine/chart-types.js +37 -0
- package/dist/package/declarations/src/engine/echarts.d.ts +16 -0
- package/dist/package/declarations/src/engine/echarts.js +491 -0
- package/dist/package/declarations/src/engine/types.d.ts +36 -0
- package/dist/package/declarations/src/engine/types.js +20 -0
- package/dist/package/declarations/src/env.d.ts +17 -0
- package/dist/package/declarations/src/env.js +67 -0
- package/dist/package/declarations/src/index.d.ts +1 -0
- package/dist/package/declarations/src/index.js +1 -0
- package/dist/package/declarations/src/locales/en-US.json +254 -0
- package/dist/package/declarations/src/locales/generated.d.ts +1021 -0
- package/dist/package/declarations/src/locales/generated.js +13 -0
- package/dist/package/declarations/src/locales/ja-JP.json +254 -0
- package/dist/package/declarations/src/locales/zh-CN.json +254 -0
- package/dist/package/declarations/src/locales/zh-TW.json +254 -0
- package/dist/package/declarations/src/module.d.ts +2 -0
- package/dist/package/declarations/src/module.js +9 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_card.d.ts +145 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_card.js +6 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_dashboard.d.ts +107 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_dashboard.js +6 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_query.d.ts +157 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_query.js +6 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_query_record.d.ts +83 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_query_record.js +15 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_source.d.ts +301 -0
- package/dist/package/declarations/src/rpc/bi/admin/v1/bi_source.js +6 -0
- package/dist/package/declarations/src/rpc/common/v1/types.d.ts +60 -0
- package/dist/package/declarations/src/rpc/common/v1/types.js +6 -0
- package/dist/package/declarations/src/schema/layout.d.ts +7 -0
- package/dist/package/declarations/src/schema/layout.js +52 -0
- package/dist/package/declarations/src/schema/types.d.ts +83 -0
- package/dist/package/declarations/src/schema/types.js +1 -0
- package/dist/package/declarations/src/theme/index.d.ts +29 -0
- package/dist/package/declarations/src/theme/index.js +83 -0
- package/dist/package/declarations/src/theme/palettes.d.ts +7 -0
- package/dist/package/declarations/src/theme/palettes.js +28 -0
- package/dist/package/declarations/src/theme/presets.d.ts +73 -0
- package/dist/package/declarations/src/theme/presets.js +235 -0
- package/dist/package/declarations/src/views/card/index.vue.d.ts +3 -0
- package/dist/package/declarations/src/views/card/index.vue.js +18 -0
- package/dist/package/declarations/src/views/dashboard/index.vue.d.ts +3 -0
- package/dist/package/declarations/src/views/dashboard/index.vue.js +107 -0
- package/dist/package/declarations/src/views/designer/index.vue.d.ts +3 -0
- package/dist/package/declarations/src/views/designer/index.vue.js +332 -0
- package/dist/package/declarations/src/views/manage/index.vue.d.ts +3 -0
- package/dist/package/declarations/src/views/manage/index.vue.js +325 -0
- package/dist/package/declarations/src/views/query/index.vue.d.ts +3 -0
- package/dist/package/declarations/src/views/query/index.vue.js +876 -0
- package/dist/package/declarations/src/views/source/index.vue.d.ts +3 -0
- package/dist/package/declarations/src/views/source/index.vue.js +343 -0
- package/dist/package/declarations/src/views/workbench/index.vue.d.ts +3 -0
- package/dist/package/declarations/src/views/workbench/index.vue.js +362 -0
- package/dist/package/src/api/bi/admin/v1/bi_card.ts +67 -0
- package/dist/package/src/api/bi/admin/v1/bi_dashboard.ts +51 -0
- package/dist/package/src/api/bi/admin/v1/bi_query.ts +55 -0
- package/dist/package/src/api/bi/admin/v1/bi_query_record.ts +25 -0
- package/dist/package/src/api/bi/admin/v1/bi_source.ts +80 -0
- package/dist/package/src/bi-core.ts +43 -0
- package/dist/package/src/bi-dashboard-schema.ts +69 -0
- package/dist/package/src/bi-messages.ts +249 -0
- package/dist/package/src/bi-response.ts +39 -0
- package/dist/package/src/bi-vue.ts +13 -0
- package/dist/package/src/card-schema.ts +111 -0
- package/dist/package/src/components/BiCardChartEditor.vue +139 -0
- package/dist/package/src/components/BiCardFormDialog.vue +289 -0
- package/dist/package/src/components/BiCardLibrary.vue +310 -0
- package/dist/package/src/components/BiCardVisualization.vue +95 -0
- package/dist/package/src/components/BiChart.vue +134 -0
- package/dist/package/src/components/BiDashboard.vue +53 -0
- package/dist/package/src/components/BiDashboardMenuDialog.vue +177 -0
- package/dist/package/src/components/BiDataCard.vue +231 -0
- package/dist/package/src/components/BiDesigner.vue +287 -0
- package/dist/package/src/components/BiSourceModelDialog.vue +846 -0
- package/dist/package/src/components/BiWorkbenchPreview.vue +131 -0
- package/dist/package/src/components/source-model-layout.ts +62 -0
- package/dist/package/src/dataset/index.ts +28 -0
- package/dist/package/src/dataset/labels.ts +19 -0
- package/dist/package/src/dataset/transform.ts +189 -0
- package/dist/package/src/engine/chart-types.ts +44 -0
- package/dist/package/src/engine/echarts.ts +568 -0
- package/dist/package/src/engine/types.ts +60 -0
- package/dist/package/src/env.ts +82 -0
- package/dist/package/src/index.ts +1 -0
- package/dist/package/src/locales/en-US.json +254 -0
- package/dist/package/src/locales/generated.ts +16 -0
- package/dist/package/src/locales/ja-JP.json +254 -0
- package/dist/package/src/locales/zh-CN.json +254 -0
- package/dist/package/src/locales/zh-TW.json +254 -0
- package/dist/package/src/module.ts +12 -0
- package/dist/package/src/rpc/bi/admin/v1/bi_card.ts +169 -0
- package/dist/package/src/rpc/bi/admin/v1/bi_dashboard.ts +126 -0
- package/dist/package/src/rpc/bi/admin/v1/bi_query.ts +187 -0
- package/dist/package/src/rpc/bi/admin/v1/bi_query_record.ts +98 -0
- package/dist/package/src/rpc/bi/admin/v1/bi_source.ts +338 -0
- package/dist/package/src/rpc/common/v1/types.ts +77 -0
- package/dist/package/src/schema/layout.ts +54 -0
- package/dist/package/src/schema/types.ts +103 -0
- package/dist/package/src/styles.scss +1 -0
- package/dist/package/src/theme/index.ts +108 -0
- package/dist/package/src/theme/palettes.ts +34 -0
- package/dist/package/src/theme/presets.ts +278 -0
- package/dist/package/src/views/card/index.vue +5 -0
- package/dist/package/src/views/dashboard/index.vue +58 -0
- package/dist/package/src/views/designer/index.vue +164 -0
- package/dist/package/src/views/manage/index.vue +276 -0
- package/dist/package/src/views/query/index.vue +593 -0
- package/dist/package/src/views/source/index.vue +214 -0
- package/dist/package/src/views/workbench/index.vue +208 -0
- package/package.json +67 -3
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FormDialog
|
|
3
|
+
ref="dialogRef"
|
|
4
|
+
:model-value="modelValue"
|
|
5
|
+
:title="card ? t('bi.card.edit') : t('bi.card.create')"
|
|
6
|
+
width="min(1040px, 94vw)"
|
|
7
|
+
label-width="7em"
|
|
8
|
+
:model="form"
|
|
9
|
+
:fields="fields"
|
|
10
|
+
:rules="rules"
|
|
11
|
+
:confirm-loading="saving"
|
|
12
|
+
:close-on-click-modal="!saving"
|
|
13
|
+
:form-props="{ disabled: saving }"
|
|
14
|
+
@update:model-value="emit('update:modelValue', $event)"
|
|
15
|
+
@confirm="submit"
|
|
16
|
+
:destroy-on-close="true"
|
|
17
|
+
@closed="reset"
|
|
18
|
+
>
|
|
19
|
+
<template #measures>
|
|
20
|
+
<div class="bi-card-measures">
|
|
21
|
+
<div v-for="(measure, index) in form.measures" :key="index" class="bi-card-measures__row">
|
|
22
|
+
<el-select disabled v-model="measure.code" :aria-label="t('bi.card.measure')">
|
|
23
|
+
<el-option v-for="item in source?.measures ?? []" :key="item.code" :label="item.name" :value="item.code" />
|
|
24
|
+
</el-select>
|
|
25
|
+
<el-select disabled v-model="measure.aggregation" :aria-label="t('bi.card.aggregation')">
|
|
26
|
+
<el-option
|
|
27
|
+
v-for="aggregation in source?.measures.find(item => item.code === measure.code)?.aggregations ?? []"
|
|
28
|
+
:key="aggregation"
|
|
29
|
+
:label="t(`bi.card.aggregation.${aggregation}`)"
|
|
30
|
+
:value="aggregation"
|
|
31
|
+
/>
|
|
32
|
+
</el-select>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
<template #chart>
|
|
37
|
+
<BiCardChartEditor v-model="chartConfig" v-model:chart-type="form.chart_type" :query="query" :result="previewResult" />
|
|
38
|
+
</template>
|
|
39
|
+
<template #preview>
|
|
40
|
+
<BiDataCard v-if="previewResult" :card="previewCard" :preview-result="previewResult" :source-name="source?.name" />
|
|
41
|
+
<el-empty v-else :image-size="36" :description="t('bi.card.preview_empty')" />
|
|
42
|
+
</template>
|
|
43
|
+
<template #footer>
|
|
44
|
+
<el-button :disabled="saving" @click="emit('update:modelValue', false)">{{ t("common.action.cancel") }}</el-button>
|
|
45
|
+
<el-button :loading="previewing" :disabled="saving" @click="preview">{{ t("bi.card.preview") }}</el-button>
|
|
46
|
+
<el-button type="primary" :loading="saving" @click="submit">{{ t("bi.card.save") }}</el-button>
|
|
47
|
+
</template>
|
|
48
|
+
</FormDialog>
|
|
49
|
+
</template>
|
|
50
|
+
<script setup lang="ts">
|
|
51
|
+
import { computed, reactive, ref, watch } from "vue";
|
|
52
|
+
import { t } from "@liujitcn/kratos-admin-core";
|
|
53
|
+
import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
|
|
54
|
+
import type { ProFormField } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
|
|
55
|
+
import type { BiCard } from "../rpc/bi/admin/v1/bi_card";
|
|
56
|
+
import type { BiMeasureSpec, BiQueryResult, BiQuerySpec, BiSourceDescriptor } from "../rpc/bi/admin/v1/bi_query";
|
|
57
|
+
import { defBiCardService } from "../api/bi/admin/v1/bi_card";
|
|
58
|
+
import { defBiQueryService } from "../api/bi/admin/v1/bi_query";
|
|
59
|
+
import BiDataCard from "./BiDataCard.vue";
|
|
60
|
+
import BiCardChartEditor from "./BiCardChartEditor.vue";
|
|
61
|
+
import { parseCardChart } from "../card-schema";
|
|
62
|
+
import type { CardChartConfig } from "../card-schema";
|
|
63
|
+
|
|
64
|
+
/** 卡片编辑状态,查询条件沿用原语义计划。 */
|
|
65
|
+
interface BiCardEditState {
|
|
66
|
+
name: string;
|
|
67
|
+
source_code: string;
|
|
68
|
+
dimensions: string[];
|
|
69
|
+
measures: BiMeasureSpec[];
|
|
70
|
+
chart_type: string;
|
|
71
|
+
refresh_seconds: number;
|
|
72
|
+
is_shared: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const props = defineProps<{
|
|
76
|
+
modelValue: boolean;
|
|
77
|
+
sources: BiSourceDescriptor[];
|
|
78
|
+
card?: BiCard;
|
|
79
|
+
initialQuery?: BiQuerySpec;
|
|
80
|
+
initialResult?: BiQueryResult;
|
|
81
|
+
initialTitle?: string;
|
|
82
|
+
initialChartType?: string;
|
|
83
|
+
}>();
|
|
84
|
+
const emit = defineEmits<{ "update:modelValue": [value: boolean]; saved: [card: BiCard] }>();
|
|
85
|
+
const dialogRef = ref<InstanceType<typeof FormDialog>>();
|
|
86
|
+
const form = reactive<BiCardEditState>(defaultForm());
|
|
87
|
+
const baseQuery = ref<BiQuerySpec>();
|
|
88
|
+
const previewResult = ref<BiQueryResult>();
|
|
89
|
+
const sourceRecordID = ref(0);
|
|
90
|
+
const chartConfig = ref<CardChartConfig>({ encoding: {}, style: { showLegend: true, showLabel: false } });
|
|
91
|
+
const saving = ref(false);
|
|
92
|
+
const previewing = ref(false);
|
|
93
|
+
let previewRequest = 0;
|
|
94
|
+
const source = computed(() => props.sources.find(item => item.code === form.source_code));
|
|
95
|
+
const query = computed<BiQuerySpec>(() => ({
|
|
96
|
+
source_code: form.source_code,
|
|
97
|
+
dimensions: [...form.dimensions],
|
|
98
|
+
measures: form.measures.map(item => ({ ...item })),
|
|
99
|
+
filters: baseQuery.value?.filters ?? [],
|
|
100
|
+
limit: baseQuery.value?.limit || 100,
|
|
101
|
+
time_range: baseQuery.value?.time_range,
|
|
102
|
+
orders: baseQuery.value?.orders ?? []
|
|
103
|
+
}));
|
|
104
|
+
const previewCard = computed<BiCard>(() => ({
|
|
105
|
+
id: 0,
|
|
106
|
+
tenant_id: 0,
|
|
107
|
+
project_id: 0,
|
|
108
|
+
owner_id: 0,
|
|
109
|
+
name: form.name,
|
|
110
|
+
source_code: form.source_code,
|
|
111
|
+
query: query.value,
|
|
112
|
+
chart_type: form.chart_type,
|
|
113
|
+
chart_json: JSON.stringify(chartConfig.value),
|
|
114
|
+
query_record_id: sourceRecordID.value,
|
|
115
|
+
refresh_seconds: form.refresh_seconds,
|
|
116
|
+
is_shared: form.is_shared,
|
|
117
|
+
updated_at: "",
|
|
118
|
+
category: "mine",
|
|
119
|
+
can_edit: true
|
|
120
|
+
}));
|
|
121
|
+
const fields = computed<ProFormField[]>(() => [
|
|
122
|
+
{ prop: "name", label: t("bi.card.name"), component: "input", colSpan: 12, props: { maxlength: 128 } },
|
|
123
|
+
{
|
|
124
|
+
prop: "source_code",
|
|
125
|
+
label: t("bi.card.source"),
|
|
126
|
+
component: "select",
|
|
127
|
+
colSpan: 12,
|
|
128
|
+
options: props.sources.map(item => ({ label: item.name, value: item.code })),
|
|
129
|
+
props: { disabled: true }
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
prop: "dimensions",
|
|
133
|
+
label: t("bi.card.dimensions"),
|
|
134
|
+
component: "select",
|
|
135
|
+
colSpan: 12,
|
|
136
|
+
options: (source.value?.dimensions ?? []).map(item => ({ label: item.name, value: item.code })),
|
|
137
|
+
props: {
|
|
138
|
+
multiple: true,
|
|
139
|
+
multipleLimit: 2,
|
|
140
|
+
disabled: true
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{ prop: "measures", label: t("bi.card.measure"), component: "slot", slotName: "measures", colSpan: 12 },
|
|
144
|
+
{ prop: "chart", label: t("bi.card.chart_type"), component: "slot", slotName: "chart", colSpan: 12 },
|
|
145
|
+
{ prop: "preview", label: t("bi.card.preview"), component: "slot", slotName: "preview", colSpan: 12 },
|
|
146
|
+
{
|
|
147
|
+
prop: "refresh_seconds",
|
|
148
|
+
label: t("bi.card.refresh"),
|
|
149
|
+
component: "select",
|
|
150
|
+
colSpan: 12,
|
|
151
|
+
options: [0, 300, 86400].map(value => ({ label: t(`bi.card.refresh.${value}`), value }))
|
|
152
|
+
},
|
|
153
|
+
{ prop: "is_shared", label: t("bi.card.share"), component: "switch", colSpan: 12 }
|
|
154
|
+
]);
|
|
155
|
+
const rules = computed(() => ({
|
|
156
|
+
name: [{ required: true, whitespace: true, message: t("bi.card.validation.name"), trigger: "blur" }],
|
|
157
|
+
source_code: [{ required: true, message: t("bi.card.validation.source"), trigger: "change" }],
|
|
158
|
+
measures: [{ type: "array" as const, required: true, min: 1, message: t("bi.card.validation.measures"), trigger: "change" }]
|
|
159
|
+
}));
|
|
160
|
+
|
|
161
|
+
/** 创建独立表单状态,关闭后不保留上次卡片。 */
|
|
162
|
+
function defaultForm(): BiCardEditState {
|
|
163
|
+
return {
|
|
164
|
+
name: "",
|
|
165
|
+
source_code: "",
|
|
166
|
+
dimensions: [],
|
|
167
|
+
measures: [],
|
|
168
|
+
chart_type: "bar",
|
|
169
|
+
refresh_seconds: 0,
|
|
170
|
+
is_shared: false
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** 打开弹窗时复制原查询,保留问数生成的过滤、排序和时间范围。 */
|
|
175
|
+
watch(
|
|
176
|
+
() => props.modelValue,
|
|
177
|
+
visible => {
|
|
178
|
+
if (!visible) return;
|
|
179
|
+
const current = props.card?.query ?? props.initialQuery;
|
|
180
|
+
baseQuery.value = current ? (JSON.parse(JSON.stringify(current)) as BiQuerySpec) : undefined;
|
|
181
|
+
Object.assign(form, defaultForm(), {
|
|
182
|
+
name: props.card?.name ?? props.initialTitle ?? "",
|
|
183
|
+
source_code: current?.source_code || props.sources[0]?.code || "",
|
|
184
|
+
dimensions: [...(current?.dimensions ?? [])],
|
|
185
|
+
measures: (current?.measures ?? []).map(item => ({ ...item })),
|
|
186
|
+
chart_type: props.card?.chart_type || (current && !current.dimensions.length ? "metric" : props.initialChartType || "bar"),
|
|
187
|
+
refresh_seconds: props.card?.refresh_seconds ?? 0,
|
|
188
|
+
is_shared: props.card?.is_shared ?? false
|
|
189
|
+
});
|
|
190
|
+
previewResult.value = props.initialResult;
|
|
191
|
+
sourceRecordID.value = props.card?.query_record_id || props.initialResult?.query_record_id || 0;
|
|
192
|
+
chartConfig.value = parseCardChart(props.card ?? { chart_json: "" });
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
watch(
|
|
196
|
+
query,
|
|
197
|
+
() => {
|
|
198
|
+
previewRequest++;
|
|
199
|
+
previewResult.value = undefined;
|
|
200
|
+
sourceRecordID.value = 0;
|
|
201
|
+
chartConfig.value = { encoding: {}, style: { ...chartConfig.value.style } };
|
|
202
|
+
},
|
|
203
|
+
{ deep: true, flush: "sync" }
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
/** 预览当前语义配置,忽略配置变化前的迟到响应。 */
|
|
207
|
+
async function preview() {
|
|
208
|
+
if (!(await dialogRef.value?.validate()) || previewing.value) return;
|
|
209
|
+
if (!sourceRecordID.value) {
|
|
210
|
+
ElMessage.warning(t("bi.card.ask_required"));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const request = ++previewRequest;
|
|
214
|
+
previewing.value = true;
|
|
215
|
+
try {
|
|
216
|
+
const result = await defBiQueryService.PreviewBiQuery({ query: query.value });
|
|
217
|
+
if (request === previewRequest) {
|
|
218
|
+
previewResult.value = result;
|
|
219
|
+
}
|
|
220
|
+
} catch (cause) {
|
|
221
|
+
ElMessage.error(cause instanceof Error ? cause.message : t("bi.query.error"));
|
|
222
|
+
} finally {
|
|
223
|
+
previewing.value = false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** 仅保存与原始智能问数关联的卡片,图表编辑不改变查询定义。 */
|
|
228
|
+
async function submit() {
|
|
229
|
+
if (!(await dialogRef.value?.validate()) || saving.value || previewing.value) return;
|
|
230
|
+
if (!sourceRecordID.value) {
|
|
231
|
+
ElMessage.warning(t("bi.card.ask_required"));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!previewResult.value) await preview();
|
|
235
|
+
if (!previewResult.value || !sourceRecordID.value) return;
|
|
236
|
+
saving.value = true;
|
|
237
|
+
try {
|
|
238
|
+
const saved = await defBiCardService.SaveBiCard({
|
|
239
|
+
id: props.card?.id ?? 0,
|
|
240
|
+
card: {
|
|
241
|
+
name: form.name,
|
|
242
|
+
query: query.value,
|
|
243
|
+
chart_type: form.chart_type,
|
|
244
|
+
chart_json: JSON.stringify(chartConfig.value),
|
|
245
|
+
query_record_id: sourceRecordID.value,
|
|
246
|
+
refresh_seconds: form.refresh_seconds,
|
|
247
|
+
is_shared: form.is_shared
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
emit("saved", saved);
|
|
251
|
+
emit("update:modelValue", false);
|
|
252
|
+
ElMessage.success(t("bi.card.saved"));
|
|
253
|
+
} catch (cause) {
|
|
254
|
+
ElMessage.error(cause instanceof Error ? cause.message : t("bi.card.save_failed"));
|
|
255
|
+
} finally {
|
|
256
|
+
saving.value = false;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** 关闭弹窗后清空表单、预览与校验状态。 */
|
|
261
|
+
function reset() {
|
|
262
|
+
previewRequest++;
|
|
263
|
+
baseQuery.value = undefined;
|
|
264
|
+
Object.assign(form, defaultForm());
|
|
265
|
+
previewResult.value = undefined;
|
|
266
|
+
sourceRecordID.value = 0;
|
|
267
|
+
chartConfig.value = { encoding: {}, style: { showLegend: true, showLabel: false } };
|
|
268
|
+
dialogRef.value?.clearValidate();
|
|
269
|
+
}
|
|
270
|
+
</script>
|
|
271
|
+
|
|
272
|
+
<style scoped>
|
|
273
|
+
.bi-card-measures {
|
|
274
|
+
display: flex;
|
|
275
|
+
width: 100%;
|
|
276
|
+
flex-direction: column;
|
|
277
|
+
gap: 8px;
|
|
278
|
+
}
|
|
279
|
+
.bi-card-measures__row {
|
|
280
|
+
display: grid;
|
|
281
|
+
grid-template-columns: minmax(0, 1fr) minmax(90px, 0.65fr) auto;
|
|
282
|
+
gap: 8px;
|
|
283
|
+
}
|
|
284
|
+
@media (max-width: 480px) {
|
|
285
|
+
.bi-card-measures__row {
|
|
286
|
+
grid-template-columns: minmax(0, 1fr) 90px auto;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
</style>
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<main class="bi-card-library">
|
|
3
|
+
<el-card class="admin-page-card" shadow="never">
|
|
4
|
+
<el-tabs v-model="category" @tab-change="search">
|
|
5
|
+
<el-tab-pane v-for="item in categories" :key="item" :name="item">
|
|
6
|
+
<template #label>
|
|
7
|
+
<span>{{ t(`bi.card.category.${item || "all"}`) }}</span>
|
|
8
|
+
<small class="bi-card-library__count">{{ counts[item || "all"] }}</small>
|
|
9
|
+
</template>
|
|
10
|
+
</el-tab-pane>
|
|
11
|
+
</el-tabs>
|
|
12
|
+
<div class="bi-card-library__filters">
|
|
13
|
+
<el-input v-model="name" :placeholder="t('bi.card.search')" clearable @keyup.enter="search" @clear="search" />
|
|
14
|
+
<el-select v-model="sourceCode" :placeholder="t('bi.card.all_sources')" clearable @change="search">
|
|
15
|
+
<el-option v-for="item in sources" :key="item.code" :label="item.name" :value="item.code" />
|
|
16
|
+
</el-select>
|
|
17
|
+
<el-button type="primary" :icon="icons.Search" :loading="loading" @click="search">{{
|
|
18
|
+
t("common.action.search")
|
|
19
|
+
}}</el-button>
|
|
20
|
+
<span>{{ t("bi.card.total", { count: total }) }}</span>
|
|
21
|
+
</div>
|
|
22
|
+
</el-card>
|
|
23
|
+
<el-skeleton v-if="loading" :rows="6" animated /><el-empty v-else-if="!cards.length" :description="t('bi.card.empty')" />
|
|
24
|
+
<div v-else class="bi-card-library__grid">
|
|
25
|
+
<BiDataCard
|
|
26
|
+
v-for="card in cards"
|
|
27
|
+
:key="card.id"
|
|
28
|
+
:card="card"
|
|
29
|
+
:source-name="sources.find(item => item.code === card.source_code)?.name || card.source_code"
|
|
30
|
+
><template #actions>
|
|
31
|
+
<el-tooltip :content="t(workbench.favorite_ids.includes(card.id) ? 'bi.card.unfavorite' : 'bi.card.favorite', { name: card.name })">
|
|
32
|
+
<el-button
|
|
33
|
+
text
|
|
34
|
+
circle
|
|
35
|
+
:type="workbench.favorite_ids.includes(card.id) ? 'warning' : 'default'"
|
|
36
|
+
:icon="workbench.favorite_ids.includes(card.id) ? icons.StarFilled : icons.Star"
|
|
37
|
+
:aria-label="t(workbench.favorite_ids.includes(card.id) ? 'bi.card.unfavorite' : 'bi.card.favorite', { name: card.name })"
|
|
38
|
+
:aria-pressed="workbench.favorite_ids.includes(card.id)"
|
|
39
|
+
@click="toggleFavorite(card)"
|
|
40
|
+
/>
|
|
41
|
+
</el-tooltip>
|
|
42
|
+
</template
|
|
43
|
+
><template #footer
|
|
44
|
+
><div>
|
|
45
|
+
<el-button link type="primary" @click="previewCard = card">{{ t("bi.card.preview") }}</el-button
|
|
46
|
+
><el-button v-if="card.can_edit" link @click="openEditor(card)">{{ t("common.action.edit") }}</el-button
|
|
47
|
+
><el-button v-if="card.can_edit" link type="danger" @click="remove(card)">{{ t("common.action.delete") }}</el-button>
|
|
48
|
+
</div>
|
|
49
|
+
<el-button size="small" type="primary" @click="pickDashboard(card)">{{ t("bi.card.add_to_dashboard") }}</el-button
|
|
50
|
+
><el-button
|
|
51
|
+
v-if="card.query_record_id"
|
|
52
|
+
link
|
|
53
|
+
:disabled="!card.can_edit"
|
|
54
|
+
@click="router.push({ path: '/bi/query', query: { record_id: String(card.query_record_id) } })"
|
|
55
|
+
>{{ t("bi.card.source_record", { id: card.query_record_id }) }}</el-button
|
|
56
|
+
></template
|
|
57
|
+
></BiDataCard
|
|
58
|
+
>
|
|
59
|
+
</div>
|
|
60
|
+
<el-pagination
|
|
61
|
+
v-if="total > 100"
|
|
62
|
+
v-model:current-page="page"
|
|
63
|
+
:page-size="100"
|
|
64
|
+
:total="total"
|
|
65
|
+
layout="prev,pager,next"
|
|
66
|
+
@current-change="loadCards"
|
|
67
|
+
/><BiCardFormDialog v-model="editorOpen" :sources="sources" :card="editingCard" @saved="reload" /><el-dialog
|
|
68
|
+
:model-value="Boolean(previewCard)"
|
|
69
|
+
:title="t('bi.card.preview')"
|
|
70
|
+
width="min(760px,94vw)"
|
|
71
|
+
destroy-on-close
|
|
72
|
+
@close="previewCard = undefined"
|
|
73
|
+
><BiDataCard
|
|
74
|
+
v-if="previewCard"
|
|
75
|
+
:card="previewCard"
|
|
76
|
+
:source-name="sources.find(item => item.code === previewCard?.source_code)?.name || previewCard.source_code" /></el-dialog
|
|
77
|
+
><FormDialog
|
|
78
|
+
v-model="dashboardOpen"
|
|
79
|
+
:model="target"
|
|
80
|
+
:title="t('bi.card.add_to_dashboard')"
|
|
81
|
+
:fields="dashboardFields"
|
|
82
|
+
:rules="{ id: [{ required: true, message: t('bi.card.select_dashboard'), trigger: 'change' }] }"
|
|
83
|
+
:confirm-loading="saving"
|
|
84
|
+
@confirm="addToDashboard"
|
|
85
|
+
@closed="target.id = undefined"
|
|
86
|
+
/>
|
|
87
|
+
</main>
|
|
88
|
+
</template>
|
|
89
|
+
<script setup lang="ts">
|
|
90
|
+
import { computed, onMounted, onActivated, reactive, ref } from "vue";
|
|
91
|
+
import { useRouter } from "vue-router";
|
|
92
|
+
import { t } from "@liujitcn/kratos-admin-core";
|
|
93
|
+
import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
|
|
94
|
+
import type { ProFormField } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
|
|
95
|
+
import type { BiCard, BiCardCounts, BiWorkbench } from "../rpc/bi/admin/v1/bi_card";
|
|
96
|
+
import type { BiDashboard } from "../rpc/bi/admin/v1/bi_dashboard";
|
|
97
|
+
import type { BiSourceDescriptor } from "../rpc/bi/admin/v1/bi_query";
|
|
98
|
+
import { defBiCardService } from "../api/bi/admin/v1/bi_card";
|
|
99
|
+
import { defBiQueryService } from "../api/bi/admin/v1/bi_query";
|
|
100
|
+
import { defBiDashboardService } from "../api/bi/admin/v1/bi_dashboard";
|
|
101
|
+
import { parseDashboardLayout, reflowDashboardCards } from "../bi-dashboard-schema";
|
|
102
|
+
import BiDataCard from "./BiDataCard.vue";
|
|
103
|
+
import BiCardFormDialog from "./BiCardFormDialog.vue";
|
|
104
|
+
const icons = { Star, StarFilled, Search };
|
|
105
|
+
const router = useRouter(),
|
|
106
|
+
cards = ref<BiCard[]>([]),
|
|
107
|
+
sources = ref<BiSourceDescriptor[]>([]),
|
|
108
|
+
name = ref(""),
|
|
109
|
+
sourceCode = ref(""),
|
|
110
|
+
category = ref(""),
|
|
111
|
+
page = ref(1),
|
|
112
|
+
total = ref(0),
|
|
113
|
+
loading = ref(false),
|
|
114
|
+
saving = ref(false),
|
|
115
|
+
editorOpen = ref(false),
|
|
116
|
+
dashboardOpen = ref(false),
|
|
117
|
+
editingCard = ref<BiCard>(),
|
|
118
|
+
previewCard = ref<BiCard>(),
|
|
119
|
+
addingCard = ref<BiCard>(),
|
|
120
|
+
dashboards = ref<BiDashboard[]>([]);
|
|
121
|
+
const categories = ["", "mine", "favorite", "shared", "system"];
|
|
122
|
+
const counts = ref<BiCardCounts>({ all: 0, system: 0, mine: 0, favorite: 0, shared: 0 });
|
|
123
|
+
const workbench = ref<BiWorkbench>({ dashboard_ids: [], favorite_ids: [], dashboards: [] });
|
|
124
|
+
const target = reactive({ id: undefined as number | undefined });
|
|
125
|
+
let request = 0,
|
|
126
|
+
loaded = false;
|
|
127
|
+
const dashboardFields = computed<ProFormField[]>(() => [
|
|
128
|
+
{
|
|
129
|
+
prop: "id",
|
|
130
|
+
label: t("bi.dashboard.title"),
|
|
131
|
+
component: "select",
|
|
132
|
+
options: dashboards.value.map(item => ({ label: item.name, value: item.id })),
|
|
133
|
+
props: { filterable: true }
|
|
134
|
+
}
|
|
135
|
+
]);
|
|
136
|
+
/** 按同一过滤条件查询当前可见卡片,丢弃迟到请求。 */
|
|
137
|
+
async function loadCards() {
|
|
138
|
+
const current = ++request;
|
|
139
|
+
loading.value = true;
|
|
140
|
+
try {
|
|
141
|
+
const data = await defBiCardService.PageBiCard({
|
|
142
|
+
name: name.value,
|
|
143
|
+
source_code: sourceCode.value,
|
|
144
|
+
category: category.value,
|
|
145
|
+
search_source_codes: sources.value.filter(item => item.name.includes(name.value)).map(item => item.code),
|
|
146
|
+
page_num: page.value,
|
|
147
|
+
page_size: 100
|
|
148
|
+
});
|
|
149
|
+
if (current !== request) return;
|
|
150
|
+
cards.value = data.cards;
|
|
151
|
+
total.value = data.total;
|
|
152
|
+
counts.value = data.counts!;
|
|
153
|
+
loaded = true;
|
|
154
|
+
} catch (cause) {
|
|
155
|
+
ElMessage.error(cause instanceof Error ? cause.message : t("bi.query.error"));
|
|
156
|
+
} finally {
|
|
157
|
+
if (current === request) loading.value = false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** 搜索或切换分类时从第一页开始。 */
|
|
161
|
+
function search() {
|
|
162
|
+
page.value = 1;
|
|
163
|
+
void loadCards();
|
|
164
|
+
}
|
|
165
|
+
/** 预加载查询目录和收藏后刷新列表。 */
|
|
166
|
+
async function reload() {
|
|
167
|
+
try {
|
|
168
|
+
const [dictionary, config] = await Promise.all([defBiQueryService.ListBiSources({}), defBiCardService.GetBiWorkbench({})]);
|
|
169
|
+
sources.value = dictionary.sources;
|
|
170
|
+
workbench.value = config;
|
|
171
|
+
await loadCards();
|
|
172
|
+
} catch (cause) {
|
|
173
|
+
ElMessage.error(cause instanceof Error ? cause.message : t("bi.query.error"));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** 打开已有卡片的编辑表单。 */
|
|
177
|
+
function openEditor(card: BiCard) {
|
|
178
|
+
editingCard.value = card;
|
|
179
|
+
editorOpen.value = true;
|
|
180
|
+
}
|
|
181
|
+
/** 保存收藏后只更新星标选中状态,保持当前列表和图表实例。 */
|
|
182
|
+
async function toggleFavorite(card: BiCard) {
|
|
183
|
+
try {
|
|
184
|
+
const latest = await defBiCardService.GetBiWorkbench({});
|
|
185
|
+
const ids = latest.favorite_ids.includes(card.id)
|
|
186
|
+
? latest.favorite_ids.filter(id => id !== card.id)
|
|
187
|
+
: [...latest.favorite_ids, card.id];
|
|
188
|
+
const saved = await defBiCardService.SaveBiWorkbench({ dashboard_ids: latest.dashboard_ids, favorite_ids: ids });
|
|
189
|
+
workbench.value.favorite_ids = saved.favorite_ids;
|
|
190
|
+
} catch (cause) {
|
|
191
|
+
ElMessage.error(cause instanceof Error ? cause.message : t("bi.card.save_failed"));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** 删除当前用户自己的卡片,确认时展示卡片名称。 */
|
|
195
|
+
async function remove(card: BiCard) {
|
|
196
|
+
try {
|
|
197
|
+
await ElMessageBox.confirm(t("bi.card.delete_confirm", { name: card.name }), t("common.action.delete"), { type: "warning" });
|
|
198
|
+
} catch {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
await defBiCardService.DeleteBiCard({ id: card.id });
|
|
203
|
+
await reload();
|
|
204
|
+
} catch (cause) {
|
|
205
|
+
ElMessage.error(cause instanceof Error ? cause.message : t("bi.card.delete_failed"));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** 选择当前用户可编辑的仪表盘草稿。 */
|
|
209
|
+
async function pickDashboard(card: BiCard) {
|
|
210
|
+
try {
|
|
211
|
+
const result: BiDashboard[] = [];
|
|
212
|
+
let current = 1;
|
|
213
|
+
while (true) {
|
|
214
|
+
const data = await defBiDashboardService.PageBiDashboard({ name: "", page_num: current, page_size: 100 });
|
|
215
|
+
result.push(...data.dashboards.filter(item => item.can_edit));
|
|
216
|
+
if (current * 100 >= data.total || !data.dashboards.length) break;
|
|
217
|
+
current++;
|
|
218
|
+
}
|
|
219
|
+
if (!result.length) {
|
|
220
|
+
ElMessage.info(t("bi.card.create_dashboard_first"));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
dashboards.value = result;
|
|
224
|
+
addingCard.value = card;
|
|
225
|
+
target.id = undefined;
|
|
226
|
+
dashboardOpen.value = true;
|
|
227
|
+
} catch (cause) {
|
|
228
|
+
ElMessage.error(cause instanceof Error ? cause.message : t("bi.query.error"));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/** 只追加卡片引用,发布之前不改变正式仪表盘。 */
|
|
232
|
+
async function addToDashboard() {
|
|
233
|
+
if (!target.id || !addingCard.value || saving.value) return;
|
|
234
|
+
saving.value = true;
|
|
235
|
+
try {
|
|
236
|
+
const item = await defBiDashboardService.GetBiDashboard({ id: target.id });
|
|
237
|
+
const layout = parseDashboardLayout(item.schema_json);
|
|
238
|
+
if (layout.cards.some(item => item.card_id === addingCard.value!.id)) {
|
|
239
|
+
ElMessage.info(t("bi.card.already_in_dashboard"));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
layout.cards = reflowDashboardCards([
|
|
243
|
+
...layout.cards,
|
|
244
|
+
{ id: `card_${addingCard.value.id}`, card_id: addingCard.value.id, w: 6, h: 4 }
|
|
245
|
+
]);
|
|
246
|
+
await defBiDashboardService.SaveBiDashboard({ dashboard: { ...item, schema_json: JSON.stringify(layout) } });
|
|
247
|
+
dashboardOpen.value = false;
|
|
248
|
+
ElMessage.success(t("bi.card.add_dashboard_success"));
|
|
249
|
+
} catch (cause) {
|
|
250
|
+
ElMessage.error(cause instanceof Error ? cause.message : t("bi.card.save_failed"));
|
|
251
|
+
} finally {
|
|
252
|
+
saving.value = false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
onMounted(() => void reload());
|
|
256
|
+
onActivated(() => {
|
|
257
|
+
if (loaded) void reload();
|
|
258
|
+
});
|
|
259
|
+
</script>
|
|
260
|
+
<style scoped>
|
|
261
|
+
.bi-card-library {
|
|
262
|
+
display: flex;
|
|
263
|
+
flex-direction: column;
|
|
264
|
+
gap: 16px;
|
|
265
|
+
min-width: 0;
|
|
266
|
+
}
|
|
267
|
+
.bi-card-library__count {
|
|
268
|
+
margin-left: 8px;
|
|
269
|
+
color: var(--el-text-color-secondary);
|
|
270
|
+
font-size: 12px;
|
|
271
|
+
}
|
|
272
|
+
.bi-card-library__filters {
|
|
273
|
+
display: flex;
|
|
274
|
+
align-items: center;
|
|
275
|
+
flex-wrap: wrap;
|
|
276
|
+
gap: 12px;
|
|
277
|
+
}
|
|
278
|
+
.bi-card-library__filters > .el-input {
|
|
279
|
+
width: 260px;
|
|
280
|
+
}
|
|
281
|
+
.bi-card-library__filters > .el-select {
|
|
282
|
+
width: 200px;
|
|
283
|
+
}
|
|
284
|
+
.bi-card-library__filters > span {
|
|
285
|
+
font-size: 12px;
|
|
286
|
+
color: var(--el-text-color-secondary);
|
|
287
|
+
}
|
|
288
|
+
.bi-card-library__grid {
|
|
289
|
+
display: grid;
|
|
290
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
291
|
+
gap: 16px;
|
|
292
|
+
}
|
|
293
|
+
.bi-card-library :deep(.el-pagination) {
|
|
294
|
+
justify-content: flex-end;
|
|
295
|
+
}
|
|
296
|
+
@media (max-width: 1100px) {
|
|
297
|
+
.bi-card-library__grid {
|
|
298
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
@media (max-width: 650px) {
|
|
302
|
+
.bi-card-library__grid {
|
|
303
|
+
grid-template-columns: 1fr;
|
|
304
|
+
}
|
|
305
|
+
.bi-card-library__filters > .el-input,
|
|
306
|
+
.bi-card-library__filters > .el-select {
|
|
307
|
+
width: 100%;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
</style>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="data.metricOnly" class="bi-card-metrics" :class="{ 'is-multiple': data.metrics.length > 1, 'is-compact': compact }">
|
|
3
|
+
<div v-for="metric in data.metrics" :key="metric.code" class="bi-card-metrics__item">
|
|
4
|
+
<span v-if="data.metrics.length > 1">{{ metric.name }}</span
|
|
5
|
+
><strong>{{ formatCardMetric(metric.value) }}</strong>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
<div v-else class="bi-card-plot">
|
|
9
|
+
<BiChart :chart="data.chart" :rows="data.rows" :theme="theme" :empty-text="t('common.message.no_data')" />
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { computed } from "vue";
|
|
15
|
+
import { t } from "@liujitcn/kratos-admin-core";
|
|
16
|
+
import type { BiCard } from "../rpc/bi/admin/v1/bi_card";
|
|
17
|
+
import type { BiQueryResult } from "../rpc/bi/admin/v1/bi_query";
|
|
18
|
+
import { buildCardChart, formatCardMetric } from "../card-schema";
|
|
19
|
+
import { useBiTheme } from "../env";
|
|
20
|
+
import BiChart from "./BiChart.vue";
|
|
21
|
+
|
|
22
|
+
const props = defineProps<{ card: BiCard; result: BiQueryResult; compact?: boolean }>();
|
|
23
|
+
const theme = useBiTheme();
|
|
24
|
+
const data = computed(() => {
|
|
25
|
+
const value = buildCardChart(props.card, props.result);
|
|
26
|
+
if (props.compact) {
|
|
27
|
+
value.chart.style = { ...value.chart.style, showLegend: false, showLabel: false, dataZoom: false };
|
|
28
|
+
value.chart.options = { tooltip: { show: false }, visualMap: { show: false } };
|
|
29
|
+
if (["bar", "line", "area", "scatter", "heatmap"].includes(props.card.chart_type)) {
|
|
30
|
+
value.chart.options.grid = { left: 4, right: 4, top: 6, bottom: 4 };
|
|
31
|
+
value.chart.options.xAxis = { type: props.card.chart_type === "scatter" ? "value" : "category", show: false };
|
|
32
|
+
value.chart.options.yAxis = { type: props.card.chart_type === "heatmap" ? "category" : "value", show: false };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
});
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<style scoped>
|
|
40
|
+
.bi-card-metrics {
|
|
41
|
+
display: grid;
|
|
42
|
+
height: 100%;
|
|
43
|
+
align-content: center;
|
|
44
|
+
gap: 16px;
|
|
45
|
+
overflow: auto;
|
|
46
|
+
color: var(--el-text-color-primary);
|
|
47
|
+
}
|
|
48
|
+
.bi-card-metrics__item {
|
|
49
|
+
display: flex;
|
|
50
|
+
min-width: 0;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
gap: 4px;
|
|
53
|
+
text-align: center;
|
|
54
|
+
overflow-wrap: anywhere;
|
|
55
|
+
}
|
|
56
|
+
.bi-card-metrics__item strong {
|
|
57
|
+
font-size: 32px;
|
|
58
|
+
font-weight: 500;
|
|
59
|
+
font-variant-numeric: tabular-nums;
|
|
60
|
+
}
|
|
61
|
+
.bi-card-metrics__item span,
|
|
62
|
+
.bi-card-gauge__details span {
|
|
63
|
+
color: var(--el-text-color-secondary);
|
|
64
|
+
font-size: 12px;
|
|
65
|
+
}
|
|
66
|
+
.bi-card-metrics:not(.is-multiple) {
|
|
67
|
+
align-content: start;
|
|
68
|
+
padding-top: 4px;
|
|
69
|
+
}
|
|
70
|
+
.bi-card-metrics:not(.is-multiple) .bi-card-metrics__item {
|
|
71
|
+
text-align: left;
|
|
72
|
+
}
|
|
73
|
+
.bi-card-metrics.is-multiple {
|
|
74
|
+
gap: 12px;
|
|
75
|
+
grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
|
|
76
|
+
}
|
|
77
|
+
.bi-card-metrics.is-multiple strong {
|
|
78
|
+
font-size: 22px;
|
|
79
|
+
}
|
|
80
|
+
.bi-card-metrics.is-compact .bi-card-metrics__item strong {
|
|
81
|
+
font-size: 20px;
|
|
82
|
+
}
|
|
83
|
+
.bi-card-plot {
|
|
84
|
+
display: flex;
|
|
85
|
+
height: 100%;
|
|
86
|
+
min-height: 0;
|
|
87
|
+
flex-direction: column;
|
|
88
|
+
gap: 4px;
|
|
89
|
+
}
|
|
90
|
+
.bi-card-plot :deep(.bi-chart) {
|
|
91
|
+
flex: 1;
|
|
92
|
+
min-height: 0;
|
|
93
|
+
height: auto;
|
|
94
|
+
}
|
|
95
|
+
</style>
|