2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-07 07:03:12 +08:00

feat: 一堆调整

This commit is contained in:
2025-12-01 19:37:19 +08:00
parent a64a1ba5c8
commit 5fdce07190
28 changed files with 304 additions and 122 deletions

View File

@@ -1,25 +0,0 @@
import { http } from '@/utils'
export default {
// 面板信息
panel: (): any => http.Get('/dashboard/panel'),
// 首页应用
homeApps: (): any => http.Get('/dashboard/home_apps'),
// 实时信息
current: (nets: string[], disks: string[]): any =>
http.Post('/dashboard/current', { nets, disks }, { meta: { noAlert: true } }),
// 系统信息
systemInfo: (): any => http.Get('/dashboard/system_info'),
// 统计信息
countInfo: (): any => http.Get('/dashboard/count_info'),
// 已安装的数据库和PHP
installedDbAndPhp: (): any => http.Get('/dashboard/installed_db_and_php'),
// 检查更新
checkUpdate: (): any => http.Get('/dashboard/check_update'),
// 更新日志
updateInfo: (): any => http.Get('/dashboard/update_info'),
// 更新面板
update: (): any => http.Post('/dashboard/update'),
// 重启面板
restart: (): any => http.Post('/dashboard/restart')
}

View File

@@ -0,0 +1,25 @@
import { http } from '@/utils'
export default {
// 面板信息
panel: (): any => http.Get('/home/panel'),
// 首页应用
apps: (): any => http.Get('/home/apps'),
// 实时信息
current: (nets: string[], disks: string[]): any =>
http.Post('/home/current', { nets, disks }, { meta: { noAlert: true } }),
// 系统信息
systemInfo: (): any => http.Get('/home/system_info'),
// 统计信息
countInfo: (): any => http.Get('/home/count_info'),
// 已安装的数据库和PHP
installedDbAndPhp: (): any => http.Get('/home/installed_db_and_php'),
// 检查更新
checkUpdate: (): any => http.Get('/home/check_update'),
// 更新日志
updateInfo: (): any => http.Get('/home/update_info'),
// 更新面板
update: (): any => http.Post('/home/update'),
// 重启面板
restart: (): any => http.Post('/home/restart')
}

View File

@@ -6,13 +6,13 @@ const themeStore = useThemeStore()
const router = useRouter()
const logo = computed(() => themeStore.logo || logoImg)
const toDashboard = () => {
router.push({ name: 'dashboard' })
const toHome = () => {
router.push({ name: 'home' })
}
</script>
<template>
<div class="h-60 f-c-c cursor-pointer" @click="toDashboard">
<div class="h-60 f-c-c cursor-pointer" @click="toHome">
<n-image :src="logo" height="32" preview-disabled />
<h2
v-show="!themeStore.sider.collapsed"

View File

@@ -8,16 +8,17 @@ export function translateTitle(key: string): string {
Backup: $gettext('Backup'),
Certificate: $gettext('Certificate'),
Container: $gettext('Container'),
Dashboard: $gettext('Dashboard'),
Update: $gettext('Update'),
Database: $gettext('Database'),
Files: $gettext('Files'),
Firewall: $gettext('Firewall'),
Home: $gettext('Home'),
Monitoring: $gettext('Monitoring'),
Settings: $gettext('Settings'),
Project: $gettext('Project'),
Setting: $gettext('Setting'),
Terminal: $gettext('Terminal'),
Tasks: $gettext('Tasks'),
Task: $gettext('Task'),
Toolbox: $gettext('Toolbox'),
Update: $gettext('Update'),
Website: $gettext('Website'),
'Website Edit': $gettext('Website Edit'),
// 应用标题

View File

@@ -12,7 +12,7 @@ import { gettext, setCurrent, setupNaiveDiscreteApi } from '@/utils'
import { install as VueMonacoEditorPlugin } from '@guolao/vue-monaco-editor'
import dashboard from '@/api/panel/dashboard'
import dashboard from '@/api/panel/home'
import CronNaivePlugin from '@vue-js-cron/naive-ui'
async function setupApp() {

View File

@@ -3,20 +3,37 @@ defineOptions({
name: 'app-index'
})
import { NButton } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import app from '@/api/panel/app'
import InstallView from '@/views/app/InstallView.vue'
const { $gettext } = useGettext()
const currentTab = ref('installed')
const handleUpdateCache = () => {
useRequest(app.updateCache()).onSuccess(() => {
window.$message.success($gettext('Cache updated successfully'))
})
}
</script>
<template>
<common-page show-header show-footer>
<template #tabbar>
<n-tabs v-model:value="currentTab" animated>
<n-tab name="installed" :tab="$gettext('Installed')" />
<n-tab name="install" :tab="$gettext('Install')" />
<n-tab name="environment" :tab="$gettext('Environment')" />
<n-tab name="compose" :tab="$gettext('Compose Templates')" />
</n-tabs>
<div class="flex items-center justify-between">
<n-tabs v-model:value="currentTab" animated class="flex-1">
<n-tab name="installed" :tab="$gettext('Installed')" />
<n-tab name="install" :tab="$gettext('Install')" />
<n-tab name="environment" :tab="$gettext('Environment')" />
<n-tab name="compose" :tab="$gettext('Compose Templates')" />
</n-tabs>
<n-button v-if="currentTab != 'installed'" type="primary" @click="handleUpdateCache">
{{ $gettext('Update Cache') }}
</n-button>
</div>
</template>
<install-view v-if="currentTab === 'install'" />
</common-page>

View File

@@ -196,13 +196,6 @@ const handleManage = (slug: string) => {
router.push({ name: 'apps-' + slug + '-index' })
}
const handleUpdateCache = () => {
useRequest(app.updateCache()).onSuccess(() => {
refresh()
window.$message.success($gettext('Cache updated successfully'))
})
}
onMounted(() => {
refresh()
})
@@ -210,11 +203,6 @@ onMounted(() => {
<template>
<n-flex vertical>
<n-flex>
<n-button type="primary" @click="handleUpdateCache">
{{ $gettext('Update Cache') }}
</n-button>
</n-flex>
<n-alert type="warning">{{
$gettext(
'Before updating apps, it is strongly recommended to backup/snapshot first, so you can roll back immediately if there are any issues!'

View File

@@ -3,14 +3,14 @@ defineOptions({
name: 'backup-index'
})
import dashboard from '@/api/panel/dashboard'
import home from '@/api/panel/home'
import ListView from '@/views/backup/ListView.vue'
import { useGettext } from 'vue3-gettext'
const { $gettext } = useGettext()
const currentTab = ref('website')
const { data: installedDbAndPhp } = useRequest(dashboard.installedDbAndPhp, {
const { data: installedDbAndPhp } = useRequest(home.installedDbAndPhp, {
initialData: {
db: [
{

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup>
defineOptions({
name: 'dashboard-index'
name: 'home-index'
})
import { LineChart } from 'echarts/charts'
@@ -16,7 +16,7 @@ import { CanvasRenderer } from 'echarts/renderers'
import { NButton, NPopconfirm, useThemeVars } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import dashboard from '@/api/panel/dashboard'
import home from '@/api/panel/home'
import { router } from '@/router'
import { useTabStore } from '@/store'
import { formatDateTime, formatDuration, toTimestamp } from '@/utils/common'
@@ -39,7 +39,7 @@ const themeVars = useThemeVars()
const tabStore = useTabStore()
const realtime = ref<Realtime | null>(null)
const { data: systemInfo } = useRequest(dashboard.systemInfo, {
const { data: systemInfo } = useRequest(home.systemInfo, {
initialData: {
procs: 0,
hostname: '',
@@ -59,7 +59,7 @@ const { data: systemInfo } = useRequest(dashboard.systemInfo, {
disks: []
}
})
const { data: homeApps, loading: homeAppsLoading } = useRequest(dashboard.homeApps, {
const { data: apps, loading: appLoading } = useRequest(home.apps, {
initialData: {
description: '',
icon: '',
@@ -68,7 +68,7 @@ const { data: homeApps, loading: homeAppsLoading } = useRequest(dashboard.homeAp
version: ''
}
})
const { data: countInfo } = useRequest(dashboard.countInfo, {
const { data: countInfo } = useRequest(home.countInfo, {
initialData: {
website: 0,
database: 0,
@@ -235,7 +235,7 @@ let isFetching = false
const fetchCurrent = () => {
if (isFetching) return
isFetching = true
useRequest(dashboard.current(nets.value, disks.value))
useRequest(home.current(nets.value, disks.value))
.onSuccess(({ data }) => {
data.percent = formatPercent(data.percent)
data.mem.usedPercent = formatPercent(data.mem.usedPercent)
@@ -323,7 +323,7 @@ const fetchCurrent = () => {
const handleRestartPanel = () => {
clearInterval(homeInterval)
window.$message.loading($gettext('Panel restarting...'))
useRequest(dashboard.restart()).onSuccess(() => {
useRequest(home.restart()).onSuccess(() => {
window.$message.success($gettext('Panel restarted successfully'))
setTimeout(() => {
tabStore.reloadTab(tabStore.active)
@@ -332,9 +332,9 @@ const handleRestartPanel = () => {
}
const handleUpdate = () => {
useRequest(dashboard.checkUpdate()).onSuccess(({ data }) => {
useRequest(home.checkUpdate()).onSuccess(({ data }) => {
if (data.update) {
router.push({ name: 'dashboard-update' })
router.push({ name: 'home-update' })
} else {
window.$message.success($gettext('Current version is the latest'))
}
@@ -682,7 +682,7 @@ if (import.meta.hot) {
<n-card :segmented="true" size="small" :title="$gettext('Quick Apps')" min-h-340>
<n-scrollbar max-h-270>
<n-grid
v-if="!homeAppsLoading"
v-if="!appLoading"
x-gap="12"
y-gap="12"
cols="3 s:1 m:2 l:3"
@@ -690,7 +690,7 @@ if (import.meta.hot) {
responsive="screen"
p-10
>
<n-gi v-for="item in homeApps" :key="item.name">
<n-gi v-for="item in apps" :key="item.name">
<n-card
:segmented="true"
size="small"
@@ -717,10 +717,10 @@ if (import.meta.hot) {
</n-gi>
</n-grid>
</n-scrollbar>
<n-text v-if="!homeAppsLoading && !homeApps.length">
<n-text v-if="!appLoading && !apps.length">
{{ $gettext('You have not set any apps to display here!') }}
</n-text>
<n-skeleton v-if="homeAppsLoading" text :repeat="12" />
<n-skeleton v-if="appLoading" text :repeat="12" />
</n-card>
<n-card :segmented="true" size="small" :title="$gettext('Environment Information')">
<n-table v-if="systemInfo" :single-line="false">

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
defineOptions({
name: 'dashboard-update'
name: 'home-update'
})
import { MdPreview } from 'md-editor-v3'
@@ -9,12 +9,12 @@ import type { MessageReactive } from 'naive-ui'
import { NButton } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import dashboard from '@/api/panel/dashboard'
import home from '@/api/panel/home'
import { router } from '@/router'
import { formatDateTime } from '@/utils'
const { $gettext } = useGettext()
const { data: versions } = useRequest(dashboard.updateInfo, {
const { data: versions } = useRequest(home.updateInfo, {
initialData: []
})
let messageReactive: MessageReactive | null = null
@@ -29,13 +29,13 @@ const handleUpdate = () => {
messageReactive = window.$message.loading($gettext('Panel updating...'), {
duration: 0
})
useRequest(dashboard.update())
useRequest(home.update())
.onSuccess(() => {
setTimeout(() => {
setTimeout(() => {
window.location.reload()
}, 400)
router.push({ name: 'dashboard-index' })
router.push({ name: 'home-index' })
}, 2500)
window.$message.success($gettext('Panel updated successfully'))
})

View File

@@ -3,27 +3,26 @@ import type { RouteType } from '~/types/router'
const Layout = () => import('@/layout/IndexView.vue')
export default {
name: 'dashboard',
name: 'home',
path: '/',
component: Layout,
redirect: '/dashboard',
meta: {
order: 0
},
children: [
{
name: 'dashboard-index',
path: 'dashboard',
name: 'home-index',
path: '',
component: () => import('./IndexView.vue'),
meta: {
title: 'Dashboard',
icon: 'mdi:gauge',
title: 'Home',
icon: 'mdi:house-outline',
role: ['admin'],
requireAuth: true
}
},
{
name: 'dashboard-update',
name: 'home-update',
path: 'update',
component: () => import('./UpdateView.vue'),
isHidden: true,

View File

@@ -15,8 +15,8 @@ export default {
path: '',
component: () => import('./IndexView.vue'),
meta: {
title: 'Projects',
icon: 'mdi:folder-multiple',
title: 'Project',
icon: 'mdi:folder-multiple-outline',
role: ['admin'],
requireAuth: true
}

View File

@@ -15,7 +15,7 @@ export default {
path: '',
component: () => import('./IndexView.vue'),
meta: {
title: 'Settings',
title: 'Setting',
icon: 'mdi:settings-outline',
role: ['admin'],
requireAuth: true

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import app from '@/api/panel/app'
import cron from '@/api/panel/cron'
import dashboard from '@/api/panel/dashboard'
import dashboard from '@/api/panel/home'
import website from '@/api/panel/website'
import Editor from '@guolao/vue-monaco-editor'
import { CronNaive } from '@vue-js-cron/naive-ui'

View File

@@ -5,7 +5,6 @@ defineOptions({
import CreateModal from '@/views/task/CreateModal.vue'
import CronView from '@/views/task/CronView.vue'
import SystemView from '@/views/task/SystemView.vue'
import TaskView from '@/views/task/TaskView.vue'
import { NButton } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
@@ -21,7 +20,6 @@ const create = ref(false)
<template #tabbar>
<n-tabs v-model:value="current" animated>
<n-tab name="cron" :tab="$gettext('Scheduled Tasks')" />
<n-tab name="system" :tab="$gettext('System Processes')" />
<n-tab name="task" :tab="$gettext('Panel Tasks')" />
</n-tabs>
</template>
@@ -32,7 +30,6 @@ const create = ref(false)
</n-button>
</n-flex>
<cron-view v-if="current === 'cron'" />
<system-view v-if="current === 'system'" />
<task-view v-if="current === 'task'" />
</n-flex>
</common-page>

View File

@@ -15,7 +15,7 @@ export default {
path: '',
component: () => import('./IndexView.vue'),
meta: {
title: 'Tasks',
title: 'Task',
icon: 'mdi:timetable',
role: ['admin'],
requireAuth: true

View File

@@ -4,22 +4,25 @@ defineOptions({
})
import BenchmarkView from '@/views/toolbox/BenchmarkView.vue'
import ProcessView from '@/views/toolbox/ProcessView.vue'
import SystemView from '@/views/toolbox/SystemView.vue'
import { useGettext } from 'vue3-gettext'
const { $gettext } = useGettext()
const current = ref('system')
const current = ref('process')
</script>
<template>
<common-page show-header show-footer>
<template #tabbar>
<n-tabs v-model:value="current" animated>
<n-tab name="process" :tab="$gettext('Process')" />
<n-tab name="system" :tab="$gettext('System')" />
<n-tab name="benchmark" :tab="$gettext('Benchmark')" />
</n-tabs>
</template>
<n-flex vertical>
<process-view v-if="current === 'process'" />
<system-view v-if="current === 'system'" />
<benchmark-view v-if="current === 'benchmark'" />
</n-flex>

View File

@@ -0,0 +1,171 @@
<script setup lang="ts">
import { NButton, NDataTable, NPopconfirm, NTag } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import process from '@/api/panel/process'
import { formatBytes, formatDateTime, formatPercent } from '@/utils'
const { $gettext } = useGettext()
const columns: any = [
{
title: 'PID',
key: 'pid',
width: 120,
ellipsis: { tooltip: true }
},
{
title: $gettext('Name'),
key: 'name',
minWidth: 250,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: $gettext('Parent PID'),
key: 'ppid',
width: 120,
ellipsis: { tooltip: true }
},
{
title: $gettext('Threads'),
key: 'num_threads',
width: 100,
ellipsis: { tooltip: true }
},
{
title: $gettext('User'),
key: 'username',
minWidth: 100,
ellipsis: { tooltip: true }
},
{
title: $gettext('Status'),
key: 'status',
minWidth: 150,
ellipsis: { tooltip: true },
render(row: any) {
switch (row.status) {
case 'R':
return h(NTag, { type: 'success' }, { default: () => $gettext('Running') })
case 'S':
return h(NTag, { type: 'warning' }, { default: () => $gettext('Sleeping') })
case 'T':
return h(NTag, { type: 'error' }, { default: () => $gettext('Stopped') })
case 'I':
return h(NTag, { type: 'primary' }, { default: () => $gettext('Idle') })
case 'Z':
return h(NTag, { type: 'error' }, { default: () => $gettext('Zombie') })
case 'W':
return h(NTag, { type: 'warning' }, { default: () => $gettext('Waiting') })
case 'L':
return h(NTag, { type: 'info' }, { default: () => $gettext('Locked') })
default:
return h(NTag, { type: 'default' }, { default: () => row.status })
}
}
},
{
title: 'CPU',
key: 'cpu',
minWidth: 100,
ellipsis: { tooltip: true },
render(row: any): string {
return formatPercent(row.cpu) + '%'
}
},
{
title: $gettext('Memory'),
key: 'rss',
minWidth: 100,
ellipsis: { tooltip: true },
render(row: any): string {
return formatBytes(row.rss)
}
},
{
title: $gettext('Start Time'),
key: 'start_time',
width: 160,
ellipsis: { tooltip: true },
render(row: any): string {
return formatDateTime(row.start_time)
}
},
{
title: $gettext('Actions'),
key: 'actions',
width: 150,
hideInExcel: true,
render(row: any) {
return h(
NPopconfirm,
{
onPositiveClick: () => {
useRequest(process.kill(row.pid)).onSuccess(() => {
refresh()
window.$message.success(
$gettext('Process %{ pid } has been terminated', { pid: row.pid })
)
})
}
},
{
default: () => {
return $gettext('Are you sure you want to terminate process %{ pid }?', {
pid: row.pid
})
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'error'
},
{
default: () => $gettext('Terminate')
}
)
}
}
)
}
}
]
const { loading, data, page, total, pageSize, pageCount, refresh } = usePagination(
(page, pageSize) => process.list(page, pageSize),
{
initialData: { total: 0, list: [] },
initialPageSize: 20,
total: (res: any) => res.total,
data: (res: any) => res.items
}
)
</script>
<template>
<n-flex vertical>
<n-data-table
striped
remote
:scroll-x="1400"
:loading="loading"
:columns="columns"
:data="data"
:row-key="(row: any) => row.pid"
v-model:page="page"
v-model:pageSize="pageSize"
:pagination="{
page: page,
pageCount: pageCount,
pageSize: pageSize,
itemCount: total,
showQuickJumper: true,
showSizePicker: true,
pageSizes: [20, 50, 100, 200]
}"
/>
</n-flex>
</template>

View File

@@ -9,7 +9,7 @@ import { NButton } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import cert from '@/api/panel/cert'
import dashboard from '@/api/panel/dashboard'
import dashboard from '@/api/panel/home'
import website from '@/api/panel/website'
import ProxyBuilderModal from '@/views/website/ProxyBuilderModal.vue'

View File

@@ -2,7 +2,7 @@
import { NButton, NCheckbox, NDataTable, NFlex, NInput, NPopconfirm, NSwitch, NTag } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import dashboard from '@/api/panel/dashboard'
import dashboard from '@/api/panel/home'
import website from '@/api/panel/website'
import { useFileStore } from '@/store'
import { generateRandomString, isNullOrUndef } from '@/utils'