2024-06-29 18:16:29 +08:00
|
|
|
<script setup lang="ts">
|
|
|
|
import { ref, onMounted } from 'vue';
|
|
|
|
import { useRuntimeConfig, useCookie } from '#imports';
|
|
|
|
import {useMainLayoutStore} from "~/strores/UseMainLayoutStore";
|
|
|
|
import { useToast } from 'vue-toastification';
|
|
|
|
type ProcessListType = {
|
|
|
|
pid: string;
|
|
|
|
user: string;
|
|
|
|
cpu: string;
|
|
|
|
memory: string;
|
|
|
|
processName: string;
|
|
|
|
};
|
|
|
|
const toast=useToast()
|
|
|
|
const processList = ref<ProcessListType[]>([]);
|
|
|
|
const mainLayoutStore = useMainLayoutStore();
|
|
|
|
const currentSort = ref<'pid' | 'user' | 'cpu' | 'memory' | 'processName'>('cpu');
|
|
|
|
const currentSortDir = ref<'asc' | 'desc'>('desc');
|
|
|
|
const props=defineProps({
|
|
|
|
userName:{
|
|
|
|
type:String,
|
|
|
|
default:''
|
|
|
|
},
|
|
|
|
})
|
|
|
|
const emit=defineEmits([
|
|
|
|
'update'
|
|
|
|
])
|
|
|
|
const getProcessList = async () => {
|
|
|
|
const response = await $fetch('/Api/Server/GetServerProcessesList', {
|
|
|
|
method: 'GET',
|
|
|
|
params: {
|
2024-07-22 21:05:18 +08:00
|
|
|
ServerId: mainLayoutStore.SelectServer.value,
|
2024-06-29 18:16:29 +08:00
|
|
|
UserName:props.userName
|
|
|
|
},
|
|
|
|
baseURL: useRuntimeConfig().public.baseUrl,
|
|
|
|
headers: {
|
|
|
|
'Authorization': 'Bearer ' + useCookie('token').value,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
processList.value = response as ProcessListType[];
|
|
|
|
}
|
|
|
|
let interval:NodeJS.Timeout;
|
|
|
|
onMounted(async () => {
|
|
|
|
await getProcessList();
|
|
|
|
interval = setInterval(async ()=>{
|
|
|
|
await getProcessList();
|
|
|
|
},10000)
|
|
|
|
});
|
|
|
|
onBeforeUnmount(()=>{
|
|
|
|
clearInterval(interval);
|
|
|
|
})
|
|
|
|
|
|
|
|
const sortedProcessList = computed(() => {
|
|
|
|
emit('update',processList.value.map(x=>x.pid))
|
|
|
|
return processList.value.sort((a, b) => {
|
|
|
|
let modifier = 1;
|
|
|
|
if (currentSortDir.value === 'desc') modifier = -1;
|
|
|
|
if (a[currentSort.value] < b[currentSort.value]) return -1 * modifier;
|
|
|
|
if (a[currentSort.value] > b[currentSort.value]) return modifier;
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const sort = (s: 'pid' | 'user' | 'cpu' | 'memory' | 'processName') => {
|
|
|
|
if (currentSort.value === s) {
|
|
|
|
currentSortDir.value = currentSortDir.value === 'asc' ? 'desc' : 'asc';
|
|
|
|
} else {
|
|
|
|
currentSort.value = s;
|
|
|
|
currentSortDir.value = 'asc';
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const killProcess = (pid: string,force:boolean=false) => {
|
|
|
|
$fetch('/Api/Server/GetServerProcessesKill', {
|
|
|
|
method: 'GET',
|
|
|
|
params: {
|
|
|
|
ServerId: mainLayoutStore.SelectServer.id,
|
|
|
|
Pid: pid,
|
|
|
|
Force:force,
|
|
|
|
},
|
|
|
|
baseURL: useRuntimeConfig().public.baseUrl,
|
|
|
|
headers: {
|
|
|
|
'Authorization': 'Bearer ' + useCookie('token').value,
|
|
|
|
},
|
|
|
|
}).then((res) => {
|
|
|
|
console.log(res)
|
|
|
|
toast.success(res)
|
|
|
|
}).catch((err) => {
|
|
|
|
toast.error(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
|
|
<div class="process-table">
|
|
|
|
<table>
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th @click="sort('processName')">
|
|
|
|
<div>
|
|
|
|
<p>进程名称</p>
|
|
|
|
<Icon v-if="currentSort==='processName'&¤tSortDir==='desc'" name="ArrowDownNarrowWide"></Icon>
|
|
|
|
<Icon v-if="currentSort==='processName'&¤tSortDir==='asc'" name="ArrowUpNarrowWide"></Icon>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</th>
|
|
|
|
<th @click="sort('pid')">
|
|
|
|
<div>
|
|
|
|
<p>进程ID</p>
|
|
|
|
<Icon v-if="currentSort==='pid'&¤tSortDir==='desc'" name="ArrowDownNarrowWide"></Icon>
|
|
|
|
<Icon v-if="currentSort==='pid'&¤tSortDir==='asc'" name="ArrowUpNarrowWide"></Icon>
|
|
|
|
</div>
|
|
|
|
</th>
|
|
|
|
<th @click="sort('user')">
|
|
|
|
<div>
|
|
|
|
<p>用户</p>
|
|
|
|
<Icon v-if="currentSort==='user'&¤tSortDir==='desc'" name="ArrowDownNarrowWide"></Icon>
|
|
|
|
<Icon v-if="currentSort==='user'&¤tSortDir==='asc'" name="ArrowUpNarrowWide"></Icon>
|
|
|
|
</div>
|
|
|
|
</th>
|
|
|
|
<th @click="sort('cpu')">
|
|
|
|
<div>
|
|
|
|
<p>CPU使用率</p>
|
|
|
|
<Icon v-if="currentSort==='cpu'&¤tSortDir==='desc'" name="ArrowDownNarrowWide"></Icon>
|
|
|
|
<Icon v-if="currentSort==='cpu'&¤tSortDir==='asc'" name="ArrowUpNarrowWide"></Icon>
|
|
|
|
</div>
|
|
|
|
</th>
|
|
|
|
<th @click="sort('memory')">
|
|
|
|
<div>
|
|
|
|
<p>内存使用率</p>
|
|
|
|
<Icon v-if="currentSort==='memory'&¤tSortDir==='desc'" name="ArrowDownNarrowWide"></Icon>
|
|
|
|
<Icon v-if="currentSort==='memory'&¤tSortDir==='asc'" name="ArrowUpNarrowWide"></Icon>
|
|
|
|
</div>
|
|
|
|
</th>
|
|
|
|
<th>
|
|
|
|
<div>
|
|
|
|
<p>操作</p>
|
|
|
|
</div>
|
|
|
|
</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
<tr v-for="item in sortedProcessList" :key="item.pid">
|
|
|
|
<td>
|
|
|
|
<Icon name="Shell"/>
|
|
|
|
{{ item.processName }}
|
|
|
|
</td>
|
|
|
|
<td>{{ item.pid }}</td>
|
|
|
|
<td>{{ item.user }}</td>
|
|
|
|
<td>{{ item.cpu }}</td>
|
|
|
|
<td>{{ item.memory }}</td>
|
|
|
|
<td><div>
|
|
|
|
<button @click="killProcess(item.pid)">关闭</button><button @click="killProcess(item.pid,true)">杀死</button>
|
|
|
|
</div></td>
|
|
|
|
</tr>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
@import "base";
|
|
|
|
.process-table {
|
|
|
|
width: 100%;
|
|
|
|
padding: $padding*2;
|
2024-07-02 14:28:15 +08:00
|
|
|
*{
|
|
|
|
@include SC_Font;
|
|
|
|
}
|
2024-06-29 18:16:29 +08:00
|
|
|
table {
|
|
|
|
width: 100%;
|
|
|
|
border-collapse: collapse;
|
|
|
|
}
|
|
|
|
th, td {
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
border-top: unset;
|
|
|
|
border-bottom: unset;
|
|
|
|
padding: 8px;
|
|
|
|
}
|
2024-07-02 14:28:15 +08:00
|
|
|
p,td{
|
|
|
|
color: $light-text-color;
|
|
|
|
.dark-mode &{
|
|
|
|
color: $dark-text-color;
|
|
|
|
}
|
|
|
|
}
|
2024-06-29 18:16:29 +08:00
|
|
|
th {
|
|
|
|
>div{
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
gap: 8px;
|
|
|
|
cursor: pointer;
|
|
|
|
p{
|
|
|
|
margin: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tr:hover{
|
|
|
|
background: $light-bg-underline-color;
|
2024-07-02 14:28:15 +08:00
|
|
|
.dark-mode &{
|
|
|
|
background: $dark-bg-underline-color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
td:hover{
|
|
|
|
background:rgba($primary-color,0.2);
|
|
|
|
.dark-mode &{
|
|
|
|
background:#2F3F53;
|
|
|
|
}
|
2024-06-29 18:16:29 +08:00
|
|
|
}
|
|
|
|
tr>td:first-of-type{
|
|
|
|
display: flex;
|
|
|
|
gap: $gap;
|
|
|
|
}
|
|
|
|
td:not(:first-child):not(:last-child) {
|
|
|
|
text-align: right;
|
|
|
|
}
|
|
|
|
td:last-of-type{
|
|
|
|
//大小适应内容
|
|
|
|
>div{
|
|
|
|
display: flex;
|
|
|
|
gap: $gap*2;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
button{
|
|
|
|
background: unset;
|
|
|
|
border: unset;
|
|
|
|
padding: 0;
|
2024-07-02 14:28:15 +08:00
|
|
|
color:$light-text-color;
|
2024-06-29 18:16:29 +08:00
|
|
|
&:hover{
|
|
|
|
color: $primary-color;
|
2024-07-02 14:28:15 +08:00
|
|
|
cursor: pointer;
|
2024-06-29 18:16:29 +08:00
|
|
|
font-weight: 800;
|
|
|
|
}
|
|
|
|
&:last-of-type:hover{
|
|
|
|
color: red;
|
|
|
|
}
|
2024-07-02 14:28:15 +08:00
|
|
|
.dark-mode &{
|
|
|
|
color:$dark-text-color;
|
|
|
|
}
|
2024-06-29 18:16:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
td:first-of-type,th:first-of-type,td:last-of-type,th:last-of-type {
|
|
|
|
border: unset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|