feat: Add carousel settings to overview page (#11853)

This commit is contained in:
ssongliu
2026-02-10 08:45:06 +00:00
committed by GitHub
parent e0f2910710
commit 31d2af554e
14 changed files with 155 additions and 24 deletions
+1
View File
@@ -30,6 +30,7 @@ func Init() {
migrations.UpdateXpackHideMenuSort,
migrations.AdjustXpackNode,
migrations.UpdateAiAgentsMenu,
migrations.AddDashboardCarouselSetting,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+30 -7
View File
@@ -591,13 +591,36 @@ var AddUpgradeBackupCopies = &gormigrate.Migration{
}
var AddScriptSync = &gormigrate.Migration{
ID: "20250916-add-script-sync",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "ScriptSync", Value: constant.StatusEnable}).Error; err != nil {
return err
}
return nil
},
ID: "20250916-add-script-sync",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "ScriptSync", Value: constant.StatusEnable}).Error; err != nil {
return err
}
return nil
},
}
var AddDashboardCarouselSetting = &gormigrate.Migration{
ID: "20260210-add-dashboard-carousel-setting",
Migrate: func(tx *gorm.DB) error {
var addSettingsIfMissing = func(tx *gorm.DB, key, value string) error {
var setting model.Setting
if err := tx.Where("key = ?", key).First(&setting).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return tx.Create(&model.Setting{Key: key, Value: value}).Error
}
return err
}
return nil
}
if err := addSettingsIfMissing(tx, "DashboardMemoVisible", constant.StatusEnable); err != nil {
return err
}
if err := addSettingsIfMissing(tx, "DashboardSimpleNodeVisible", constant.StatusEnable); err != nil {
return err
}
return nil
},
}
var UpdateXpackHideMenuSort = &gormigrate.Migration{
+2
View File
@@ -34,6 +34,8 @@ export namespace Setting {
allowIPs: string;
bindDomain: string;
securityEntrance: string;
dashboardMemoVisible: string;
dashboardSimpleNodeVisible: string;
expirationDays: number;
expirationTime: string;
complexityVerification: string;
+2 -1
View File
@@ -472,7 +472,8 @@ const message = {
disk: 'Disk',
memo: 'Memo',
memoPlaceholder: 'Click to edit memo...',
memoPlaceholder: 'Double-click to edit memo content...',
carouselSetting: 'Carousel settings',
tooltipSensitiveInfo: 'Show/Hide sensitive info',
},
tabs: {
+2 -1
View File
@@ -474,7 +474,8 @@ const message = {
networkCard: 'Tarjeta de red',
disk: 'Disco',
memo: 'Memo',
memoPlaceholder: 'Haga clic para editar el memo...',
memoPlaceholder: 'Doble clic para editar el memo...',
carouselSetting: 'Configuración del carrusel',
tooltipSensitiveInfo: 'Mostrar/Ocultar información sensible',
},
tabs: {
+2 -1
View File
@@ -458,7 +458,8 @@ const message = {
networkCard: 'ネットワークカード',
disk: 'ディスク',
memo: 'メモ',
memoPlaceholder: 'クリックしてメモを編集...',
memoPlaceholder: 'ダブルクリックしてメモを編集...',
carouselSetting: 'カルーセル設定',
tooltipSensitiveInfo: '機密情報を表示/非表示',
},
tabs: {
+2 -1
View File
@@ -461,7 +461,8 @@ const message = {
networkCard: '네트워크 카드',
disk: '디스크',
memo: '메모',
memoPlaceholder: '클릭하여 메모를 편집...',
memoPlaceholder: '메모를 편집하려면 두 번 클릭...',
carouselSetting: '캐러셀 설정',
tooltipSensitiveInfo: '민감한 정보 표시/숨기기',
},
tabs: {
+2 -1
View File
@@ -467,7 +467,8 @@ const message = {
networkCard: 'Kad rangkaian',
disk: 'Disk',
memo: 'Catatan',
memoPlaceholder: 'Klik untuk mengedit catatan...',
memoPlaceholder: 'Dwiklik untuk mengedit memo...',
carouselSetting: 'Tetapan karusel',
tooltipSensitiveInfo: 'Tunjuk/Sembunyikan maklumat sensitif',
},
tabs: {
+2 -1
View File
@@ -465,7 +465,8 @@ const message = {
networkCard: 'Placa de rede',
disk: 'Disco',
memo: 'Memo',
memoPlaceholder: 'Clique para editar o memo...',
memoPlaceholder: 'Clique duas vezes para editar o memo...',
carouselSetting: 'Configuração do carrossel',
tooltipSensitiveInfo: 'Mostrar/Ocultar informações sensíveis',
},
tabs: {
+2 -1
View File
@@ -462,7 +462,8 @@ const message = {
networkCard: 'Интерфейс',
disk: 'Диск',
memo: 'Заметка',
memoPlaceholder: 'Нажмите, чтобы редактировать заметку...',
memoPlaceholder: 'Дважды щелкните, чтобы редактировать заметку...',
carouselSetting: 'Настройки карусели',
tooltipSensitiveInfo: 'Показать/скрыть конфиденциальную информацию',
},
tabs: {
+2 -1
View File
@@ -473,7 +473,8 @@ const message = {
networkCard: ' kartı',
disk: 'Disk',
memo: 'Not',
memoPlaceholder: 'Notu düzenlemek için tıklayın...',
memoPlaceholder: 'Notu düzenlemek için çift tıklayın...',
carouselSetting: 'Karusel ayarları',
tooltipSensitiveInfo: 'Hassas bilgileri göster/gizle',
},
tabs: {
+2 -1
View File
@@ -456,7 +456,8 @@ const message = {
networkCard: '網卡',
disk: '磁碟',
memo: '備忘錄',
memoPlaceholder: '點擊編輯備忘錄內容...',
memoPlaceholder: '雙擊可編輯備忘錄內容...',
carouselSetting: '輪播設定',
tooltipSensitiveInfo: '顯示/隱藏敏感資訊',
},
tabs: {
+2 -1
View File
@@ -458,7 +458,8 @@ const message = {
disk: '磁盘',
memo: '备忘录',
memoPlaceholder: '点击编辑备忘录内容...',
memoPlaceholder: '双击可编辑备忘录内容...',
carouselSetting: '轮播设置',
tooltipSensitiveInfo: '显示/隐藏敏感信息',
},
tabs: {
+102 -7
View File
@@ -166,6 +166,7 @@
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
<el-carousel
class="my-carousel"
:class="{ 'no-indicator': carouselItemCount <= 1 }"
:key="simpleNodes.length"
height="368px"
indicator-position=""
@@ -174,6 +175,49 @@
<el-carousel-item key="systemInfo">
<CardWithHeader :header="$t('home.systemInfo')">
<template #header-r>
<el-popover
popper-class="dashboard-carousel-popover"
placement="bottom"
:title="$t('home.carouselSetting')"
width="220"
trigger="click"
>
<div class="dashboard-carousel-setting">
<div class="setting-item mt-2">
<span>{{ $t('home.systemInfo') }}</span>
<div class="mr-4">-</div>
</div>
<div class="setting-item mt-2">
<span>{{ $t('home.memo') }}</span>
<el-switch
v-model="memoCarouselSetting"
active-value="Enable"
inactive-value="Disable"
@change="
(val) => updateDashboardCarouselSetting('DashboardMemoVisible', val)
"
/>
</div>
<div class="setting-item">
<span>{{ $t('setting.panel') }}</span>
<el-switch
v-model="simpleNodeCarouselSetting"
active-value="Enable"
inactive-value="Disable"
@change="
(val) =>
updateDashboardCarouselSetting(
'DashboardSimpleNodeVisible',
val,
)
"
/>
</div>
</div>
<template #reference>
<el-button class="h-button-setting" link icon="Setting" />
</template>
</el-popover>
<el-tooltip :content="$t('commons.button.refresh')" placement="top">
<el-button class="h-button-setting" @click="refreshDashboard" link icon="Refresh" />
</el-tooltip>
@@ -276,12 +320,9 @@
</template>
</CardWithHeader>
</el-carousel-item>
<el-carousel-item key="memoInfo">
<el-carousel-item key="memoInfo" v-if="showMemoCarousel">
<CardWithHeader :header="$t('home.memo')" class="memo-card">
<template #header-r>
<el-tooltip v-if="!memoEditing" :content="$t('commons.button.edit')" placement="top">
<el-button class="h-button-setting" @click="startMemoEdit" link icon="Edit" />
</el-tooltip>
<el-tooltip v-if="memoEditing" :content="$t('commons.button.save')" placement="top">
<el-button
class="h-button-setting"
@@ -307,7 +348,7 @@
show-word-limit
:placeholder="$t('home.memoPlaceholder')"
/>
<div v-else class="memo-content">
<div v-else class="memo-content" @dblclick="startMemoEdit">
<MarkDownEditor v-if="memoContent" :content="memoContent" />
<span v-else class="memo-placeholder">
{{ $t('home.memoPlaceholder') }}
@@ -380,7 +421,7 @@
</template>
<script lang="ts" setup>
import { onMounted, onBeforeUnmount, ref, reactive } from 'vue';
import { onMounted, onBeforeUnmount, ref, reactive, computed } from 'vue';
import SystemStatus from '@/views/home/status/index.vue';
import AppLauncher from '@/views/home/app/index.vue';
import VCharts from '@/components/v-charts/index.vue';
@@ -401,6 +442,7 @@ import {
loadUpgradeInfo,
getMemo,
updateMemo,
updateSetting,
} from '@/api/modules/setting';
import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
@@ -461,6 +503,16 @@ const memoContent = ref('');
const memoEditContent = ref('');
const memoEditing = ref(false);
const memoSaving = ref(false);
const memoCarouselSetting = ref();
const simpleNodeCarouselSetting = ref();
const showMemoCarousel = computed(() => memoCarouselSetting.value === 'Enable');
const carouselItemCount = computed(() => {
let count = 1;
if (showMemoCarousel.value) count += 1;
if (showSimpleNode()) count += 1;
return count;
});
const baseInfo = ref<Dashboard.BaseInfo>({
hostname: '',
@@ -655,7 +707,11 @@ const quickJump = (item: any) => {
};
const showSimpleNode = () => {
return globalStore.isMasterProductPro && simpleNodes.value?.length !== 0;
return (
simpleNodeCarouselSetting.value === 'Enable' &&
globalStore.isMasterProductPro &&
simpleNodes.value?.length !== 0
);
};
const toggleSensitiveInfo = () => {
@@ -783,6 +839,18 @@ const loadMemo = async () => {
}
};
const updateDashboardCarouselSetting = async (key: string, value: 'Enable' | 'Disable') => {
const target = key === 'DashboardMemoVisible' ? memoCarouselSetting : simpleNodeCarouselSetting;
const previous = value === 'Enable' ? 'Disable' : 'Enable';
try {
await updateSetting({ key, value });
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
} catch (error) {
target.value = previous;
MsgError(error.message);
}
};
const startMemoEdit = () => {
memoEditContent.value = memoContent.value;
memoEditing.value = true;
@@ -862,6 +930,8 @@ const loadSafeStatus = async () => {
}
const res = await getSettingInfo();
isSafety.value = res.data.securityEntrance;
memoCarouselSetting.value = res.data.dashboardMemoVisible;
simpleNodeCarouselSetting.value = res.data.dashboardSimpleNodeVisible;
setDashboardCache('safeStatus', isSafety.value, DASHBOARD_CACHE_TTL.safeStatus);
};
@@ -1027,6 +1097,12 @@ onBeforeUnmount(() => {
}
.my-carousel {
&.no-indicator {
.el-carousel__indicators {
display: none;
}
}
.el-carousel__button {
margin-bottom: -4px;
background-color: var(--el-text-color-regular);
@@ -1130,4 +1206,23 @@ onBeforeUnmount(() => {
color: var(--el-text-color-placeholder);
font-style: italic;
}
.dashboard-carousel-setting {
display: flex;
flex-direction: column;
.setting-item {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.dashboard-carousel-popover {
.el-popover__title {
padding-bottom: 10px;
margin-bottom: 8px;
border-bottom: 1px solid var(--el-border-color);
}
}
</style>