perf: split light dashboard runtime
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardConfig } from './store/dashboardDesignStore';
|
import type { DashboardConfig } from './store/dashboardRuntimeStore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 仪表盘渲染器
|
* 仪表盘渲染器
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DashboardConfig } from './store/dashboardRuntimeStore';
|
||||||
|
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import { ElEmpty } from 'element-plus';
|
||||||
|
|
||||||
|
import LightWidgetRenderer from './components/LightWidgetRenderer.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
config: DashboardConfig | string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const dashboardConfig = ref<DashboardConfig | null>(null);
|
||||||
|
|
||||||
|
function parseConfig() {
|
||||||
|
if (!props.config) {
|
||||||
|
dashboardConfig.value = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof props.config === 'string') {
|
||||||
|
try {
|
||||||
|
dashboardConfig.value = JSON.parse(props.config);
|
||||||
|
} catch {
|
||||||
|
console.error('Invalid light dashboard config JSON');
|
||||||
|
dashboardConfig.value = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dashboardConfig.value = props.config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(parseConfig);
|
||||||
|
watch(() => props.config, parseConfig);
|
||||||
|
|
||||||
|
const layout = computed(() => {
|
||||||
|
if (!dashboardConfig.value) return [];
|
||||||
|
return dashboardConfig.value.widgets.map((w) => ({
|
||||||
|
i: w.i,
|
||||||
|
x: w.x,
|
||||||
|
y: w.y,
|
||||||
|
w: w.w,
|
||||||
|
h: w.h,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const gridStyle = computed(() => {
|
||||||
|
if (!dashboardConfig.value) return {};
|
||||||
|
const [gapX, gapY] = dashboardConfig.value.margin;
|
||||||
|
const style: Record<string, string> = {
|
||||||
|
display: 'grid',
|
||||||
|
gap: `${gapY}px ${gapX}px`,
|
||||||
|
gridAutoRows: `${dashboardConfig.value.rowHeight}px`,
|
||||||
|
gridTemplateColumns: `repeat(${dashboardConfig.value.columns}, minmax(0, 1fr))`,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!dashboardConfig.value.showOuterMargin) {
|
||||||
|
style.marginLeft = `-${gapX}px`;
|
||||||
|
style.marginRight = `-${gapX}px`;
|
||||||
|
style.marginTop = `-${gapY}px`;
|
||||||
|
style.width = `calc(100% + ${gapX * 2}px)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return style;
|
||||||
|
});
|
||||||
|
|
||||||
|
function getWidget(i: string) {
|
||||||
|
if (!dashboardConfig.value) return null;
|
||||||
|
return dashboardConfig.value.widgets.find((w) => w.i === i);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAnimationDelay(i: string) {
|
||||||
|
if (!dashboardConfig.value) return 0;
|
||||||
|
const index = dashboardConfig.value.widgets.findIndex((w) => w.i === i);
|
||||||
|
return index * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItemStyle(item: { h: number; w: number; x: number; y: number }) {
|
||||||
|
return {
|
||||||
|
gridColumn: `${item.x + 1} / span ${item.w}`,
|
||||||
|
gridRow: `${item.y + 1} / span ${item.h}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="dashboard-renderer h-full"
|
||||||
|
:style="
|
||||||
|
dashboardConfig?.backgroundColor?.includes('gradient')
|
||||||
|
? { background: dashboardConfig.backgroundColor }
|
||||||
|
: { backgroundColor: dashboardConfig?.backgroundColor || '' }
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div v-if="dashboardConfig && layout.length > 0">
|
||||||
|
<div :style="gridStyle">
|
||||||
|
<div
|
||||||
|
v-for="item in layout"
|
||||||
|
:key="item.i"
|
||||||
|
class="dashboard-widget"
|
||||||
|
:style="getItemStyle(item)"
|
||||||
|
>
|
||||||
|
<LightWidgetRenderer
|
||||||
|
v-if="getWidget(item.i)"
|
||||||
|
:widget="getWidget(item.i)!"
|
||||||
|
:is-design-mode="false"
|
||||||
|
:animation-delay="getAnimationDelay(item.i)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ElEmpty
|
||||||
|
v-else
|
||||||
|
:description="$t('dashboard-design.noConfigTip')"
|
||||||
|
class="py-20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dashboard-renderer {
|
||||||
|
background-color: var(--el-bg-color-page);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-widget {
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--el-bg-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 1px 3px rgb(0 0 0 / 10%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type {
|
||||||
|
DashboardWidget,
|
||||||
|
WidgetMaterial,
|
||||||
|
} from '../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
|
import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import { defaultWidgetStyle } from '../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
animationDelay?: number;
|
||||||
|
isDesignMode?: boolean;
|
||||||
|
material?: WidgetMaterial;
|
||||||
|
widget: DashboardWidget;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const isEntered = ref(false);
|
||||||
|
const isUpdating = ref(false);
|
||||||
|
|
||||||
|
const widgetComponents: Record<
|
||||||
|
string,
|
||||||
|
ReturnType<typeof defineAsyncComponent>
|
||||||
|
> = {
|
||||||
|
'announcement-list': defineAsyncComponent(
|
||||||
|
() => import('./widgets/AnnouncementList.vue'),
|
||||||
|
),
|
||||||
|
'approval-center': defineAsyncComponent(
|
||||||
|
() => import('./widgets/ApprovalCenter.vue'),
|
||||||
|
),
|
||||||
|
'my-apps': defineAsyncComponent(() => import('./widgets/MyApps.vue')),
|
||||||
|
'notice-list': defineAsyncComponent(() => import('./widgets/NoticeList.vue')),
|
||||||
|
'quick-links': defineAsyncComponent(() => import('./widgets/QuickLinks.vue')),
|
||||||
|
'server-monitor': defineAsyncComponent(
|
||||||
|
() => import('./widgets/ServerMonitor.vue'),
|
||||||
|
),
|
||||||
|
weather: defineAsyncComponent(() => import('./widgets/WeatherWidget.vue')),
|
||||||
|
'welcome-card': defineAsyncComponent(
|
||||||
|
() => import('./widgets/WelcomeCard.vue'),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentComponent = computed(() => widgetComponents[props.widget.type]);
|
||||||
|
|
||||||
|
const mergedWidget = computed(() => props.widget);
|
||||||
|
|
||||||
|
const widgetStyle = computed(() => {
|
||||||
|
const style = { ...defaultWidgetStyle, ...props.widget.style };
|
||||||
|
const css: Record<string, string> = {};
|
||||||
|
const bgValue = style.backgroundColor || 'var(--el-bg-color)';
|
||||||
|
|
||||||
|
if (bgValue.includes('gradient')) {
|
||||||
|
css.background = bgValue;
|
||||||
|
} else {
|
||||||
|
css.backgroundColor = bgValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.borderWidth && style.borderWidth > 0) {
|
||||||
|
css.borderWidth = `${style.borderWidth}px`;
|
||||||
|
css.borderStyle = style.borderStyle || 'solid';
|
||||||
|
css.borderColor = style.borderColor || 'var(--el-border-color)';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.borderRadius !== undefined) {
|
||||||
|
css.borderRadius = `${style.borderRadius}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.shadowEnabled) {
|
||||||
|
const color = style.shadowColor || 'rgba(0, 0, 0, 0.1)';
|
||||||
|
const blur = style.shadowBlur || 4;
|
||||||
|
const x = style.shadowOffsetX || 0;
|
||||||
|
const y = style.shadowOffsetY || 1;
|
||||||
|
css.boxShadow = `${x}px ${y}px ${blur}px ${color}`;
|
||||||
|
} else {
|
||||||
|
css.boxShadow = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
return css;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
isEntered.value = true;
|
||||||
|
}, props.animationDelay || 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
const animationClass = computed(() => ({
|
||||||
|
'widget-enter': true,
|
||||||
|
'widget-entered': isEntered.value,
|
||||||
|
'widget-updating': isUpdating.value,
|
||||||
|
}));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="widget-renderer relative h-full w-full overflow-hidden"
|
||||||
|
:class="animationClass"
|
||||||
|
:style="widgetStyle"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="currentComponent"
|
||||||
|
v-if="currentComponent"
|
||||||
|
:widget="mergedWidget"
|
||||||
|
:is-design-mode="isDesignMode"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex h-full w-full items-center justify-center text-gray-400"
|
||||||
|
>
|
||||||
|
{{ $t('dashboard-design.unknownWidget') }}: {{ widget.type }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@keyframes pulse-update {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 0 2px var(--el-color-primary-light-5);
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-enter {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px) scale(0.95);
|
||||||
|
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-entered {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-updating {
|
||||||
|
animation: pulse-update 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import type {
|
import type {
|
||||||
DashboardWidget,
|
DashboardWidget,
|
||||||
WidgetMaterial,
|
WidgetMaterial,
|
||||||
} from '../store/dashboardDesignStore';
|
} from '../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
@@ -18,8 +18,8 @@ import { $t } from '@vben/locales';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
defaultWidgetStyle,
|
defaultWidgetStyle,
|
||||||
useDashboardDesignStore,
|
useDashboardRuntimeStore,
|
||||||
} from '../store/dashboardDesignStore';
|
} from '../store/dashboardRuntimeStore';
|
||||||
import { createRefreshTimer, fetchWidgetData } from '../utils/dataFetcher';
|
import { createRefreshTimer, fetchWidgetData } from '../utils/dataFetcher';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -106,15 +106,13 @@ const widgetComponents: Record<
|
|||||||
'filter-select': defineAsyncComponent(
|
'filter-select': defineAsyncComponent(
|
||||||
() => import('./widgets/FilterSelect.vue'),
|
() => import('./widgets/FilterSelect.vue'),
|
||||||
),
|
),
|
||||||
'filter-date': defineAsyncComponent(
|
'filter-date': defineAsyncComponent(() => import('./widgets/FilterDate.vue')),
|
||||||
() => import('./widgets/FilterDate.vue'),
|
|
||||||
),
|
|
||||||
'filter-date-range': defineAsyncComponent(
|
'filter-date-range': defineAsyncComponent(
|
||||||
() => import('./widgets/FilterDateRange.vue'),
|
() => import('./widgets/FilterDateRange.vue'),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const store = useDashboardDesignStore();
|
const store = useDashboardRuntimeStore();
|
||||||
|
|
||||||
const currentComponent = computed(() => {
|
const currentComponent = computed(() => {
|
||||||
return widgetComponents[props.widget.type];
|
return widgetComponents[props.widget.type];
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '../../store/dashboardDesignStore';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import type { UserAnnouncement } from '#/api/core/announcement';
|
import type { UserAnnouncement } from '#/api/core/announcement';
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '#/components/dashboard-design';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '../../store/dashboardDesignStore';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
@@ -7,14 +7,14 @@ import { $t } from '@vben/locales';
|
|||||||
|
|
||||||
import { ElDatePicker } from 'element-plus';
|
import { ElDatePicker } from 'element-plus';
|
||||||
|
|
||||||
import { useDashboardDesignStore } from '../../store/dashboardDesignStore';
|
import { useDashboardRuntimeStore } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isDesignMode?: boolean;
|
isDesignMode?: boolean;
|
||||||
widget: DashboardWidget;
|
widget: DashboardWidget;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const store = useDashboardDesignStore();
|
const store = useDashboardRuntimeStore();
|
||||||
const dateValue = ref<string>(props.widget.props.defaultValue || '');
|
const dateValue = ref<string>(props.widget.props.defaultValue || '');
|
||||||
|
|
||||||
const label = computed(() => props.widget.props.label || '');
|
const label = computed(() => props.widget.props.label || '');
|
||||||
|
|||||||
+6
-12
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '../../store/dashboardDesignStore';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
@@ -7,14 +7,14 @@ import { $t } from '@vben/locales';
|
|||||||
|
|
||||||
import { ElDatePicker } from 'element-plus';
|
import { ElDatePicker } from 'element-plus';
|
||||||
|
|
||||||
import { useDashboardDesignStore } from '../../store/dashboardDesignStore';
|
import { useDashboardRuntimeStore } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isDesignMode?: boolean;
|
isDesignMode?: boolean;
|
||||||
widget: DashboardWidget;
|
widget: DashboardWidget;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const store = useDashboardDesignStore();
|
const store = useDashboardRuntimeStore();
|
||||||
const dateRange = ref<[string, string] | []>(
|
const dateRange = ref<[string, string] | []>(
|
||||||
props.widget.props.defaultValue || [],
|
props.widget.props.defaultValue || [],
|
||||||
);
|
);
|
||||||
@@ -30,12 +30,8 @@ const endPlaceholder = computed(
|
|||||||
props.widget.props.endPlaceholder ||
|
props.widget.props.endPlaceholder ||
|
||||||
$t('dashboard-design.material.defaultProps.filterEndDate'),
|
$t('dashboard-design.material.defaultProps.filterEndDate'),
|
||||||
);
|
);
|
||||||
const startParamKey = computed(
|
const startParamKey = computed(() => props.widget.props.startParamKey || '');
|
||||||
() => props.widget.props.startParamKey || '',
|
const endParamKey = computed(() => props.widget.props.endParamKey || '');
|
||||||
);
|
|
||||||
const endParamKey = computed(
|
|
||||||
() => props.widget.props.endParamKey || '',
|
|
||||||
);
|
|
||||||
const dateFormat = computed(
|
const dateFormat = computed(
|
||||||
() => props.widget.props.dateFormat || 'YYYY-MM-DD',
|
() => props.widget.props.dateFormat || 'YYYY-MM-DD',
|
||||||
);
|
);
|
||||||
@@ -108,9 +104,7 @@ watch(
|
|||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasParamKey = computed(
|
const hasParamKey = computed(() => startParamKey.value || endParamKey.value);
|
||||||
() => startParamKey.value || endParamKey.value,
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '../../store/dashboardDesignStore';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
@@ -8,14 +8,14 @@ import { $t } from '@vben/locales';
|
|||||||
|
|
||||||
import { ElInput } from 'element-plus';
|
import { ElInput } from 'element-plus';
|
||||||
|
|
||||||
import { useDashboardDesignStore } from '../../store/dashboardDesignStore';
|
import { useDashboardRuntimeStore } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isDesignMode?: boolean;
|
isDesignMode?: boolean;
|
||||||
widget: DashboardWidget;
|
widget: DashboardWidget;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const store = useDashboardDesignStore();
|
const store = useDashboardRuntimeStore();
|
||||||
const inputValue = ref(props.widget.props.defaultValue || '');
|
const inputValue = ref(props.widget.props.defaultValue || '');
|
||||||
|
|
||||||
const label = computed(() => props.widget.props.label || '');
|
const label = computed(() => props.widget.props.label || '');
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '../../store/dashboardDesignStore';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { computed, onMounted, ref, watch } from 'vue';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
@@ -9,14 +9,14 @@ import { ElOption, ElSelect } from 'element-plus';
|
|||||||
|
|
||||||
import { requestClient } from '#/api/request';
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
import { useDashboardDesignStore } from '../../store/dashboardDesignStore';
|
import { useDashboardRuntimeStore } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isDesignMode?: boolean;
|
isDesignMode?: boolean;
|
||||||
widget: DashboardWidget;
|
widget: DashboardWidget;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const store = useDashboardDesignStore();
|
const store = useDashboardRuntimeStore();
|
||||||
const selectValue = ref<any>(props.widget.props.defaultValue || '');
|
const selectValue = ref<any>(props.widget.props.defaultValue || '');
|
||||||
const dynamicOptions = ref<{ label: string; value: any }[]>([]);
|
const dynamicOptions = ref<{ label: string; value: any }[]>([]);
|
||||||
const loadingOptions = ref(false);
|
const loadingOptions = ref(false);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '#/components/dashboard-design';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
import type { ApplicationListItem } from '#/api/core/application';
|
import type { ApplicationListItem } from '#/api/core/application';
|
||||||
|
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
@@ -107,20 +107,23 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
<!-- 应用网格 -->
|
<!-- 应用网格 -->
|
||||||
<ElScrollbar class="flex-1">
|
<ElScrollbar class="flex-1">
|
||||||
<div
|
<div v-if="displayApps.length > 0" class="flex flex-wrap gap-4">
|
||||||
v-if="displayApps.length > 0"
|
|
||||||
class="flex flex-wrap gap-4"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
v-for="app in displayApps"
|
v-for="app in displayApps"
|
||||||
:key="app.id"
|
:key="app.id"
|
||||||
class="flex cursor-pointer flex-col items-center gap-2 transition-transform hover:scale-105 m-4"
|
class="m-4 flex cursor-pointer flex-col items-center gap-2 transition-transform hover:scale-105"
|
||||||
style="width: 72px"
|
style="width: 72px"
|
||||||
@click="handleClick(app)"
|
@click="handleClick(app)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex h-12 w-12 items-center justify-center rounded-xl"
|
class="flex h-12 w-12 items-center justify-center rounded-xl"
|
||||||
style="background: linear-gradient(135deg, var(--el-color-primary-light-3), var(--el-color-primary))"
|
style="
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--el-color-primary-light-3),
|
||||||
|
var(--el-color-primary)
|
||||||
|
);
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<IconifyIcon
|
<IconifyIcon
|
||||||
v-if="app.icon"
|
v-if="app.icon"
|
||||||
@@ -129,9 +132,10 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
<AppWindow v-else class="h-6 w-6 text-white" />
|
<AppWindow v-else class="h-6 w-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span class="text-muted-foreground w-full truncate text-center text-xs">{{
|
<span
|
||||||
getAppName(app)
|
class="text-muted-foreground w-full truncate text-center text-xs"
|
||||||
}}</span>
|
>{{ getAppName(app) }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ElEmpty
|
<ElEmpty
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '../../store/dashboardDesignStore';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import type { Message } from '#/api/core/message';
|
import type { Message } from '#/api/core/message';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '#/components/dashboard-design';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import type {
|
|||||||
RealtimeStats,
|
RealtimeStats,
|
||||||
ServerMonitorResponse,
|
ServerMonitorResponse,
|
||||||
} from '#/api/core/server-monitor';
|
} from '#/api/core/server-monitor';
|
||||||
import type { DashboardWidget } from '#/components/dashboard-design';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '../../store/dashboardDesignStore';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '../../store/dashboardDesignStore';
|
import type { DashboardWidget } from '../../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export type DataSourceType = 'api' | 'dataSource' | 'static' | 'upload';
|
||||||
|
|
||||||
|
export interface FieldMapping {
|
||||||
|
source: string;
|
||||||
|
target: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParamBinding {
|
||||||
|
globalKey: string;
|
||||||
|
paramName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DataSourceConfig {
|
||||||
|
apiBody?: Record<string, any>;
|
||||||
|
apiHeaders?: Record<string, string>;
|
||||||
|
apiMethod?: 'GET' | 'POST';
|
||||||
|
apiParams?: Record<string, any>;
|
||||||
|
apiUrl?: string;
|
||||||
|
dataPath?: string;
|
||||||
|
dataSourceCode?: string;
|
||||||
|
fieldMappings?: FieldMapping[];
|
||||||
|
paramBindings?: ParamBinding[];
|
||||||
|
refreshEnabled?: boolean;
|
||||||
|
refreshInterval?: number;
|
||||||
|
type: DataSourceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WidgetStyle {
|
||||||
|
backgroundColor?: string;
|
||||||
|
backgroundImage?: string;
|
||||||
|
backgroundSize?: 'auto' | 'contain' | 'cover';
|
||||||
|
borderColor?: string;
|
||||||
|
borderRadius?: number;
|
||||||
|
borderStyle?: 'dashed' | 'dotted' | 'none' | 'solid';
|
||||||
|
borderWidth?: number;
|
||||||
|
padding?: number;
|
||||||
|
shadowBlur?: number;
|
||||||
|
shadowColor?: string;
|
||||||
|
shadowEnabled?: boolean;
|
||||||
|
shadowOffsetX?: number;
|
||||||
|
shadowOffsetY?: number;
|
||||||
|
titleAlign?: 'center' | 'left' | 'right';
|
||||||
|
titleColor?: string;
|
||||||
|
titleFontSize?: number;
|
||||||
|
titleFontWeight?: 'bold' | 'normal';
|
||||||
|
titleShow?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DashboardWidget {
|
||||||
|
dataSource?: DataSourceConfig;
|
||||||
|
h: number;
|
||||||
|
i: string;
|
||||||
|
id: string;
|
||||||
|
maxH?: number;
|
||||||
|
maxW?: number;
|
||||||
|
minH?: number;
|
||||||
|
minW?: number;
|
||||||
|
props: Record<string, any>;
|
||||||
|
style?: WidgetStyle;
|
||||||
|
title?: string;
|
||||||
|
type: string;
|
||||||
|
w: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DashboardConfig {
|
||||||
|
backgroundColor?: string;
|
||||||
|
columns: number;
|
||||||
|
id: string;
|
||||||
|
margin: [number, number];
|
||||||
|
name: string;
|
||||||
|
rowHeight: number;
|
||||||
|
showOuterMargin?: boolean;
|
||||||
|
widgets: DashboardWidget[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WidgetMaterial {
|
||||||
|
category: 'chart' | 'filter' | 'list' | 'widget';
|
||||||
|
defaultH: number;
|
||||||
|
defaultProps: Record<string, any>;
|
||||||
|
defaultW: number;
|
||||||
|
icon: string;
|
||||||
|
minH?: number;
|
||||||
|
minW?: number;
|
||||||
|
title: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultWidgetStyle: WidgetStyle = {
|
||||||
|
backgroundColor: '',
|
||||||
|
borderWidth: 0,
|
||||||
|
borderColor: '',
|
||||||
|
borderStyle: 'solid',
|
||||||
|
borderRadius: 8,
|
||||||
|
shadowEnabled: true,
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
shadowBlur: 4,
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowOffsetY: 1,
|
||||||
|
padding: 16,
|
||||||
|
titleShow: true,
|
||||||
|
titleFontSize: 14,
|
||||||
|
titleColor: '',
|
||||||
|
titleAlign: 'left',
|
||||||
|
titleFontWeight: 'normal',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDashboardRuntimeStore = defineStore('dashboard-runtime', () => {
|
||||||
|
const globalParams = ref<Record<string, any>>({});
|
||||||
|
const globalParamsVersion = ref(0);
|
||||||
|
|
||||||
|
const updateGlobalParam = (key: string, value: any) => {
|
||||||
|
globalParams.value = { ...globalParams.value, [key]: value };
|
||||||
|
globalParamsVersion.value++;
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeGlobalParam = (key: string) => {
|
||||||
|
const next = { ...globalParams.value };
|
||||||
|
delete next[key];
|
||||||
|
globalParams.value = next;
|
||||||
|
globalParamsVersion.value++;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearGlobalParams = () => {
|
||||||
|
globalParams.value = {};
|
||||||
|
globalParamsVersion.value++;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
clearGlobalParams,
|
||||||
|
globalParams,
|
||||||
|
globalParamsVersion,
|
||||||
|
removeGlobalParam,
|
||||||
|
updateGlobalParam,
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
DataSourceConfig,
|
DataSourceConfig,
|
||||||
FieldMapping,
|
FieldMapping,
|
||||||
} from '../store/dashboardDesignStore';
|
} from '../store/dashboardRuntimeStore';
|
||||||
|
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
@@ -87,7 +87,9 @@ export async function fetchWidgetData(
|
|||||||
try {
|
try {
|
||||||
const response = await requestClient.get(
|
const response = await requestClient.get(
|
||||||
`/api/core/data-source/execute/${dataSource.dataSourceCode}`,
|
`/api/core/data-source/execute/${dataSource.dataSourceCode}`,
|
||||||
{ params: params && Object.keys(params).length > 0 ? params : undefined },
|
{
|
||||||
|
params: params && Object.keys(params).length > 0 ? params : undefined,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// 后端返回格式是 {data: ...},需要提取 data 字段
|
// 后端返回格式是 {data: ...},需要提取 data 字段
|
||||||
|
|||||||
@@ -1,19 +1,69 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { defineAsyncComponent, h, onMounted, ref, watch } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
import { ElEmpty, ElMessage, ElScrollbar } from 'element-plus';
|
import { ElEmpty, ElMessage, ElScrollbar } from 'element-plus';
|
||||||
|
|
||||||
import { getPageByCodeApi } from '#/api/core/page-manager';
|
import { getPageByCodeApi } from '#/api/core/page-manager';
|
||||||
import DashboardRenderer from '#/components/dashboard-design/DashboardRenderer.vue';
|
|
||||||
|
|
||||||
defineOptions({ name: 'PageRender' });
|
defineOptions({ name: 'PageRender' });
|
||||||
|
|
||||||
|
const lightHomeWidgetTypes = new Set([
|
||||||
|
'announcement-list',
|
||||||
|
'approval-center',
|
||||||
|
'my-apps',
|
||||||
|
'notice-list',
|
||||||
|
'quick-links',
|
||||||
|
'server-monitor',
|
||||||
|
'weather',
|
||||||
|
'welcome-card',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const LightDashboardRenderer = defineAsyncComponent({
|
||||||
|
loader: () =>
|
||||||
|
import('#/components/dashboard-design/LightDashboardRenderer.vue'),
|
||||||
|
delay: 120,
|
||||||
|
loadingComponent: {
|
||||||
|
render: () =>
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
class:
|
||||||
|
'flex h-full min-h-[240px] items-center justify-center text-sm text-gray-500',
|
||||||
|
},
|
||||||
|
'正在加载控制中心...',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
errorComponent: {
|
||||||
|
render: () => h(ElEmpty, { description: '控制中心加载失败' }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const DashboardRenderer = defineAsyncComponent({
|
||||||
|
loader: () => import('#/components/dashboard-design/DashboardRenderer.vue'),
|
||||||
|
delay: 120,
|
||||||
|
loadingComponent: {
|
||||||
|
render: () =>
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
class:
|
||||||
|
'flex h-full min-h-[240px] items-center justify-center text-sm text-gray-500',
|
||||||
|
},
|
||||||
|
'正在加载页面组件...',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
errorComponent: {
|
||||||
|
render: () => h(ElEmpty, { description: '页面组件加载失败' }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const pageConfig = ref('');
|
const pageConfig = ref('');
|
||||||
const pageName = ref('');
|
const pageName = ref('');
|
||||||
const pageCode = ref('');
|
const pageCode = ref('');
|
||||||
|
const useLightRenderer = ref(false);
|
||||||
|
|
||||||
function getPageCode(): string {
|
function getPageCode(): string {
|
||||||
if (route.query.pageCode) {
|
if (route.query.pageCode) {
|
||||||
@@ -33,6 +83,19 @@ function getPageCode(): string {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canUseLightRenderer(code: string, config: any): boolean {
|
||||||
|
if (code !== 'main_home') return false;
|
||||||
|
const widgets = config?.widgets;
|
||||||
|
return (
|
||||||
|
Array.isArray(widgets) &&
|
||||||
|
widgets.every(
|
||||||
|
(widget) =>
|
||||||
|
lightHomeWidgetTypes.has(widget?.type) &&
|
||||||
|
(!widget?.dataSource || widget.dataSource.type === 'static'),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function loadPageData() {
|
async function loadPageData() {
|
||||||
const code = getPageCode();
|
const code = getPageCode();
|
||||||
if (!code) {
|
if (!code) {
|
||||||
@@ -45,12 +108,16 @@ async function loadPageData() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const page = await getPageByCodeApi(code);
|
const page = await getPageByCodeApi(code);
|
||||||
pageName.value = page.name;
|
const rawConfig =
|
||||||
pageConfig.value =
|
|
||||||
page.page_config && Object.keys(page.page_config).length > 0
|
page.page_config && Object.keys(page.page_config).length > 0
|
||||||
? JSON.stringify(page.page_config)
|
? page.page_config
|
||||||
: '';
|
: null;
|
||||||
|
|
||||||
|
pageName.value = page.name;
|
||||||
|
useLightRenderer.value = canUseLightRenderer(code, rawConfig);
|
||||||
|
pageConfig.value = rawConfig ? JSON.stringify(rawConfig) : '';
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
useLightRenderer.value = false;
|
||||||
ElMessage.error(error?.message || '加载页面失败');
|
ElMessage.error(error?.message || '加载页面失败');
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -72,7 +139,11 @@ watch(
|
|||||||
<template>
|
<template>
|
||||||
<ElScrollbar class="rounded-[8px] py-3">
|
<ElScrollbar class="rounded-[8px] py-3">
|
||||||
<div v-loading="loading" class="h-[calc(100vh-120px)] rounded-[8px] px-3">
|
<div v-loading="loading" class="h-[calc(100vh-120px)] rounded-[8px] px-3">
|
||||||
<DashboardRenderer v-if="pageConfig" :config="pageConfig" />
|
<LightDashboardRenderer
|
||||||
|
v-if="pageConfig && useLightRenderer"
|
||||||
|
:config="pageConfig"
|
||||||
|
/>
|
||||||
|
<DashboardRenderer v-else-if="pageConfig" :config="pageConfig" />
|
||||||
<ElEmpty v-else-if="!loading" description="暂无页面配置" />
|
<ElEmpty v-else-if="!loading" description="暂无页面配置" />
|
||||||
</div>
|
</div>
|
||||||
</ElScrollbar>
|
</ElScrollbar>
|
||||||
|
|||||||
@@ -11,7 +11,14 @@ import type { ChatMessage } from '#/components/ChatBox/index';
|
|||||||
* Agent 对话面板组件
|
* Agent 对话面板组件
|
||||||
* 可复用于 Agent 编辑页面和 Agent 对话页面
|
* 可复用于 Agent 编辑页面和 Agent 对话页面
|
||||||
*/
|
*/
|
||||||
import { computed, nextTick, onUnmounted, ref, watch } from 'vue';
|
import {
|
||||||
|
computed,
|
||||||
|
defineAsyncComponent,
|
||||||
|
nextTick,
|
||||||
|
onUnmounted,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
@@ -23,10 +30,16 @@ import {
|
|||||||
sendAgentMessageStream,
|
sendAgentMessageStream,
|
||||||
} from '#/api/ai-platform/ai-platform';
|
} from '#/api/ai-platform/ai-platform';
|
||||||
import { ChatBox } from '#/components/ChatBox/index';
|
import { ChatBox } from '#/components/ChatBox/index';
|
||||||
import { AppDesignPanel, DesignEditorPanel } from '#/components/form-editor';
|
|
||||||
|
|
||||||
import AiWorkingAnimation from '../../../../components/ai-loading/AiWorkingAnimation.vue';
|
import AiWorkingAnimation from '../../../../components/ai-loading/AiWorkingAnimation.vue';
|
||||||
|
|
||||||
|
const AppDesignPanel = defineAsyncComponent(
|
||||||
|
() => import('#/components/form-editor/AppDesignPanel.vue'),
|
||||||
|
);
|
||||||
|
const DesignEditorPanel = defineAsyncComponent(
|
||||||
|
() => import('#/components/form-editor/DesignEditorPanel.vue'),
|
||||||
|
);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
/** Agent 详情(可选,用于显示名称和欢迎配置) */
|
/** Agent 详情(可选,用于显示名称和欢迎配置) */
|
||||||
agent?: Agent | null;
|
agent?: Agent | null;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import type {
|
|||||||
SystemSummaryData,
|
SystemSummaryData,
|
||||||
} from '#/components/form-editor';
|
} from '#/components/form-editor';
|
||||||
|
|
||||||
import { computed, onUnmounted, ref, watch } from 'vue';
|
import { computed, defineAsyncComponent, onUnmounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { Settings2, X } from '@vben/icons';
|
import { Settings2, X } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
@@ -30,15 +30,27 @@ import {
|
|||||||
runWorkflowStreamApi,
|
runWorkflowStreamApi,
|
||||||
} from '#/api/ai-platform/ai-platform';
|
} from '#/api/ai-platform/ai-platform';
|
||||||
import { ChatBox } from '#/components/ChatBox/index';
|
import { ChatBox } from '#/components/ChatBox/index';
|
||||||
import {
|
const AppDesignPanel = defineAsyncComponent(
|
||||||
AppDesignPanel,
|
() => import('#/components/form-editor/AppDesignPanel.vue'),
|
||||||
AppSettingsPanel,
|
);
|
||||||
DashboardBasicInfoConfirmPanel,
|
const AppSettingsPanel = defineAsyncComponent(
|
||||||
DashboardDesignConfirmPanel,
|
() => import('#/components/form-editor/AppSettingsPanel.vue'),
|
||||||
DashboardPublishConfirmPanel,
|
);
|
||||||
DesignEditorPanel,
|
const DashboardBasicInfoConfirmPanel = defineAsyncComponent(
|
||||||
SystemSummaryConfirmPanel,
|
() => import('#/components/form-editor/DashboardBasicInfoConfirmPanel.vue'),
|
||||||
} from '#/components/form-editor';
|
);
|
||||||
|
const DashboardDesignConfirmPanel = defineAsyncComponent(
|
||||||
|
() => import('#/components/form-editor/DashboardDesignConfirmPanel.vue'),
|
||||||
|
);
|
||||||
|
const DashboardPublishConfirmPanel = defineAsyncComponent(
|
||||||
|
() => import('#/components/form-editor/DashboardPublishConfirmPanel.vue'),
|
||||||
|
);
|
||||||
|
const DesignEditorPanel = defineAsyncComponent(
|
||||||
|
() => import('#/components/form-editor/DesignEditorPanel.vue'),
|
||||||
|
);
|
||||||
|
const SystemSummaryConfirmPanel = defineAsyncComponent(
|
||||||
|
() => import('#/components/form-editor/SystemSummaryConfirmPanel.vue'),
|
||||||
|
);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
nodes: any[];
|
nodes: any[];
|
||||||
@@ -1250,6 +1262,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 设计编辑面板 -->
|
<!-- 设计编辑面板 -->
|
||||||
<DesignEditorPanel
|
<DesignEditorPanel
|
||||||
|
v-if="showDesignPanel"
|
||||||
:visible="showDesignPanel"
|
:visible="showDesignPanel"
|
||||||
:design="currentDesign"
|
:design="currentDesign"
|
||||||
@update:visible="showDesignPanel = $event"
|
@update:visible="showDesignPanel = $event"
|
||||||
@@ -1259,6 +1272,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 应用设计面板 -->
|
<!-- 应用设计面板 -->
|
||||||
<AppDesignPanel
|
<AppDesignPanel
|
||||||
|
v-if="showAppDesignPanel"
|
||||||
:visible="showAppDesignPanel"
|
:visible="showAppDesignPanel"
|
||||||
:design="currentAppDesign"
|
:design="currentAppDesign"
|
||||||
@update:visible="showAppDesignPanel = $event"
|
@update:visible="showAppDesignPanel = $event"
|
||||||
@@ -1268,6 +1282,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 应用设置面板 -->
|
<!-- 应用设置面板 -->
|
||||||
<AppSettingsPanel
|
<AppSettingsPanel
|
||||||
|
v-if="showAppSettingsPanel"
|
||||||
:visible="showAppSettingsPanel"
|
:visible="showAppSettingsPanel"
|
||||||
:settings="currentAppSettings"
|
:settings="currentAppSettings"
|
||||||
@update:visible="showAppSettingsPanel = $event"
|
@update:visible="showAppSettingsPanel = $event"
|
||||||
@@ -1277,6 +1292,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 仪表盘基础信息面板 -->
|
<!-- 仪表盘基础信息面板 -->
|
||||||
<DashboardBasicInfoConfirmPanel
|
<DashboardBasicInfoConfirmPanel
|
||||||
|
v-if="showDashboardBasicInfoPanel"
|
||||||
:visible="showDashboardBasicInfoPanel"
|
:visible="showDashboardBasicInfoPanel"
|
||||||
:basic-info="currentDashboardBasicInfo"
|
:basic-info="currentDashboardBasicInfo"
|
||||||
@update:visible="showDashboardBasicInfoPanel = $event"
|
@update:visible="showDashboardBasicInfoPanel = $event"
|
||||||
@@ -1286,6 +1302,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 仪表盘设计面板 -->
|
<!-- 仪表盘设计面板 -->
|
||||||
<DashboardDesignConfirmPanel
|
<DashboardDesignConfirmPanel
|
||||||
|
v-if="showDashboardDesignPanel"
|
||||||
:visible="showDashboardDesignPanel"
|
:visible="showDashboardDesignPanel"
|
||||||
:design="currentDashboardDesign"
|
:design="currentDashboardDesign"
|
||||||
@update:visible="showDashboardDesignPanel = $event"
|
@update:visible="showDashboardDesignPanel = $event"
|
||||||
@@ -1295,6 +1312,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 仪表盘发布面板 -->
|
<!-- 仪表盘发布面板 -->
|
||||||
<DashboardPublishConfirmPanel
|
<DashboardPublishConfirmPanel
|
||||||
|
v-if="showDashboardPublishPanel"
|
||||||
:visible="showDashboardPublishPanel"
|
:visible="showDashboardPublishPanel"
|
||||||
:publish-data="currentDashboardPublish"
|
:publish-data="currentDashboardPublish"
|
||||||
@update:visible="showDashboardPublishPanel = $event"
|
@update:visible="showDashboardPublishPanel = $event"
|
||||||
@@ -1304,6 +1322,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 系统总结面板 -->
|
<!-- 系统总结面板 -->
|
||||||
<SystemSummaryConfirmPanel
|
<SystemSummaryConfirmPanel
|
||||||
|
v-if="showSystemSummaryPanel"
|
||||||
:visible="showSystemSummaryPanel"
|
:visible="showSystemSummaryPanel"
|
||||||
:data="currentSystemSummary"
|
:data="currentSystemSummary"
|
||||||
@update:visible="showSystemSummaryPanel = $event"
|
@update:visible="showSystemSummaryPanel = $event"
|
||||||
|
|||||||
Reference in New Issue
Block a user