2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 14:57:16 +08:00

feat: 进程管理

This commit is contained in:
耗子
2024-12-03 21:14:27 +08:00
parent 8c7a65a8b6
commit a3bbbfa87c
8 changed files with 336 additions and 10 deletions

View File

@@ -0,0 +1,8 @@
import { http } from '@/utils'
export default {
// 获取进程列表
list: (page: number, limit: number) => http.Get(`/process`, { params: { page, limit } }),
// 杀死进程
kill: (pid: number) => http.Post(`/process/kill`, { pid })
}

View File

@@ -330,11 +330,7 @@ const handleUpdate = () => {
}
const toSponsor = () => {
if (locale.value === 'en') {
window.open('https://opencollective.com/tnb')
} else {
window.open('https://afdian.com/a/TheTNB')
}
window.open('https://afdian.com/a/TheTNB')
}
const handleManageApp = (slug: string) => {
@@ -443,11 +439,7 @@ if (import.meta.hot) {
<p>负载状态</p>
<n-progress
type="dashboard"
:percentage="
Math.round(formatPercent((realtime.load.load1 / cores) * 100)) > 100
? 100
: Math.round(formatPercent((realtime.load.load1 / cores) * 100))
"
:percentage="Math.round(formatPercent((realtime.load.load1 / cores) * 100))"
:color="statusColor((realtime.load.load1 / cores) * 100)"
>
</n-progress>

View File

@@ -8,6 +8,7 @@ defineOptions({
import TheIcon from '@/components/custom/TheIcon.vue'
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'
const current = ref('cron')
@@ -27,6 +28,9 @@ const create = ref(false)
<n-tab-pane name="cron" tab="计划任务">
<cron-view />
</n-tab-pane>
<n-tab-pane name="system" tab="系统进程">
<system-view />
</n-tab-pane>
<n-tab-pane name="task" tab="面板任务">
<task-view />
</n-tab-pane>

View File

@@ -0,0 +1,164 @@
<script setup lang="ts">
import { NButton, NDataTable, NPopconfirm, NTag } from 'naive-ui'
import process from '@/api/panel/process'
import { formatBytes, formatDateTime, formatPercent, renderIcon } from '@/utils'
const columns: any = [
{
title: 'PID',
key: 'pid',
width: 120,
ellipsis: { tooltip: true }
},
{
title: '名称',
key: 'name',
minWidth: 250,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '父进程 ID',
key: 'ppid',
width: 120,
ellipsis: { tooltip: true }
},
{
title: '线程数',
key: 'num_threads',
width: 100,
ellipsis: { tooltip: true }
},
{
title: '用户',
key: 'username',
minWidth: 100,
ellipsis: { tooltip: true }
},
{
title: '状态',
key: 'status',
minWidth: 150,
ellipsis: { tooltip: true },
render(row: any) {
switch (row.status) {
case 'R':
return h(NTag, { type: 'success' }, { default: () => '运行' })
case 'S':
return h(NTag, { type: 'warning' }, { default: () => '睡眠' })
case 'T':
return h(NTag, { type: 'error' }, { default: () => '停止' })
case 'I':
return h(NTag, { type: 'primary' }, { default: () => '空闲' })
case 'Z':
return h(NTag, { type: 'error' }, { default: () => '僵尸' })
case 'W':
return h(NTag, { type: 'warning' }, { default: () => '等待' })
case 'L':
return h(NTag, { type: 'info' }, { default: () => '锁定' })
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: '内存',
key: 'rss',
minWidth: 100,
ellipsis: { tooltip: true },
render(row: any): string {
return formatBytes(row.rss)
}
},
{
title: '启动时间',
key: 'start_time',
width: 160,
ellipsis: { tooltip: true },
render(row: any): string {
return formatDateTime(row.start_time)
}
},
{
title: '操作',
key: 'actions',
width: 150,
align: 'center',
hideInExcel: true,
render(row: any) {
return h(
NPopconfirm,
{
onPositiveClick: async () => {
await process.kill(row.pid)
await refresh()
window.$message.success(`进程 ${row.pid} 已终止`)
}
},
{
default: () => {
return '确定终止进程 ' + row.pid + ' ?'
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'error'
},
{
default: () => '终止',
icon: renderIcon('material-symbols:stop-circle-outline-rounded', { size: 14 })
}
)
}
}
)
}
}
]
const { loading, data, page, total, pageSize, pageCount, refresh } = usePagination(
(page, pageSize) => process.list(page, pageSize),
{
initialData: { total: 0, list: [] },
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>