mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-09-05 06:04:35 +08:00
parent
81fcc61ec9
commit
53f75086ec
30 changed files with 394 additions and 73 deletions
|
@ -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
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -15,6 +15,9 @@ ErrApiConfigIPInvalid: 'API インターフェースの呼び出しに使用さ
|
|||
ErrApiConfigDisable: 'このインターフェースは、API インターフェース呼び出しの使用を禁止しています: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'API インターフェースのタイムスタンプ エラー: {{ .detail }}'
|
||||
|
||||
ErrMinQuickJump: "少なくとも1つのクイックジャンプエントリを設定してください!"
|
||||
ErrMaxQuickJump: "最大4つのクイックジャンプエントリを設定できます!"
|
||||
|
||||
#common
|
||||
ErrUsernameIsExist: 'ユーザー名は既に存在します'
|
||||
ErrNameIsExist: '名前は既に存在します'
|
||||
|
|
|
@ -15,6 +15,9 @@ ErrApiConfigIPInvalid: 'API 인터페이스를 호출하는 데 사용된 IP가
|
|||
ErrApiConfigDisable: '이 인터페이스는 API 인터페이스 호출 사용을 금지합니다: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'API 인터페이스 타임스탬프 오류: {{ .detail }}'
|
||||
|
||||
ErrMinQuickJump: "최소 하나의 빠른 점프 항목을 설정해 주세요!"
|
||||
ErrMaxQuickJump: "최대 네 개의 빠른 점프 항목을 설정할 수 있습니다!"
|
||||
|
||||
#흔한
|
||||
ErrUsernameIsExist: '사용자 이름이 이미 존재합니다'
|
||||
ErrNameIsExist: '이름이 이미 존재합니다'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -18,6 +18,9 @@ StartPushSSLToNode: "Начало отправки сертификата на
|
|||
PushSSLToNodeFailed: "Не удалось отправить сертификат на узел: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "Сертификат успешно отправлен на узел"
|
||||
|
||||
ErrMinQuickJump: "Пожалуйста, установите хотя бы одну запись быстрого перехода!"
|
||||
ErrMaxQuickJump: "Можно установить до четырех записей быстрого перехода!"
|
||||
|
||||
#общий
|
||||
ErrUsernameIsExist: 'Имя пользователя уже существует'
|
||||
ErrNameIsExist: 'Имя уже существует'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -15,6 +15,9 @@ ErrApiConfigIPInvalid: '呼叫 API 介面 IP 不在白名單: {{ .detail }}'
|
|||
ErrApiConfigDisable: '此介面禁止使用 API 介面呼叫: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'API 介面時間戳記錯誤: {{ .detail }}'
|
||||
|
||||
ErrMinQuickJump: "請至少設定一個快速跳轉入口!"
|
||||
ErrMaxQuickJump: "最多可設定四個快速跳轉入口!"
|
||||
|
||||
#common
|
||||
ErrUsernameIsExist: '使用者名稱已存在'
|
||||
ErrNameIsExist: '名稱已存在'
|
||||
|
|
|
@ -15,6 +15,9 @@ ErrApiConfigIPInvalid: "调用 API 接口 IP 不在白名单: {{ .detail }}"
|
|||
ErrApiConfigDisable: "此接口禁止使用 API 接口调用: {{ .detail }}"
|
||||
ErrApiConfigKeyTimeInvalid: "API 接口时间戳错误: {{ .detail }}"
|
||||
|
||||
ErrMinQuickJump: "请至少设置一个快速跳转入口!"
|
||||
ErrMaxQuickJump: "最多可设置四个快速跳转入口!"
|
||||
|
||||
#common
|
||||
ErrUsernameIsExist: "用户名已存在"
|
||||
ErrNameIsExist: "名称已存在"
|
||||
|
|
|
@ -37,6 +37,7 @@ func InitAgentDB() {
|
|||
migrations.InitCronjobGroup,
|
||||
migrations.AddColumnToAlert,
|
||||
migrations.UpdateWebsiteSSL,
|
||||
migrations.AddQuickJump,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
|
|
@ -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
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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`);
|
||||
};
|
||||
|
|
|
@ -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!',
|
||||
|
|
|
@ -373,6 +373,10 @@ const message = {
|
|||
msgCenter: 'タスクセンター',
|
||||
},
|
||||
home: {
|
||||
recommend: 'おすすめ',
|
||||
dir: 'ディレクトリ',
|
||||
quickDir: 'クイックディレクトリ',
|
||||
database: 'データベース - すべて',
|
||||
restart_1panel: 'パネルを再起動します',
|
||||
restart_system: 'サーバーを再起動します',
|
||||
operationSuccess: '操作が成功し、再起動します。後で手動でブラウザを更新してください!',
|
||||
|
|
|
@ -375,6 +375,10 @@ const message = {
|
|||
msgCenter: '작업 센터',
|
||||
},
|
||||
home: {
|
||||
recommend: '추천',
|
||||
dir: '디렉토리',
|
||||
quickDir: '빠른 디렉토리',
|
||||
database: '데이터베이스 - 전체',
|
||||
restart_1panel: '패널 재시작',
|
||||
restart_system: '서버 재시작',
|
||||
operationSuccess:
|
||||
|
|
|
@ -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!',
|
||||
|
|
|
@ -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!',
|
||||
|
|
|
@ -376,6 +376,10 @@ const message = {
|
|||
msgCenter: 'Центр задач',
|
||||
},
|
||||
home: {
|
||||
recommend: 'рекомендовать',
|
||||
dir: 'каталог',
|
||||
quickDir: 'Быстрый каталог',
|
||||
database: 'База данных - Все',
|
||||
restart_1panel: 'Перезапустить панель',
|
||||
restart_system: 'Перезапустить сервер',
|
||||
operationSuccess: 'Операция выполнена успешно, перезагрузка, пожалуйста, обновите браузер вручную позже!',
|
||||
|
|
|
@ -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!',
|
||||
|
|
|
@ -376,6 +376,8 @@ const message = {
|
|||
home: {
|
||||
recommend: '推薦',
|
||||
dir: '目錄',
|
||||
quickDir: '快捷目錄',
|
||||
database: '資料庫 - 全部',
|
||||
restart_1panel: '重啟面板',
|
||||
restart_system: '重啟服務器',
|
||||
operationSuccess: '操作成功,正在重啟,請稍後手動刷新瀏覽器!',
|
||||
|
|
|
@ -374,6 +374,8 @@ const message = {
|
|||
home: {
|
||||
recommend: '推荐',
|
||||
dir: '目录',
|
||||
quickDir: '快捷目录',
|
||||
database: '数据库 - 所有',
|
||||
restart_1panel: '重启面板',
|
||||
restart_system: '重启服务器',
|
||||
operationSuccess: '操作成功,正在重启,请稍后手动刷新浏览器!',
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
95
frontend/src/views/home/quick/index.vue
Normal file
95
frontend/src/views/home/quick/index.vue
Normal 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>
|
Loading…
Add table
Reference in a new issue