feat: Optimize quick navigation for dashboard (#10238)

Refs #5167
This commit is contained in:
ssongliu 2025-09-02 17:58:10 +08:00 committed by GitHub
parent 81fcc61ec9
commit 53f75086ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 394 additions and 73 deletions

View file

@ -83,6 +83,38 @@ func (b *BaseApi) UpdateAppLauncher(c *gin.Context) {
helper.Success(c)
}
// @Tags Dashboard
// @Summary Load quick jump options
// @Success 200 {Array} dto.QuickJump
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/quick/option [post]
func (b *BaseApi) LoadQuickOption(c *gin.Context) {
helper.SuccessWithData(c, dashboardService.LoadQuickOptions())
}
// @Tags Dashboard
// @Summary Update quick jump
// @Accept json
// @Param request body dto.ChangeQuicks true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/quick/change [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"切换快速跳转","formatEN":"change quick jump"}
func (b *BaseApi) UpdateQuickJump(c *gin.Context) {
var req dto.ChangeQuicks
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := dashboardService.ChangeQuick(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Dashboard
// @Summary Load dashboard base info
// @Accept json

View file

@ -3,11 +3,6 @@ package dto
import "time"
type DashboardBase struct {
WebsiteNumber int `json:"websiteNumber"`
DatabaseNumber int `json:"databaseNumber"`
CronjobNumber int `json:"cronjobNumber"`
AppInstalledNumber int `json:"appInstalledNumber"`
Hostname string `json:"hostname"`
OS string `json:"os"`
Platform string `json:"platform"`
@ -23,9 +18,24 @@ type DashboardBase struct {
CPULogicalCores int `json:"cpuLogicalCores"`
CPUModelName string `json:"cpuModelName"`
QuickJumps []QuickJump `json:"quickJump"`
CurrentInfo DashboardCurrent `json:"currentInfo"`
}
type ChangeQuicks struct {
Quicks []QuickJump `json:"quicks"`
}
type QuickJump struct {
ID uint `json:"id"`
Name string `json:"name"`
Title string `json:"title"`
Detail string `json:"detail"`
Recommend int `json:"recommend"`
IsShow bool `json:"isShow"`
Router string `json:"router"`
}
type OsInfo struct {
OS string `json:"os"`
Platform string `json:"platform"`

View file

@ -4,3 +4,13 @@ type AppLauncher struct {
BaseModel
Key string `json:"key"`
}
type QuickJump struct {
BaseModel
Name string `json:"name"`
Title string `json:"title"`
Detail string `json:"detail"`
Recommend int `json:"recommend"`
IsShow bool `json:"isShow"`
Router string `json:"router"`
}

View file

@ -13,6 +13,10 @@ type ILauncherRepo interface {
Create(launcher *model.AppLauncher) error
Save(launcher *model.AppLauncher) error
Delete(opts ...DBOption) error
GetQuickJump(opts ...DBOption) (model.QuickJump, error)
ListQuickJump(withAll bool) []model.QuickJump
UpdateQuicks(quicks []model.QuickJump) error
}
func NewILauncherRepo() ILauncherRepo {
@ -57,3 +61,45 @@ func (u *LauncherRepo) Delete(opts ...DBOption) error {
}
return db.Delete(&model.AppLauncher{}).Error
}
func (u *LauncherRepo) GetQuickJump(opts ...DBOption) (model.QuickJump, error) {
var launcher model.QuickJump
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&launcher).Error
return launcher, err
}
func (u *LauncherRepo) ListQuickJump(withAll bool) []model.QuickJump {
var quicks []model.QuickJump
if withAll {
_ = global.DB.Find(&quicks).Error
} else {
_ = global.DB.Where("is_show = ?", true).Find(&quicks).Error
}
if !withAll && len(quicks) == 0 {
return []model.QuickJump{
{Name: "Website", Title: "menu.website", Recommend: 10, IsShow: true, Router: "/websites"},
{Name: "Database", Title: "menu.database", Recommend: 30, IsShow: true, Router: "/databases"},
{Name: "Cronjob", Title: "menu.cronjob", Recommend: 50, IsShow: true, Router: "/cronjobs"},
{Name: "AppInstalled", Title: "home.appInstalled", Recommend: 70, IsShow: true, Router: "/apps/installed"},
}
}
return quicks
}
func (u *LauncherRepo) UpdateQuicks(quicks []model.QuickJump) error {
tx := global.DB.Begin()
for _, item := range quicks {
if err := tx.Model(&model.QuickJump{}).Where("id = ?", item.ID).Updates(map[string]interface{}{
"is_show": item.IsShow,
"detail": item.Detail,
}).Error; err != nil {
tx.Rollback()
return err
}
}
tx.Commit()
return nil
}

View file

@ -14,6 +14,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
@ -38,6 +39,9 @@ type IDashboardService interface {
LoadCurrentInfoForNode() *dto.NodeCurrent
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
LoadQuickOptions() []dto.QuickJump
ChangeQuick(req dto.ChangeQuicks) error
LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error)
ChangeShow(req dto.SettingUpdate) error
ListLauncherOption(filter string) ([]dto.LauncherOption, error)
@ -140,7 +144,7 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
baseInfo.KernelVersion = hostInfo.KernelVersion
ss, _ := json.Marshal(hostInfo)
baseInfo.VirtualizationSystem = string(ss)
baseInfo.IpV4Addr = GetOutboundIP()
baseInfo.IpV4Addr = loadOutboundIP()
httpProxy := os.Getenv("http_proxy")
if httpProxy == "" {
httpProxy = os.Getenv("HTTP_PROXY")
@ -150,30 +154,7 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
}
baseInfo.SystemProxy = "noProxy"
appInstall, err := appInstallRepo.ListBy(context.Background())
if err != nil {
return nil, err
}
baseInfo.AppInstalledNumber = len(appInstall)
postgresqlDbs, err := postgresqlRepo.List()
if err != nil {
return nil, err
}
mysqlDbs, err := mysqlRepo.List()
if err != nil {
return nil, err
}
baseInfo.DatabaseNumber = len(mysqlDbs) + len(postgresqlDbs)
website, err := websiteRepo.GetBy()
if err != nil {
return nil, err
}
baseInfo.WebsiteNumber = len(website)
cronjobs, err := cronjobRepo.List()
if err != nil {
return nil, err
}
baseInfo.CronjobNumber = len(cronjobs)
loadQuickJump(&baseInfo)
cpuInfo, err := cpu.Info()
if err == nil {
@ -347,6 +328,39 @@ func (u *DashboardService) ChangeShow(req dto.SettingUpdate) error {
return nil
}
func (u *DashboardService) LoadQuickOptions() []dto.QuickJump {
quicks := launcherRepo.ListQuickJump(true)
var list []dto.QuickJump
for _, quick := range quicks {
var item dto.QuickJump
_ = copier.Copy(&item, &quick)
list = append(list, item)
}
return list
}
func (u *DashboardService) ChangeQuick(req dto.ChangeQuicks) error {
showCount := 0
var quicks []model.QuickJump
for _, item := range req.Quicks {
var quick model.QuickJump
if item.IsShow {
showCount++
}
if err := copier.Copy(&quick, &item); err != nil {
return err
}
quicks = append(quicks, quick)
}
if showCount == 0 {
return buserr.New("ErrMinQuickJump")
}
if showCount > 4 {
return buserr.New("ErrMaxQuickJump")
}
return launcherRepo.UpdateQuicks(quicks)
}
func (u *DashboardService) ListLauncherOption(filter string) ([]dto.LauncherOption, error) {
showList, _ := launcherRepo.ListName()
var data []dto.LauncherOption
@ -545,7 +559,7 @@ func loadXpuInfo() []dto.XPUInfo {
return data
}
func GetOutboundIP() string {
func loadOutboundIP() string {
conn, err := network.Dial("udp", "8.8.8.8:80")
if err != nil {
@ -556,3 +570,30 @@ func GetOutboundIP() string {
localAddr := conn.LocalAddr().(*network.UDPAddr)
return localAddr.IP.String()
}
func loadQuickJump(base *dto.DashboardBase) {
quicks := launcherRepo.ListQuickJump(false)
for i := 0; i < len(quicks); i++ {
switch quicks[i].Name {
case "Website":
website, _ := websiteRepo.GetBy()
quicks[i].Detail = fmt.Sprintf("%d", len(website))
case "Database":
postgresqlDbs, _ := postgresqlRepo.List()
mysqlDbs, _ := mysqlRepo.List()
quicks[i].Detail = fmt.Sprintf("%d", len(mysqlDbs)+len(postgresqlDbs))
case "Cronjob":
cronjobs, _ := cronjobRepo.List()
quicks[i].Detail = fmt.Sprintf("%d", len(cronjobs))
case "AppInstalled":
appInstall, _ := appInstallRepo.ListBy(context.Background())
quicks[i].Detail = fmt.Sprintf("%d", len(appInstall))
}
var item dto.QuickJump
_ = copier.Copy(&item, quicks[i])
base.QuickJumps = append(base.QuickJumps, item)
}
sort.Slice(quicks, func(i, j int) bool {
return quicks[i].Recommend < quicks[j].Recommend
})
}

View file

@ -15,6 +15,9 @@ ErrApiConfigIPInvalid: 'The IP used to call the API interface is not in the whit
ErrApiConfigDisable: 'This interface prohibits the use of API interface calls: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'API interface timestamp error: {{ .detail }}'
ErrMinQuickJump: "Please set at least one quick jump entry!"
ErrMaxQuickJump: "You can set up to four quick jump entries!"
#common
ErrUsernameIsExist: 'Username already exists'
ErrNameIsExist: 'Name already exists'

View file

@ -15,6 +15,9 @@ ErrApiConfigIPInvalid: 'API インターフェースの呼び出しに使用さ
ErrApiConfigDisable: 'このインターフェースは、API インターフェース呼び出しの使用を禁止しています: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'API インターフェースのタイムスタンプ エラー: {{ .detail }}'
ErrMinQuickJump: "少なくとも1つのクイックジャンプエントリを設定してください"
ErrMaxQuickJump: "最大4つのクイックジャンプエントリを設定できます"
#common
ErrUsernameIsExist: 'ユーザー名は既に存在します'
ErrNameIsExist: '名前は既に存在します'

View file

@ -15,6 +15,9 @@ ErrApiConfigIPInvalid: 'API 인터페이스를 호출하는 데 사용된 IP가
ErrApiConfigDisable: '이 인터페이스는 API 인터페이스 호출 사용을 금지합니다: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'API 인터페이스 타임스탬프 오류: {{ .detail }}'
ErrMinQuickJump: "최소 하나의 빠른 점프 항목을 설정해 주세요!"
ErrMaxQuickJump: "최대 네 개의 빠른 점프 항목을 설정할 수 있습니다!"
#흔한
ErrUsernameIsExist: '사용자 이름이 이미 존재합니다'
ErrNameIsExist: '이름이 이미 존재합니다'

View file

@ -18,6 +18,9 @@ StartPushSSLToNode: "Mula menolak sijil ke nod"
PushSSLToNodeFailed: "Gagal menolak sijil ke nod: {{ .err }}"
PushSSLToNodeSuccess: "Berjaya menolak sijil ke nod"
ErrMinQuickJump: "Sila tetapkan sekurang-kurangnya satu entri lompat pantas!"
ErrMaxQuickJump: "Anda boleh menetapkan sehingga empat entri lompat pantas!"
#biasa
ErrUsernameIsExist: 'Nama pengguna sudah wujud'
ErrNameIsExist: 'Nama sudah wujud'

View file

@ -18,6 +18,9 @@ StartPushSSLToNode: "Iniciando o envio do certificado para o nó"
PushSSLToNodeFailed: "Falha ao enviar o certificado para o nó: {{ .err }}"
PushSSLToNodeSuccess: "Certificado enviado com sucesso para o nó"
ErrMinQuickJump: "Defina pelo menos uma entrada de salto rápido!"
ErrMaxQuickJump: "Você pode definir até quatro entradas de salto rápido!"
#comum
ErrUsernameIsExist: 'Nome de usuário já existe'
ErrNameIsExist: 'Nome já existe'

View file

@ -18,6 +18,9 @@ StartPushSSLToNode: "Начало отправки сертификата на
PushSSLToNodeFailed: "Не удалось отправить сертификат на узел: {{ .err }}"
PushSSLToNodeSuccess: "Сертификат успешно отправлен на узел"
ErrMinQuickJump: "Пожалуйста, установите хотя бы одну запись быстрого перехода!"
ErrMaxQuickJump: "Можно установить до четырех записей быстрого перехода!"
#общий
ErrUsernameIsExist: 'Имя пользователя уже существует'
ErrNameIsExist: 'Имя уже существует'

View file

@ -18,6 +18,9 @@ StartPushSSLToNode: "Sertifika düğüme gönderilmeye başlandı"
PushSSLToNodeFailed: "Sertifika düğüme gönderilemedi: {{ .err }}"
PushSSLToNodeSuccess: "Sertifika düğüme başarıyla gönderildi"
ErrMinQuickJump: "Lütfen en az bir hızlı atlama girişi ayarlayın!"
ErrMaxQuickJump: "En fazla dört hızlı atlama girişi ayarlayabilirsiniz!"
#common
ErrUsernameIsExist: 'Kullanıcı adı zaten mevcut'
ErrNameIsExist: 'İsim zaten mevcut'

View file

@ -15,6 +15,9 @@ ErrApiConfigIPInvalid: '呼叫 API 介面 IP 不在白名單: {{ .detail }}'
ErrApiConfigDisable: '此介面禁止使用 API 介面呼叫: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'API 介面時間戳記錯誤: {{ .detail }}'
ErrMinQuickJump: "請至少設定一個快速跳轉入口!"
ErrMaxQuickJump: "最多可設定四個快速跳轉入口!"
#common
ErrUsernameIsExist: '使用者名稱已存在'
ErrNameIsExist: '名稱已存在'

View file

@ -15,6 +15,9 @@ ErrApiConfigIPInvalid: "调用 API 接口 IP 不在白名单: {{ .detail }}"
ErrApiConfigDisable: "此接口禁止使用 API 接口调用: {{ .detail }}"
ErrApiConfigKeyTimeInvalid: "API 接口时间戳错误: {{ .detail }}"
ErrMinQuickJump: "请至少设置一个快速跳转入口!"
ErrMaxQuickJump: "最多可设置四个快速跳转入口!"
#common
ErrUsernameIsExist: "用户名已存在"
ErrNameIsExist: "名称已存在"

View file

@ -37,6 +37,7 @@ func InitAgentDB() {
migrations.InitCronjobGroup,
migrations.AddColumnToAlert,
migrations.UpdateWebsiteSSL,
migrations.AddQuickJump,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View file

@ -471,3 +471,28 @@ var UpdateWebsiteSSL = &gormigrate.Migration{
return nil
},
}
var AddQuickJump = &gormigrate.Migration{
ID: "20250901-add-quick-jump",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.QuickJump{}); err != nil {
return err
}
if err := tx.Create(&model.QuickJump{Name: "Website", Title: "menu.website", Recommend: 10, IsShow: true, Router: "/websites"}).Error; err != nil {
return err
}
if err := tx.Create(&model.QuickJump{Name: "Database", Title: "home.database", Recommend: 30, IsShow: true, Router: "/databases"}).Error; err != nil {
return err
}
if err := tx.Create(&model.QuickJump{Name: "Cronjob", Title: "menu.cronjob", Recommend: 50, IsShow: true, Router: "/cronjobs"}).Error; err != nil {
return err
}
if err := tx.Create(&model.QuickJump{Name: "AppInstalled", Title: "home.appInstalled", Recommend: 70, IsShow: true, Router: "/apps/installed"}).Error; err != nil {
return err
}
if err := tx.Create(&model.QuickJump{Name: "File", Detail: "/", Title: "home.quickDir", Recommend: 90, IsShow: false, Router: "/hosts/files"}).Error; err != nil {
return err
}
return nil
},
}

View file

@ -12,6 +12,8 @@ func (s *DashboardRouter) InitRouter(Router *gin.RouterGroup) {
baseApi := v2.ApiGroupApp.BaseApi
{
cmdRouter.GET("/base/os", baseApi.LoadDashboardOsInfo)
cmdRouter.GET("/quick/option", baseApi.LoadQuickOption)
cmdRouter.POST("/quick/change", baseApi.UpdateQuickJump)
cmdRouter.GET("/app/launcher", baseApi.LoadAppLauncher)
cmdRouter.POST("/app/launcher/show", baseApi.UpdateAppLauncher)
cmdRouter.POST("/app/launcher/option", baseApi.LoadAppLauncherOption)

View file

@ -8,6 +8,15 @@ export namespace Dashboard {
diskSize: number;
}
export interface QuickJump {
id: number;
name: string;
title: string;
detail: string;
recommend: number;
isShow: boolean ;
router: string;
}
export interface AppLauncher {
key: string;
icon: string;
@ -37,11 +46,6 @@ export namespace Dashboard {
httpsPort: string;
}
export interface BaseInfo {
websiteNumber: number;
databaseNumber: number;
cronjobNumber: number;
appInstalledNumber: number;
hostname: string;
os: string;
platform: string;
@ -58,6 +62,7 @@ export namespace Dashboard {
cpuModelName: string;
currentInfo: CurrentInfo;
quickJump: Array<QuickJump>;
}
export interface CurrentInfo {
uptime: number;

View file

@ -5,6 +5,12 @@ export const loadOsInfo = () => {
return http.get<Dashboard.OsInfo>(`/dashboard/base/os`);
};
export const loadQuickOption = () => {
return http.get<Array<Dashboard.QuickJump>>(`/dashboard/quick/option`);
};
export const changeQuick = (quicks: Array<Dashboard.QuickJump>) => {
return http.post(`/dashboard/quick/change`, { quicks: quicks });
};
export const loadAppLauncher = () => {
return http.get<Array<Dashboard.AppLauncher>>(`/dashboard/app/launcher`);
};

View file

@ -386,6 +386,8 @@ const message = {
home: {
recommend: 'recommend',
dir: 'dir',
quickDir: 'Quick Dir',
database: 'Database - All',
restart_1panel: 'Restart panel',
restart_system: 'Restart server',
operationSuccess: 'Operation succeeded, rebooting, please refresh the browser manually later!',

View file

@ -373,6 +373,10 @@ const message = {
msgCenter: 'タスクセンター',
},
home: {
recommend: 'おすすめ',
dir: 'ディレクトリ',
quickDir: 'クイックディレクトリ',
database: 'データベース - すべて',
restart_1panel: 'パネルを再起動します',
restart_system: 'サーバーを再起動します',
operationSuccess: '操作が成功し再起動します後で手動でブラウザを更新してください',

View file

@ -375,6 +375,10 @@ const message = {
msgCenter: '작업 센터',
},
home: {
recommend: '추천',
dir: '디렉토리',
quickDir: '빠른 디렉토리',
database: '데이터베이스 - 전체',
restart_1panel: '패널 재시작',
restart_system: '서버 재시작',
operationSuccess:

View file

@ -381,6 +381,10 @@ const message = {
msgCenter: 'Pusat Tugas',
},
home: {
recommend: 'cadangan',
dir: 'direktori',
quickDir: 'Direktori Pantas',
database: 'Pangkalan Data - Semua',
restart_1panel: 'Mulakan semula panel',
restart_system: 'Mulakan semula pelayan',
operationSuccess: 'Operasi berjaya, sedang memulakan semula, sila segarkan pelayar secara manual nanti!',

View file

@ -379,6 +379,10 @@ const message = {
msgCenter: 'Central de Tarefas',
},
home: {
recommend: 'recomendar',
dir: 'dir',
quickDir: 'Diretório Rápido',
database: 'Banco de Dados - Todos',
restart_1panel: 'Reiniciar painel',
restart_system: 'Reiniciar servidor',
operationSuccess: 'Operação bem-sucedida, reiniciando, por favor, atualize o navegador manualmente mais tarde!',

View file

@ -376,6 +376,10 @@ const message = {
msgCenter: 'Центр задач',
},
home: {
recommend: 'рекомендовать',
dir: 'каталог',
quickDir: 'Быстрый каталог',
database: 'База данных - Все',
restart_1panel: 'Перезапустить панель',
restart_system: 'Перезапустить сервер',
operationSuccess: 'Операция выполнена успешно, перезагрузка, пожалуйста, обновите браузер вручную позже!',

View file

@ -388,8 +388,10 @@ const message = {
msgCenter: 'Görev Merkezi',
},
home: {
recommend: 'önerilen',
recommend: 'tavsiye etmek',
dir: 'dizin',
quickDir: 'Hızlı Dizin',
database: 'Veritabanı - Tümü',
restart_1panel: 'Paneli yeniden başlat',
restart_system: 'Sunucuyu yeniden başlat',
operationSuccess: 'İşlem başarılı, yeniden başlatılıyor, lütfen tarayıcıyı daha sonra manuel olarak yenileyin!',

View file

@ -376,6 +376,8 @@ const message = {
home: {
recommend: '推薦',
dir: '目錄',
quickDir: '快捷目錄',
database: '資料庫 - 全部',
restart_1panel: '重啟面板',
restart_system: '重啟服務器',
operationSuccess: '操作成功正在重啟請稍後手動刷新瀏覽器',

View file

@ -374,6 +374,8 @@ const message = {
home: {
recommend: '推荐',
dir: '目录',
quickDir: '快捷目录',
database: '数据库 - 所有',
restart_1panel: '重启面板',
restart_system: '重启服务器',
operationSuccess: '操作成功正在重启请稍后手动刷新浏览器',

View file

@ -43,39 +43,25 @@
<el-row :gutter="7" class="card-interval">
<el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
<CardWithHeader :header="$t('menu.home')" height="166px">
<template #header-r>
<el-button class="h-button-setting" @click="quickJumpRef.acceptParams()" link icon="Setting" />
</template>
<template #body>
<div class="h-overview">
<el-row>
<el-col :span="6">
<span>{{ $t('menu.website', 2) }}</span>
<el-col :span="6" v-for="item in baseInfo.quickJump" :key="item.name">
<span>{{ $t(item.title, 2) }}</span>
<div class="count">
<span @click="jumpToPath(router, '/websites')">
{{ baseInfo?.websiteNumber }}
</span>
</div>
</el-col>
<el-col :span="6">
<span>{{ $t('menu.database', 2) }} - {{ $t('commons.table.all') }}</span>
<div class="count">
<span @click="jumpToPath(router, '/databases')">
{{ baseInfo?.databaseNumber }}
</span>
</div>
</el-col>
<el-col :span="6">
<span>{{ $t('menu.cronjob', 2) }}</span>
<div class="count">
<span @click="jumpToPath(router, '/cronjobs')">
{{ baseInfo?.cronjobNumber }}
</span>
</div>
</el-col>
<el-col :span="6">
<span>{{ $t('home.appInstalled') }}</span>
<div class="count">
<span @click="jumpToPath(router, '/apps/installed')">
{{ baseInfo?.appInstalledNumber }}
</span>
<el-tooltip
v-if="item.detail.length > 20"
:content="item.detail"
placement="bottom"
>
<span @click="quickJump(item)">
{{ item.detail.substring(0, 18) + '...' }}
</span>
</el-tooltip>
<span @click="quickJump(item)" v-else>{{ item.detail }}</span>
</div>
</el-col>
</el-row>
@ -250,6 +236,7 @@
</el-row>
<LicenseImport ref="licenseRef" />
<QuickJump @search="onLoadBaseInfo(false, 'all')" ref="quickJumpRef" />
</div>
</template>
@ -259,6 +246,7 @@ import Status from '@/views/home/status/index.vue';
import AppLauncher from '@/views/home/app/index.vue';
import VCharts from '@/components/v-charts/index.vue';
import LicenseImport from '@/components/license-import/index.vue';
import QuickJump from '@/views/home/quick/index.vue';
import CardWithHeader from '@/components/card-with-header/index.vue';
import i18n from '@/lang';
import { Dashboard } from '@/api/interface/dashboard';
@ -269,6 +257,7 @@ import { getIOOptions, getNetworkOptions } from '@/api/modules/host';
import { getSettingInfo, loadUpgradeInfo } from '@/api/modules/setting';
import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
import { routerToFileWithPath, routerToPath } from '@/utils/router';
const router = useRouter();
const globalStore = GlobalStore();
@ -295,6 +284,7 @@ const ioOptions = ref();
const netOptions = ref();
const licenseRef = ref();
const quickJumpRef = ref();
const { isProductPro } = storeToRefs(globalStore);
const searchInfo = reactive({
@ -303,11 +293,6 @@ const searchInfo = reactive({
});
const baseInfo = ref<Dashboard.BaseInfo>({
websiteNumber: 0,
databaseNumber: 0,
cronjobNumber: 0,
appInstalledNumber: 0,
hostname: '',
os: '',
platform: '',
@ -323,6 +308,8 @@ const baseInfo = ref<Dashboard.BaseInfo>({
cpuLogicalCores: 0,
cpuModelName: '',
currentInfo: null,
quickJump: [],
});
const currentInfo = ref<Dashboard.CurrentInfo>({
uptime: 0,
@ -429,6 +416,13 @@ const onLoadBaseInfo = async (isInit: boolean, range: string) => {
}
};
const quickJump = (item: any) => {
if (item.name === 'File') {
routerToFileWithPath(item.detail);
}
return routerToPath(item.router);
};
const onLoadCurrentInfo = async () => {
const res = await loadCurrentInfo(searchInfo.ioOption, searchInfo.netOption);
currentInfo.value.timeSinceUptime = res.data.timeSinceUptime;
@ -635,9 +629,8 @@ onBeforeUnmount(() => {
.count {
margin-top: 10px;
span {
font-size: 25px;
font-size: 18px;
color: $primary-color;
font-weight: 500;
line-height: 32px;
cursor: pointer;
}

View file

@ -0,0 +1,95 @@
<template>
<DrawerPro v-model="drawerVisible" :header="$t('menu.home')" @close="handleClose">
<div>
<ComplexTable :heightDiff="1" :data="quickOptions" :show-header="false" row-key="id">
<el-table-column prop="title" :label="$t('setting.menu')">
<template #default="{ row }">
{{ i18n.global.t(row.title) }}
</template>
</el-table-column>
<el-table-column prop="isShow" :label="$t('setting.ifShow')">
<template #default="{ row }">
<el-switch v-model="row.isShow" />
<div v-if="row.name === 'File' && row.isShow">
<el-input v-model="row.detail" class="w-full">
<template #prepend>
<el-button
v-if="row.name === 'File' && row.isShow"
icon="Folder"
@click="fileRef.acceptParams({ path: row.detail, isAll: true })"
/>
</template>
</el-input>
</div>
</template>
</el-table-column>
</ComplexTable>
</div>
<template #footer>
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button :disabled="loading" type="primary" @click="onChangeShow">
{{ $t('commons.button.confirm') }}
</el-button>
</template>
<FileList ref="fileRef" @choose="loadDir" />
</DrawerPro>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import FileList from '@/components/file-list/index.vue';
import { changeQuick, loadQuickOption } from '@/api/modules/dashboard';
const emit = defineEmits<{ (e: 'search'): void }>();
const drawerVisible = ref();
const loading = ref();
const acceptParams = (): void => {
search();
drawerVisible.value = true;
};
const quickOptions = ref([]);
const fileRef = ref();
const search = async () => {
loading.value = true;
await loadQuickOption()
.then((res) => {
loading.value = false;
quickOptions.value = res.data || [];
})
.catch(() => {
loading.value = false;
});
};
const onChangeShow = async () => {
loading.value = true;
await changeQuick(quickOptions.value)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
drawerVisible.value = false;
search();
})
.catch(() => {
loading.value = false;
});
};
const loadDir = async (path: string) => {
for (const item of quickOptions.value) {
if (item.name === 'File') {
item.detail = path;
}
}
};
const handleClose = () => {
drawerVisible.value = false;
emit('search');
};
defineExpose({
acceptParams,
});
</script>