From 315073406f718503c12b85a99894d9d70ad47d21 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Mon, 9 Jun 2025 23:08:34 +0800 Subject: [PATCH] feat: add stack widget for dashboard (#7525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /area ui /kind feautre /milestone 2.21.x #### What this PR does / why we need it: This PR adds a core Dashboard Widget called Stack Widget, which is used to stack a batch of components in the same location and supports manual or automatic switching. image image #### Which issue(s) this PR fixes: Fixes # #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note 为仪表盘添加堆叠小部件 ``` --- .../dashboard/components/ActionButton.vue | 8 + .../components/WidgetEditableItem.vue | 20 +- .../dashboard/components/WidgetHubModal.vue | 1 + .../modules/dashboard/widgets/index.ts | 13 ++ .../presets/core/stack/StackWidget.vue | 144 ++++++++++++ .../core/stack/StackWidgetConfigModal.vue | 205 ++++++++++++++++++ .../core/stack/components/IndexIndicator.vue | 53 +++++ .../stack/components/WidgetEditableItem.vue | 84 +++++++ .../core/stack/components/WidgetViewItem.vue | 31 +++ .../widgets/presets/core/stack/types.ts | 11 + ui/src/locales/_missing_translations_es.yaml | 16 ++ ui/src/locales/en.yaml | 16 ++ ui/src/locales/zh-CN.yaml | 16 ++ ui/src/locales/zh-TW.yaml | 16 ++ 14 files changed, 623 insertions(+), 11 deletions(-) create mode 100644 ui/console-src/modules/dashboard/components/ActionButton.vue create mode 100644 ui/console-src/modules/dashboard/widgets/presets/core/stack/StackWidget.vue create mode 100644 ui/console-src/modules/dashboard/widgets/presets/core/stack/StackWidgetConfigModal.vue create mode 100644 ui/console-src/modules/dashboard/widgets/presets/core/stack/components/IndexIndicator.vue create mode 100644 ui/console-src/modules/dashboard/widgets/presets/core/stack/components/WidgetEditableItem.vue create mode 100644 ui/console-src/modules/dashboard/widgets/presets/core/stack/components/WidgetViewItem.vue create mode 100644 ui/console-src/modules/dashboard/widgets/presets/core/stack/types.ts diff --git a/ui/console-src/modules/dashboard/components/ActionButton.vue b/ui/console-src/modules/dashboard/components/ActionButton.vue new file mode 100644 index 000000000..ee0b765fd --- /dev/null +++ b/ui/console-src/modules/dashboard/components/ActionButton.vue @@ -0,0 +1,8 @@ + + diff --git a/ui/console-src/modules/dashboard/components/WidgetEditableItem.vue b/ui/console-src/modules/dashboard/components/WidgetEditableItem.vue index cc9a25ab1..46eb4bc02 100644 --- a/ui/console-src/modules/dashboard/components/WidgetEditableItem.vue +++ b/ui/console-src/modules/dashboard/components/WidgetEditableItem.vue @@ -6,6 +6,7 @@ import type { DashboardWidgetDefinition, } from "@halo-dev/console-shared"; import { computed, inject, ref, type ComputedRef } from "vue"; +import ActionButton from "./ActionButton.vue"; import WidgetConfigFormModal from "./WidgetConfigFormModal.vue"; const props = defineProps<{ @@ -61,21 +62,18 @@ function handleSaveConfig(config: Record) { @update:config="handleSaveConfig" /> { :width="1380" :layer-closable="true" :title="$t('core.dashboard_designer.widgets_modal.title')" + mount-to-body @close="emit('close')" > +import WidgetCard from "@console/modules/dashboard/components/WidgetCard.vue"; +import { IconSettings, VButton } from "@halo-dev/components"; +import { nextTick, onMounted, onUnmounted, ref } from "vue"; +import StackWidgetConfigModal from "./StackWidgetConfigModal.vue"; +import IndexIndicator from "./components/IndexIndicator.vue"; +import WidgetViewItem from "./components/WidgetViewItem.vue"; +import type { StackWidgetConfig } from "./types"; + +const props = defineProps<{ + config: StackWidgetConfig; + editMode?: boolean; + previewMode?: boolean; +}>(); + +const emit = defineEmits<{ + (e: "update:config", config: StackWidgetConfig): void; +}>(); + +const configVisible = ref(false); + +const index = ref(0); + +function handleNavigate(direction: -1 | 1) { + const targetIndex = index.value + direction; + + if (targetIndex < 0) { + index.value = props.config.widgets.length - 1; + } else if (targetIndex >= props.config.widgets.length) { + index.value = 0; + } else { + index.value = targetIndex; + } +} + +// auto play +const autoPlayInterval = ref(null); + +function startAutoPlay() { + if (!props.config.auto_play || configVisible.value) { + return; + } + if (autoPlayInterval.value) { + clearInterval(autoPlayInterval.value); + } + autoPlayInterval.value = setInterval(() => { + handleNavigate(1); + }, props.config.auto_play_interval || 3000); +} + +function stopAutoPlay() { + if (autoPlayInterval.value) { + clearInterval(autoPlayInterval.value); + autoPlayInterval.value = null; + } +} + +onMounted(() => { + startAutoPlay(); +}); + +onUnmounted(() => { + stopAutoPlay(); +}); + +async function handleSave(config: StackWidgetConfig) { + emit("update:config", config); + configVisible.value = false; + + await nextTick(); + + if (config.auto_play) { + startAutoPlay(); + } else { + stopAutoPlay(); + } +} + + diff --git a/ui/console-src/modules/dashboard/widgets/presets/core/stack/StackWidgetConfigModal.vue b/ui/console-src/modules/dashboard/widgets/presets/core/stack/StackWidgetConfigModal.vue new file mode 100644 index 000000000..c4f8f8877 --- /dev/null +++ b/ui/console-src/modules/dashboard/widgets/presets/core/stack/StackWidgetConfigModal.vue @@ -0,0 +1,205 @@ + + diff --git a/ui/console-src/modules/dashboard/widgets/presets/core/stack/components/IndexIndicator.vue b/ui/console-src/modules/dashboard/widgets/presets/core/stack/components/IndexIndicator.vue new file mode 100644 index 000000000..0c3f005d3 --- /dev/null +++ b/ui/console-src/modules/dashboard/widgets/presets/core/stack/components/IndexIndicator.vue @@ -0,0 +1,53 @@ + + + diff --git a/ui/console-src/modules/dashboard/widgets/presets/core/stack/components/WidgetEditableItem.vue b/ui/console-src/modules/dashboard/widgets/presets/core/stack/components/WidgetEditableItem.vue new file mode 100644 index 000000000..201be8d33 --- /dev/null +++ b/ui/console-src/modules/dashboard/widgets/presets/core/stack/components/WidgetEditableItem.vue @@ -0,0 +1,84 @@ + + diff --git a/ui/console-src/modules/dashboard/widgets/presets/core/stack/components/WidgetViewItem.vue b/ui/console-src/modules/dashboard/widgets/presets/core/stack/components/WidgetViewItem.vue new file mode 100644 index 000000000..0b6d54368 --- /dev/null +++ b/ui/console-src/modules/dashboard/widgets/presets/core/stack/components/WidgetViewItem.vue @@ -0,0 +1,31 @@ + + + diff --git a/ui/console-src/modules/dashboard/widgets/presets/core/stack/types.ts b/ui/console-src/modules/dashboard/widgets/presets/core/stack/types.ts new file mode 100644 index 000000000..fdb74e7e5 --- /dev/null +++ b/ui/console-src/modules/dashboard/widgets/presets/core/stack/types.ts @@ -0,0 +1,11 @@ +export interface SimpleWidget { + i: string; + id: string; + config?: Record; +} + +export interface StackWidgetConfig { + auto_play?: boolean; + auto_play_interval?: number; + widgets: SimpleWidget[]; +} diff --git a/ui/src/locales/_missing_translations_es.yaml b/ui/src/locales/_missing_translations_es.yaml index ca355e77d..b7fe0c3a9 100644 --- a/ui/src/locales/_missing_translations_es.yaml +++ b/ui/src/locales/_missing_translations_es.yaml @@ -38,6 +38,22 @@ core: label: Enabled Items pending_comments: title: Pending Comments + stack: + title: Widget Stack + operations: + add_widget: + button: Add Widget + config_modal: + title: Widget Stack Config + fields: + auto_play: + label: Auto play + auto_play_interval: + label: Auto play interval + widgets: + label: Widgets + toast: + nest_warning: You cannot add a stack widget to a stack widget dashboard_designer: title: Edit Dashboard actions: diff --git a/ui/src/locales/en.yaml b/ui/src/locales/en.yaml index cb2ae898e..c736058b7 100644 --- a/ui/src/locales/en.yaml +++ b/ui/src/locales/en.yaml @@ -108,6 +108,22 @@ core: title: Pending Comments views_stats: title: Visits + stack: + title: Widget Stack + operations: + add_widget: + button: Add Widget + config_modal: + title: Widget Stack Config + fields: + auto_play: + label: Auto play + auto_play_interval: + label: Auto play interval + widgets: + label: Widgets + toast: + nest_warning: You cannot add a stack widget to a stack widget dashboard_designer: title: Edit Dashboard actions: diff --git a/ui/src/locales/zh-CN.yaml b/ui/src/locales/zh-CN.yaml index 6f8c5bc6e..8dd27e471 100644 --- a/ui/src/locales/zh-CN.yaml +++ b/ui/src/locales/zh-CN.yaml @@ -104,6 +104,22 @@ core: title: 浏览量 pending_comments: title: 新评论 + stack: + title: 堆叠部件 + operations: + add_widget: + button: 添加部件 + config_modal: + title: 堆叠部件配置 + fields: + auto_play: + label: 自动切换 + auto_play_interval: + label: 自动切换间隔 + widgets: + label: 部件 + toast: + nest_warning: 不能将堆叠部件添加到堆叠部件中 dashboard_designer: title: 编辑仪表盘 actions: diff --git a/ui/src/locales/zh-TW.yaml b/ui/src/locales/zh-TW.yaml index 1f8d9c768..583dd032e 100644 --- a/ui/src/locales/zh-TW.yaml +++ b/ui/src/locales/zh-TW.yaml @@ -104,6 +104,22 @@ core: title: 瀏覽量 pending_comments: title: 新評論 + stack: + title: 堆疊部件 + operations: + add_widget: + button: 添加部件 + config_modal: + title: 堆疊部件配置 + fields: + auto_play: + label: 自動切換 + auto_play_interval: + label: 自動切換間隔 + widgets: + label: 部件 + toast: + nest_warning: 不能將堆疊部件添加到堆疊部件中 dashboard_designer: title: 編輯儀表盤 actions: