mirror of
https://github.com/lzh-1625/go_process_manager.git
synced 2025-10-06 00:16:53 +08:00
ui update
This commit is contained in:
@@ -40,7 +40,7 @@ class RequestHttp {
|
|||||||
this.service.interceptors.request.use(
|
this.service.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
const token = localStorage.getItem("token") || "";
|
const token = localStorage.getItem("token") || "";
|
||||||
config.headers.Authorization = token;
|
config.headers.Authorization = "bearer " + token;
|
||||||
config.url = "/api" + config.url;
|
config.url = "/api" + config.url;
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
|
import { ProcessItem } from "../types/process/process";
|
||||||
import api from "./api";
|
import api from "./api";
|
||||||
|
|
||||||
export function getProcessList() {
|
export function getProcessList() {
|
||||||
return api.get("/process", undefined).then((res) => res);
|
return api.get<ProcessItem[]>("/process", undefined).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getProcessListWait() {
|
export function getProcessListWait() {
|
||||||
return api.get("/process/wait", undefined).then((res) => res);
|
return api.get<ProcessItem[]>("/process/wait", undefined).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function killProcessAll(uuid) {
|
export function killProcessAll(uuid) {
|
||||||
|
206
resources/src/components/process/ProcessCard.vue
Normal file
206
resources/src/components/process/ProcessCard.vue
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ProcessItem } from "~/src/types/process/process";
|
||||||
|
import { init } from "echarts";
|
||||||
|
const initEChart = () => {
|
||||||
|
props.data.usage.cpu = (props.data.usage.cpu ?? [0, 0]).map((num) =>
|
||||||
|
parseFloat(num.toFixed(2))
|
||||||
|
);
|
||||||
|
props.data.usage.mem = (props.data.usage.mem ?? [0, 0]).map((num) =>
|
||||||
|
parseFloat(num.toFixed(2))
|
||||||
|
);
|
||||||
|
const cpu = props.data.usage.cpu[props.data.usage.cpu.length - 1] ?? "-";
|
||||||
|
const mem = props.data.usage.mem[props.data.usage.mem.length - 1] ?? "-";
|
||||||
|
var myChart = init(document.getElementById("echarts" + props.index));
|
||||||
|
var option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ["CPU", "内存"],
|
||||||
|
},
|
||||||
|
animationDuration: 2000,
|
||||||
|
grid: {
|
||||||
|
left: "3%",
|
||||||
|
right: "4%",
|
||||||
|
bottom: "3%",
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
boundaryGap: false,
|
||||||
|
show: false,
|
||||||
|
data: props.data.usage.time,
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: "value",
|
||||||
|
name: " CPU(" + cpu + "%)",
|
||||||
|
min: 0, // 设置CPU的y轴最小值为10
|
||||||
|
max: props.data.usage.cpuCapacity,
|
||||||
|
minInterval: 0.1,
|
||||||
|
splitLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisTick: { show: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "value",
|
||||||
|
name: " 内存(" + mem + "MB)",
|
||||||
|
max: parseFloat((props.data.usage.memCapacity / 1024).toFixed(2)),
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisTick: { show: false },
|
||||||
|
splitLine: { show: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "CPU",
|
||||||
|
type: "line",
|
||||||
|
data: props.data.usage.cpu,
|
||||||
|
yAxisIndex: 0,
|
||||||
|
showSymbol: false,
|
||||||
|
lineStyle: {
|
||||||
|
color: "#4ee5b9",
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: "#4ee5b9",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "内存",
|
||||||
|
type: "line",
|
||||||
|
data: props.data.usage.mem,
|
||||||
|
yAxisIndex: 1,
|
||||||
|
showSymbol: false,
|
||||||
|
lineStyle: {
|
||||||
|
color: "#ffe17e",
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: "#ffe17e",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
if (props.data.cgroupEnable) {
|
||||||
|
if (props.data.cpuLimit) {
|
||||||
|
(option.series as any).push({
|
||||||
|
name: "CPU限制(%)",
|
||||||
|
type: "line",
|
||||||
|
yAxisIndex: 0,
|
||||||
|
data: new Array(props.data.usage.time!.length).fill(
|
||||||
|
props.data.cpuLimit
|
||||||
|
),
|
||||||
|
lineStyle: {
|
||||||
|
type: "dashed",
|
||||||
|
color: "#4ee5b9",
|
||||||
|
},
|
||||||
|
showSymbol: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (props.data.memoryLimit) {
|
||||||
|
(option.series as any).push({
|
||||||
|
name: "内存限制(MB)",
|
||||||
|
type: "line",
|
||||||
|
yAxisIndex: 1,
|
||||||
|
data: new Array(props.data.usage.time!.length).fill(
|
||||||
|
props.data.memoryLimit
|
||||||
|
),
|
||||||
|
lineStyle: {
|
||||||
|
type: "dashed",
|
||||||
|
color: "#ffe17e",
|
||||||
|
},
|
||||||
|
showSymbol: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(option);
|
||||||
|
myChart.setOption(option);
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{ label: "按钮1", action: () => console.log("按钮1点击") },
|
||||||
|
{ label: "按钮2", action: () => console.log("按钮2点击") },
|
||||||
|
{ label: "按钮3", action: () => console.log("按钮3点击") },
|
||||||
|
];
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initEChart();
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
data: ProcessItem;
|
||||||
|
index: Number;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="chart-container">
|
||||||
|
<!-- 顶部:进程名字 + 菜单 -->
|
||||||
|
<div class="header">
|
||||||
|
<div class="top-left">{{ props.data.name }}</div>
|
||||||
|
<div class="top-right">
|
||||||
|
<button @click="">菜单</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 中间:ECharts -->
|
||||||
|
<div :id="'echarts' + props.index" class="chart"></div>
|
||||||
|
|
||||||
|
<!-- 底部:按钮组 + 时间 -->
|
||||||
|
<div class="footer">
|
||||||
|
<div class="bottom-left">
|
||||||
|
<button v-for="(btn, idx) in buttons" :key="idx" @click="">
|
||||||
|
{{ btn.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="bottom-right">{{ props.data.startTime }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.chart-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 260px; /* 可根据实际容器调整 */
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部 header */
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
height: 30px; /* 顶部固定高度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中间图表自适应 */
|
||||||
|
.chart {
|
||||||
|
flex: 1; /* 占满剩余空间 */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部 footer */
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 10px;
|
||||||
|
height: 40px; /* 底部固定高度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -22,12 +22,13 @@ import i18n from "./plugins/i18n";
|
|||||||
import Vue3Lottie from "vue3-lottie";
|
import Vue3Lottie from "vue3-lottie";
|
||||||
import { autoAnimatePlugin } from '@formkit/auto-animate/vue'
|
import { autoAnimatePlugin } from '@formkit/auto-animate/vue'
|
||||||
import permission from "./directives/permission"
|
import permission from "./directives/permission"
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
pinia.use(piniaPersist);
|
pinia.use(piniaPersist);
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
|
app.config.globalProperties.$echarts = echarts;
|
||||||
app.directive('permission', permission);
|
app.directive('permission', permission);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.use(PerfectScrollbarPlugin);
|
app.use(PerfectScrollbarPlugin);
|
||||||
|
34
resources/src/types/process/process.ts
Normal file
34
resources/src/types/process/process.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
export interface ProcessItem {
|
||||||
|
name: string;
|
||||||
|
uuid: number;
|
||||||
|
startTime: Date;
|
||||||
|
user: string;
|
||||||
|
usage: Usage;
|
||||||
|
state: State;
|
||||||
|
termType: TermType;
|
||||||
|
cgroupEnable: boolean;
|
||||||
|
memoryLimit: number | null;
|
||||||
|
cpuLimit: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
state: number;
|
||||||
|
info: Info;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Info {
|
||||||
|
Empty = "",
|
||||||
|
重启次数异常 = "重启次数异常",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TermType {
|
||||||
|
Pty = "pty",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Usage {
|
||||||
|
cpuCapacity: number;
|
||||||
|
memCapacity: number;
|
||||||
|
cpu: number[] | null;
|
||||||
|
mem: number[] | null;
|
||||||
|
time: string[] | null;
|
||||||
|
}
|
@@ -1,3 +1,48 @@
|
|||||||
<script setup lang="ts"></script>
|
<template>
|
||||||
|
<v-container>
|
||||||
|
<div class="flex-grid">
|
||||||
|
<div v-for="(i, v) in processData" class="responsive-box">
|
||||||
|
<ProcessCard :data="i" :index="v"></ProcessCard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template></template>
|
<script setup lang="ts">
|
||||||
|
import ProcessCard from "@/components/process/ProcessCard.vue";
|
||||||
|
import { getProcessList } from "~/src/api/process";
|
||||||
|
import { ProcessItem } from "~/src/types/process/process";
|
||||||
|
|
||||||
|
const processData = ref<ProcessItem[]>();
|
||||||
|
|
||||||
|
const initProcessData = () => {
|
||||||
|
getProcessList().then((e) => {
|
||||||
|
processData.value = e.data!;
|
||||||
|
console.log(e.data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initProcessData();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.flex-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap; /* 自动换行 */
|
||||||
|
justify-content: space-between; /* 两边与中间间距均匀 */
|
||||||
|
gap: 50px; /* 每个 div 之间的间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-box {
|
||||||
|
flex: 1 1 300px; /* 最小宽度 500px */
|
||||||
|
min-width: 300px; /* 强制最小宽度 */
|
||||||
|
max-width: 100%; /* 不超过容器宽度 */
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
Reference in New Issue
Block a user