style: 移除多余空行并统一代码格式

修复代码中的多余空行问题,统一字符串引号使用双引号,调整对象和数组的格式,优化代码缩进和换行,提升代码可读性和一致性
This commit is contained in:
zhangtao
2025-11-14 01:45:22 +08:00
parent 66c345d590
commit d588abb386
100 changed files with 9192 additions and 5181 deletions
+170 -85
View File
@@ -20,15 +20,15 @@
</el-tooltip>
</div>
</template>
<el-descriptions :column=12 border :direction="'vertical'">
<el-descriptions :column="12" border :direction="'vertical'">
<el-descriptions-item label="Redis版本">
{{ cache.info?.redis_version || '-' }}
{{ cache.info?.redis_version || "-" }}
</el-descriptions-item>
<el-descriptions-item label="运行模式">
{{ cache.info?.redis_mode === 'standalone' ? '单机' : '集群' }}
{{ cache.info?.redis_mode === "standalone" ? "单机" : "集群" }}
</el-descriptions-item>
<el-descriptions-item label="端口">
{{ cache.info?.tcp_port || '-' }}
{{ cache.info?.tcp_port || "-" }}
</el-descriptions-item>
<el-descriptions-item label="客户端数">
{{ cache.info?.connected_clients || 0 }}
@@ -37,26 +37,30 @@
{{ cache.info?.uptime_in_days || 0 }}
</el-descriptions-item>
<el-descriptions-item label="使用内存">
{{ cache.info?.used_memory_human || '-' }}
{{ cache.info?.used_memory_human || "-" }}
</el-descriptions-item>
<el-descriptions-item label="使用CPU">
{{ cache.info?.used_cpu_user_children ? parseFloat(cache.info.used_cpu_user_children).toFixed(2) : '-'
{{
cache.info?.used_cpu_user_children
? parseFloat(cache.info.used_cpu_user_children).toFixed(2)
: "-"
}}
</el-descriptions-item>
<el-descriptions-item label="内存配置">
{{ cache.info?.maxmemory_human || '-' }}
{{ cache.info?.maxmemory_human || "-" }}
</el-descriptions-item>
<el-descriptions-item label="AOF是否开启">
{{ cache.info?.aof_enabled === '0' ? '否' : '是' }}
{{ cache.info?.aof_enabled === "0" ? "否" : "是" }}
</el-descriptions-item>
<el-descriptions-item label="RDB是否成功">
{{ cache.info?.rdb_last_bgsave_status || '-' }}
{{ cache.info?.rdb_last_bgsave_status || "-" }}
</el-descriptions-item>
<el-descriptions-item label="Key数量">
{{ cache.db_size || 0 }}
</el-descriptions-item>
<el-descriptions-item label="网络入口/出口">
{{ `${cache.info?.instantaneous_input_kbps || 0}kps/${cache.info?.instantaneous_output_kbps || 0}kps`
{{
`${cache.info?.instantaneous_input_kbps || 0}kps/${cache.info?.instantaneous_output_kbps || 0}kps`
}}
</el-descriptions-item>
</el-descriptions>
@@ -124,25 +128,55 @@
</el-tooltip>
</div>
<div>
<el-button v-hasPerm="['module_monitor:cache:refresh']" type="primary" link icon="RefreshRight" @click="refreshCacheNames"/>
<el-button
v-hasPerm="['module_monitor:cache:refresh']"
type="primary"
link
icon="RefreshRight"
@click="refreshCacheNames"
/>
</div>
</div>
</template>
<el-table :loading="loading" :data="cacheNames" row-key="cache_name" height="600" border>
<el-table
:loading="loading"
:data="cacheNames"
row-key="cache_name"
height="600"
border
>
<template #empty>
<el-empty :image-size="80" description="暂无数据" />
</template>
<el-table-column prop="cache_name" label="缓存名称" show-overflow-tooltip>
<template #default="{ row }">
<el-button v-hasPerm="['module_monitor:cache:view']" type="primary" link @click="getCacheKeyList(row)">{{ row.cache_name }}</el-button>
<el-button
v-hasPerm="['module_monitor:cache:view']"
type="primary"
link
@click="getCacheKeyList(row)"
>
{{ row.cache_name }}
</el-button>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" show-overflow-tooltip />
<el-table-column label="操作" width="60" align="center">
<template #default="{ row }">
<el-popconfirm class="box-item" :title="`确认删除缓存 ${row.cache_name} 吗?`" placement="top" @confirm="handleClearCacheName(row)">
<el-popconfirm
class="box-item"
:title="`确认删除缓存 ${row.cache_name} 吗?`"
placement="top"
@confirm="handleClearCacheName(row)"
>
<template #reference>
<el-button v-hasPerm="['module_monitor:cache:delete']" type="danger" size="small" link icon="delete"/>
<el-button
v-hasPerm="['module_monitor:cache:delete']"
type="danger"
size="small"
link
icon="delete"
/>
</template>
</el-popconfirm>
</template>
@@ -168,24 +202,54 @@
</el-tooltip>
</div>
<div>
<el-button v-hasPerm="['module_monitor:cache:refresh']" type="primary" link icon="RefreshRight" @click="refreshCacheKeys"/>
<el-button
v-hasPerm="['module_monitor:cache:refresh']"
type="primary"
link
icon="RefreshRight"
@click="refreshCacheKeys"
/>
</div>
</div>
</template>
<el-table :loading="subLoading" :data="cacheKeys.map(key => ({ cacheKey: key }))" height="600" row-key="cacheKey" border>
<el-table
:loading="subLoading"
:data="cacheKeys.map((key) => ({ cacheKey: key }))"
height="600"
row-key="cacheKey"
border
>
<template #empty>
<el-empty :image-size="80" description="暂无数据" />
</template>
<el-table-column prop="cacheKey" label="缓存键名" show-overflow-tooltip>
<template #default="{ row }">
<el-button v-hasPerm="['module_monitor:cache:detail']" type="primary" link @click="handleCacheValue(row.cacheKey)">{{ row.cacheKey}}</el-button>
<el-button
v-hasPerm="['module_monitor:cache:detail']"
type="primary"
link
@click="handleCacheValue(row.cacheKey)"
>
{{ row.cacheKey }}
</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="60" align="center">
<template #default="{ row }">
<el-popconfirm class="box-item" :title="`确认删除键 ${row.cacheKey} 吗?`" placement="top" @confirm="handleClearCacheKey(row.cacheKey)">
<el-popconfirm
class="box-item"
:title="`确认删除键 ${row.cacheKey} 吗?`"
placement="top"
@confirm="handleClearCacheKey(row.cacheKey)"
>
<template #reference>
<el-button v-hasPerm="['module_monitor:cache:delete']" type="danger" size="small" link icon="delete"/>
<el-button
v-hasPerm="['module_monitor:cache:delete']"
type="danger"
size="small"
link
icon="delete"
/>
</template>
</el-popconfirm>
</template>
@@ -211,7 +275,15 @@
</el-tooltip>
</div>
<div>
<el-button v-hasPerm="['module_monitor:cache:delete_all']" type="danger" link icon="delete" @click="handleClearCacheAll">清理全部</el-button>
<el-button
v-hasPerm="['module_monitor:cache:delete_all']"
type="danger"
link
icon="delete"
@click="handleClearCacheAll"
>
清理全部
</el-button>
</div>
</div>
</template>
@@ -223,7 +295,13 @@
<el-input v-model="cacheForm.cache_key" readonly placeholder="缓存键名" />
</el-form-item>
<el-form-item label="缓存内容">
<el-input v-model="cacheForm.cache_value" type="textarea" :rows="18" readonly placeholder="缓存内容" />
<el-input
v-model="cacheForm.cache_value"
type="textarea"
:rows="18"
readonly
placeholder="缓存内容"
/>
</el-form-item>
</el-form>
</el-card>
@@ -235,26 +313,31 @@
</template>
<script lang="ts" setup>
import CacheAPI, { type CacheInfo, type CacheForm, type CacheMonitor, type RedisInfo } from "@/api/module_monitor/cache";
import * as echarts from 'echarts';
import CacheAPI, {
type CacheInfo,
type CacheForm,
type CacheMonitor,
type RedisInfo,
} from "@/api/module_monitor/cache";
import * as echarts from "echarts";
// 响应式状态定义
const cacheNames = ref<CacheInfo[]>([]);
const cacheKeys = ref<string[]>([]);
const loading = ref(true);
const subLoading = ref(false);
const nowCacheName = ref('');
const nowCacheName = ref("");
const commandstats = ref<HTMLElement | null>(null);
const usedmemory = ref<HTMLElement | null>(null);
const cache = ref<CacheMonitor>({
info: {} as RedisInfo,
command_stats: [],
db_size: 0
db_size: 0,
});
const cacheForm = ref<CacheForm>({
cache_name: '',
cache_key: '',
cache_value: ''
cache_name: "",
cache_key: "",
cache_value: "",
});
let commandstatsInstance: echarts.ECharts | null = null;
@@ -263,9 +346,9 @@ let usedmemoryInstance: echarts.ECharts | null = null;
const resetCacheForm = () => {
cacheKeys.value = [];
cacheForm.value = {
cache_name: '',
cache_key: '',
cache_value: ''
cache_name: "",
cache_key: "",
cache_value: "",
};
};
@@ -273,11 +356,11 @@ const resetCacheForm = () => {
const getCacheNameList = async () => {
try {
loading.value = true;
const response = await CacheAPI.getCacheNames()
const response = await CacheAPI.getCacheNames();
cacheNames.value = response.data.data;
resetCacheForm();
} catch (error) {
console.error('获取缓存列表出错:', error);
console.error("获取缓存列表出错:", error);
} finally {
loading.value = false;
}
@@ -294,7 +377,7 @@ const handleClearCacheName = async (row: CacheInfo) => {
await CacheAPI.deleteCacheName(row.cache_name);
refreshCacheNames();
} catch (error) {
console.error('清理缓存名称出错:', error);
console.error("清理缓存名称出错:", error);
}
};
@@ -310,11 +393,11 @@ const getCacheKeyList = async (row?: CacheInfo) => {
nowCacheName.value = cacheName;
cacheForm.value = {
cache_name: cacheName,
cache_key: '',
cache_value: ''
cache_key: "",
cache_value: "",
};
} catch (error) {
console.error('获取缓存键名列表出错:', error);
console.error("获取缓存键名列表出错:", error);
} finally {
subLoading.value = false;
}
@@ -331,34 +414,30 @@ async function handleClearCacheKey(cacheKey: string) {
await CacheAPI.deleteCacheKey(cacheKey);
getCacheKeyList();
} catch (error) {
console.error('清理缓存键名出错:', error);
console.error("清理缓存键名出错:", error);
}
};
}
// 缓存内容相关方法
async function handleCacheValue(cacheKey: string) {
try {
loading.value = true;
const response = await CacheAPI.getCacheValue(nowCacheName.value, cacheKey)
const response = await CacheAPI.getCacheValue(nowCacheName.value, cacheKey);
cacheForm.value = response.data.data;
} catch (error) {
console.error('获取缓存内容失败:', error);
console.error("获取缓存内容失败:", error);
} finally {
loading.value = false;
}
};
}
// 清理全部缓存
const handleClearCacheAll = async () => {
ElMessageBox.confirm(
'确定要清理全部缓存吗?',
'危险!',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
ElMessageBox.confirm("确定要清理全部缓存吗?", "危险!", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
return await CacheAPI.deleteCacheAll();
})
@@ -366,8 +445,8 @@ const handleClearCacheAll = async () => {
getCacheNameList();
})
.catch((error) => {
if (error !== 'cancel') {
console.error('清理全部缓存失败:', error);
if (error !== "cancel") {
console.error("清理全部缓存失败:", error);
}
});
};
@@ -376,15 +455,15 @@ const handleClearCacheAll = async () => {
const getInfo = async () => {
try {
loading.value = true;
const response = await CacheAPI.getCacheInfo()
const response = await CacheAPI.getCacheInfo();
cache.value = response.data.data || {
info: {},
command_stats: [],
dbSize: 0
dbSize: 0,
};
initCharts();
} catch (error) {
console.error('获取缓存监控数据失败:', error);
console.error("获取缓存监控数据失败:", error);
} finally {
loading.value = false;
}
@@ -394,44 +473,50 @@ const getInfo = async () => {
const initCharts = () => {
if (!commandstats.value || !usedmemory.value) return;
commandstatsInstance = echarts.init(commandstats.value, 'macarons');
usedmemoryInstance = echarts.init(usedmemory.value, 'macarons');
commandstatsInstance = echarts.init(commandstats.value, "macarons");
usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
const commandStatsOption = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
series: [{
name: '命令',
type: 'pie',
roseType: 'radius',
radius: [15, 95],
center: ['50%', '38%'],
data: cache.value.command_stats || [],
animationEasing: 'cubicInOut',
animationDuration: 1000
}]
series: [
{
name: "命令",
type: "pie",
roseType: "radius",
radius: [15, 95],
center: ["50%", "38%"],
data: cache.value.command_stats || [],
animationEasing: "cubicInOut",
animationDuration: 1000,
},
],
};
const usedMemory = cache.value.info?.used_memory_human || '0';
const usedMemory = cache.value.info?.used_memory_human || "0";
const usedMemoryOption = {
tooltip: {
formatter: `{b} <br/>{a} : ${usedMemory}`
formatter: `{b} <br/>{a} : ${usedMemory}`,
},
series: [{
name: '峰值',
type: 'gauge',
min: 0,
max: 1000,
detail: {
formatter: usedMemory
series: [
{
name: "峰值",
type: "gauge",
min: 0,
max: 1000,
detail: {
formatter: usedMemory,
},
data: [
{
value: parseFloat(usedMemory) || 0,
name: "内存消耗",
},
],
},
data: [{
value: parseFloat(usedMemory) || 0,
name: '内存消耗'
}]
}]
],
};
commandstatsInstance.setOption(commandStatsOption);
@@ -3,7 +3,13 @@
<div class="app-container">
<!-- 搜索区域 -->
<div class="search-container">
<el-form ref="queryFormRef" :model="queryFormData" :inline="true" label-suffix=":" @submit.prevent="handleQuery" >
<el-form
ref="queryFormRef"
:model="queryFormData"
:inline="true"
label-suffix=":"
@submit.prevent="handleQuery"
>
<el-form-item prop="ipaddr" label="IP地址">
<el-input v-model="queryFormData.ipaddr" placeholder="请输入IP地址" clearable />
</el-form-item>
@@ -15,8 +21,21 @@
</el-form-item>
<!-- 查询、重置、展开/收起按钮 -->
<el-form-item class="search-buttons">
<el-button v-hasPerm="['module_monitor:online:query']" type="primary" icon="search" native-type="submit">查询</el-button>
<el-button v-hasPerm="['module_monitor:online:query']" icon="refresh" @click="handleResetQuery">重置</el-button>
<el-button
v-hasPerm="['module_monitor:online:query']"
type="primary"
icon="search"
native-type="submit"
>
查询
</el-button>
<el-button
v-hasPerm="['module_monitor:online:query']"
icon="refresh"
@click="handleResetQuery"
>
重置
</el-button>
</el-form-item>
</el-form>
</div>
@@ -39,7 +58,14 @@
<div class="data-table__toolbar--left">
<el-row :gutter="10">
<el-col :span="1.5">
<el-button v-hasPerm="['module_monitor:online:force_logout']" type="danger" icon="delete" @click="handleClear">强退所有</el-button>
<el-button
v-hasPerm="['module_monitor:online:force_logout']"
type="danger"
icon="delete"
@click="handleClear"
>
强退所有
</el-button>
</el-col>
</el-row>
</div>
@@ -47,7 +73,13 @@
<el-row :gutter="10">
<el-col :span="1.5">
<el-tooltip content="刷新">
<el-button v-hasPerm="['module_monitor:online:refresh']" type="primary" icon="refresh" circle @click="handleRefresh"/>
<el-button
v-hasPerm="['module_monitor:online:refresh']"
type="primary"
icon="refresh"
circle
@click="handleRefresh"
/>
</el-tooltip>
</el-col>
<el-col :span="1.5">
@@ -67,29 +99,117 @@
</div>
<!-- 表格区域:系统配置列表 -->
<el-table ref="dataTableRef" v-loading="loading" :data="pageTableData" highlight-current-row class="data-table__content" :height="450" border stripe @selection-change="handleSelectionChange">
<el-table
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
class="data-table__content"
:height="450"
border
stripe
@selection-change="handleSelectionChange"
>
<template #empty>
<el-empty :image-size="80" description="暂无数据" />
</template>
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" min-width="55" align="center" />
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号" min-width="60" />
<el-table-column v-if="tableColumns.find(col => col.prop === 'session_id')?.show" key="session_id" label="会话编号" prop="session_id" min-width="250" show-overflow-tooltip/>
<el-table-column v-if="tableColumns.find(col => col.prop === 'login_type')?.show" key="login_type" label="登录类型" prop="login_type" min-width="100" />
<el-table-column v-if="tableColumns.find(col => col.prop === 'ipaddr')?.show" key="ipaddr" label="IP地址" prop="ipaddr" min-width="150" show-overflow-tooltip>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'selection')?.show"
type="selection"
min-width="55"
align="center"
/>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'index')?.show"
type="index"
fixed
label="序号"
min-width="60"
/>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'session_id')?.show"
key="session_id"
label="会话编号"
prop="session_id"
min-width="250"
show-overflow-tooltip
/>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'login_type')?.show"
key="login_type"
label="登录类型"
prop="login_type"
min-width="100"
/>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'ipaddr')?.show"
key="ipaddr"
label="IP地址"
prop="ipaddr"
min-width="150"
show-overflow-tooltip
>
<template #default="scope">
<el-text>{{ scope.row.ipaddr }}</el-text>
<CopyButton v-if="scope.row.ipaddr" :text="scope.row.ipaddr" style="margin-left: 2px" />
</template>
</el-table-column>
<el-table-column v-if="tableColumns.find(col => col.prop === 'name')?.show" key="name" label="用户名" prop="name" min-width="80" show-overflow-tooltip />
<el-table-column v-if="tableColumns.find(col => col.prop === 'user_name')?.show" key="user_name" label="账号" prop="user_name" min-width="80" />
<el-table-column v-if="tableColumns.find(col => col.prop === 'login_location')?.show" key="login_location" label="登录位置" prop="login_location" min-width="280" show-overflow-tooltip/>
<el-table-column v-if="tableColumns.find(col => col.prop === 'os')?.show" key="os" label="操作系统" prop="os" min-width="120" show-overflow-tooltip />
<el-table-column v-if="tableColumns.find(col => col.prop === 'login_time')?.show" key="login_time" label="登录时间" prop="login_time" min-width="180" />
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" key="operation" fixed="right" label="操作" min-width="100">
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'name')?.show"
key="name"
label="用户名"
prop="name"
min-width="80"
show-overflow-tooltip
/>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'user_name')?.show"
key="user_name"
label="账号"
prop="user_name"
min-width="80"
/>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'login_location')?.show"
key="login_location"
label="登录位置"
prop="login_location"
min-width="280"
show-overflow-tooltip
/>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'os')?.show"
key="os"
label="操作系统"
prop="os"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'login_time')?.show"
key="login_time"
label="登录时间"
prop="login_time"
min-width="180"
/>
<el-table-column
v-if="tableColumns.find((col) => col.prop === 'operation')?.show"
key="operation"
fixed="right"
label="操作"
min-width="100"
>
<template #default="scope">
<el-button v-hasPerm="['module_monitor:online:force_logout']" type="danger" size="small" link icon="delete" @click="handleSubmit(scope.row.session_id)">强退
<el-button
v-hasPerm="['module_monitor:online:force_logout']"
type="danger"
size="small"
link
icon="delete"
@click="handleSubmit(scope.row.session_id)"
>
强退
</el-button>
</template>
</el-table-column>
@@ -97,12 +217,15 @@
<!-- 分页区域 -->
<template #footer>
<pagination v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size" @pagination="loadingData" />
<pagination
v-model:total="total"
v-model:page="queryFormData.page_no"
v-model:limit="queryFormData.page_size"
@pagination="loadingData"
/>
</template>
</el-card>
</div>
</template>
<script lang="ts" setup>
@@ -111,7 +234,10 @@ defineOptions({
inheritAttrs: false,
});
import OnlineAPI, { type OnlineUserPageQuery, type OnlineUserTable } from "@/api/module_monitor/online";
import OnlineAPI, {
type OnlineUserPageQuery,
type OnlineUserTable,
} from "@/api/module_monitor/online";
const queryFormRef = ref();
const total = ref(0);
@@ -121,20 +247,19 @@ const loading = ref(false);
// 分页表单
const pageTableData = ref<OnlineUserTable[]>([]);
// 表格列配置
const tableColumns = ref([
{ label: '选择框', prop: 'selection', show: true },
{ label: '序号', prop: 'index', show: true },
{ label: '会话编号', prop: 'session_id', show: true },
{ label: '登录类型', prop: 'login_type', show: true },
{ label: '登录名称', prop: 'name', show: true },
{ label: '用户账号', prop: 'user_name', show: true },
{ label: '主机', prop: 'ipaddr', show: true },
{ label: '登录地点', prop: 'login_location', show: true },
{ label: '操作系统', prop: 'os', show: true },
{ label: '登录时间', prop: 'login_time', show: true },
{ label: '操作', prop: 'operation', show: true }
{ label: "选择框", prop: "selection", show: true },
{ label: "序号", prop: "index", show: true },
{ label: "会话编号", prop: "session_id", show: true },
{ label: "登录类型", prop: "login_type", show: true },
{ label: "登录名称", prop: "name", show: true },
{ label: "用户账号", prop: "user_name", show: true },
{ label: "主机", prop: "ipaddr", show: true },
{ label: "登录地点", prop: "login_location", show: true },
{ label: "操作系统", prop: "os", show: true },
{ label: "登录时间", prop: "login_time", show: true },
{ label: "操作", prop: "operation", show: true },
]);
// 分页查询参数
@@ -143,13 +268,13 @@ const queryFormData = reactive<OnlineUserPageQuery>({
page_size: 10,
name: undefined,
login_location: undefined,
ipaddr: undefined
ipaddr: undefined,
});
// 列表刷新
async function handleRefresh () {
async function handleRefresh() {
await loadingData();
};
}
// 加载表格数据
async function loadingData() {
@@ -158,11 +283,9 @@ async function loadingData() {
const response = await OnlineAPI.getOnlineList(queryFormData);
pageTableData.value = response.data.data.items;
total.value = response.data.data.total;
}
catch (error: any) {
} catch (error: any) {
console.error(error);
}
finally {
} finally {
loading.value = false;
}
}
@@ -191,19 +314,21 @@ async function handleSubmit(session_id: string) {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(async () => {
try {
loading.value = true;
await OnlineAPI.deleteOnline(session_id);
handleResetQuery();
} catch (error: any) {
console.error(error);
} finally {
loading.value = false;
}
}).catch(() => {
ElMessageBox.close();
});
})
.then(async () => {
try {
loading.value = true;
await OnlineAPI.deleteOnline(session_id);
handleResetQuery();
} catch (error: any) {
console.error(error);
} finally {
loading.value = false;
}
})
.catch(() => {
ElMessageBox.close();
});
}
// 强退所有
@@ -212,19 +337,21 @@ async function handleClear() {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(async () => {
try {
loading.value = true;
await OnlineAPI.clearOnline();
handleResetQuery();
} catch (error: any) {
console.error(error);
} finally {
loading.value = false;
}
}).catch(() => {
ElMessageBox.close();
});
})
.then(async () => {
try {
loading.value = true;
await OnlineAPI.clearOnline();
handleResetQuery();
} catch (error: any) {
console.error(error);
} finally {
loading.value = false;
}
})
.catch(() => {
ElMessageBox.close();
});
}
onMounted(() => {
loadingData();
@@ -2,7 +2,13 @@
<div class="app-container">
<!-- 搜索区域 -->
<div class="search-container">
<el-form ref="queryFormRef" :model="queryFormData" :inline="true" label-suffix=":" @submit.prevent="handleQuery">
<el-form
ref="queryFormRef"
:model="queryFormData"
:inline="true"
label-suffix=":"
@submit.prevent="handleQuery"
>
<el-form-item prop="name" label="关键词">
<el-input
v-model="queryFormData.name"
@@ -13,8 +19,21 @@
/>
</el-form-item>
<el-form-item class="search-buttons">
<el-button v-hasPerm="['module_monitor:resource:query']" type="primary" icon="search" native-type="submit">查询</el-button>
<el-button v-hasPerm="['module_monitor:resource:query']" icon="refresh" @click="handleResetQuery">重置</el-button>
<el-button
v-hasPerm="['module_monitor:resource:query']"
type="primary"
icon="search"
native-type="submit"
>
查询
</el-button>
<el-button
v-hasPerm="['module_monitor:resource:query']"
icon="refresh"
@click="handleResetQuery"
>
重置
</el-button>
</el-form-item>
</el-form>
</div>
@@ -31,7 +50,6 @@
</span>
<!-- 资源路径 -->
<div class="breadcrumb-container">
<span class="breadcrumb-label"></span>
<el-breadcrumb separator="/">
<el-breadcrumb-item
@@ -52,20 +70,46 @@
<div class="data-table__toolbar--left">
<el-row :gutter="10">
<el-col :span="1.5">
<el-button v-hasPerm="['module_monitor:resource:upload']" type="success" icon="plus" @click="handleUpload">上传文件</el-button>
<el-button
v-hasPerm="['module_monitor:resource:upload']"
type="success"
icon="plus"
@click="handleUpload"
>
上传文件
</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPerm="['module_monitor:resource:create_dir']" type="primary" icon="folder-add" @click="handleCreateDir">新建文件夹</el-button>
<el-button
v-hasPerm="['module_monitor:resource:create_dir']"
type="primary"
icon="folder-add"
@click="handleCreateDir"
>
新建文件夹
</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPerm="['module_monitor:resource:delete']" type="danger" icon="delete" :disabled="selectedItems.length === 0" @click="handleBatchDelete">批量删除</el-button>
<el-button
v-hasPerm="['module_monitor:resource:delete']"
type="danger"
icon="delete"
:disabled="selectedItems.length === 0"
@click="handleBatchDelete"
>
批量删除
</el-button>
</el-col>
</el-row>
</div>
<div class="data-table__toolbar--right">
<el-row :gutter="10">
<el-col :span="1.5">
<el-checkbox v-model="showHiddenFiles" v-hasPerm="['module_monitor:resource:show_hidden']" @change="handleShowHiddenChange">
<el-checkbox
v-model="showHiddenFiles"
v-hasPerm="['module_monitor:resource:show_hidden']"
@change="handleShowHiddenChange"
>
显示隐藏文件
</el-checkbox>
</el-col>
@@ -89,7 +133,13 @@
</el-col>
<el-col :span="1.5">
<el-tooltip content="刷新">
<el-button v-hasPerm="['module_monitor:resource:refresh']" type="primary" icon="refresh" circle @click="handleRefresh"/>
<el-button
v-hasPerm="['module_monitor:resource:refresh']"
type="primary"
icon="refresh"
circle
@click="handleRefresh"
/>
</el-tooltip>
</el-col>
</el-row>
@@ -121,7 +171,7 @@
<Folder v-if="row.is_dir" />
<Document v-else />
</el-icon>
<span
<span
v-hasPerm="['module_monitor:resource:preview']"
:class="{ 'file-name-clickable': true }"
@click="handleFileNameClick(row)"
@@ -137,7 +187,13 @@
</template>
</el-table-column>
<el-table-column label="修改时间" prop="modified_time" min-width="180" sortable />
<el-table-column fixed="right" label="操作" align="center" min-width="200" class="search-buttons">
<el-table-column
fixed="right"
label="操作"
align="center"
min-width="200"
class="search-buttons"
>
<template #default="{ row }">
<el-button
v-if="!row.is_dir"
@@ -150,10 +206,24 @@
>
下载
</el-button>
<el-button v-hasPerm="['module_monitor:resource:rename']" type="primary" size="small" link icon="edit" @click="handleRename(row)">
<el-button
v-hasPerm="['module_monitor:resource:rename']"
type="primary"
size="small"
link
icon="edit"
@click="handleRename(row)"
>
重命名
</el-button>
<el-button v-hasPerm="['module_monitor:resource:delete']" type="danger" size="small" link icon="delete" @click="handleDelete(row)">
<el-button
v-hasPerm="['module_monitor:resource:delete']"
type="danger"
size="small"
link
icon="delete"
@click="handleDelete(row)"
>
删除
</el-button>
</template>
@@ -179,9 +249,9 @@
<div class="item-name">{{ item.name }}</div>
<div v-if="!item.is_dir" class="item-size">
{{ formatFileSize(item.size) }}
</div>
</div>
</div>
</div>
<!-- 分页区域 -->
<template #footer>
@@ -211,28 +281,30 @@
>
<el-icon class="el-icon--upload"><UploadFilled /></el-icon>
<div class="el-upload__text">
将文件拖到此处,或<em>点击上传</em>
将文件拖到此处,或
<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip" style="color: red;">
<div class="el-upload__tip" style="color: red">
不支持多文件上传,单个文件不超过100MB,多文件上传,取最后一个文件上传
</div>
</template>
</el-upload>
<template #footer>
<el-button @click="uploadDialogVisible = false">取消</el-button>
<el-button v-hasPerm="['module_monitor:resource:upload']" type="primary" :loading="uploading" @click="handleUploadConfirm">
<el-button
v-hasPerm="['module_monitor:resource:upload']"
type="primary"
:loading="uploading"
@click="handleUploadConfirm"
>
确定上传
</el-button>
</template>
</el-dialog>
<!-- 新建文件夹对话框 -->
<el-dialog
v-model="createDirDialogVisible"
title="新建文件夹"
width="400px"
>
<el-dialog v-model="createDirDialogVisible" title="新建文件夹" width="400px">
<el-form :model="createDirForm" label-width="80px">
<el-form-item label="文件夹名" required>
<el-input
@@ -244,16 +316,18 @@
</el-form>
<template #footer>
<el-button @click="createDirDialogVisible = false">取消</el-button>
<el-button v-hasPerm="['module_monitor:resource:create_dir']" type="primary" @click="handleCreateDirConfirm">确定</el-button>
<el-button
v-hasPerm="['module_monitor:resource:create_dir']"
type="primary"
@click="handleCreateDirConfirm"
>
确定
</el-button>
</template>
</el-dialog>
<!-- 重命名对话框 -->
<el-dialog
v-model="renameDialogVisible"
title="重命名"
width="400px"
>
<el-dialog v-model="renameDialogVisible" title="重命名" width="400px">
<el-form :model="renameForm" label-width="80px">
<el-form-item label="新名称" required>
<el-input
@@ -265,7 +339,13 @@
</el-form>
<template #footer>
<el-button @click="renameDialogVisible = false">取消</el-button>
<el-button v-hasPerm="['module_monitor:resource:rename']" type="primary" @click="handleRenameConfirm">确定</el-button>
<el-button
v-hasPerm="['module_monitor:resource:rename']"
type="primary"
@click="handleRenameConfirm"
>
确定
</el-button>
</template>
</el-dialog>
</div>
@@ -277,66 +357,70 @@ defineOptions({
inheritAttrs: false,
});
import { ref, reactive, onMounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {
List,
Grid,
Folder,
Document,
UploadFilled,
QuestionFilled
} from '@element-plus/icons-vue'
import { ResourceAPI, type ResourceItem, type ResourcePageQuery } from '@/api/module_monitor/resource'
QuestionFilled,
} from "@element-plus/icons-vue";
import {
ResourceAPI,
type ResourceItem,
type ResourcePageQuery,
} from "@/api/module_monitor/resource";
// 分页表单
const fileList = ref<ResourceItem[]>([])
const fileList = ref<ResourceItem[]>([]);
// 响应式数据
const loading = ref(false)
const selectedItems = ref<ResourceItem[]>([])
const breadcrumbList = ref([{ name: '资源根目录', path: '/' }])
const showHiddenFiles = ref(false)
const viewMode = ref<'list' | 'grid'>('list')
const total = ref(0)
const isSearchMode = ref(false) // 用于标记是否处于搜索模式
const queryFormRef = ref()
const loading = ref(false);
const selectedItems = ref<ResourceItem[]>([]);
const breadcrumbList = ref([{ name: "资源根目录", path: "/" }]);
const showHiddenFiles = ref(false);
const viewMode = ref<"list" | "grid">("list");
const total = ref(0);
const isSearchMode = ref(false); // 用于标记是否处于搜索模式
const queryFormRef = ref();
// 路径相关数据
const currentPath = ref('/') // 当前路径的响应式变量
const currentPath = ref("/"); // 当前路径的响应式变量
// 分页查询参数
const pagination = reactive({
page_no: 1,
page_size: 10
})
page_size: 10,
});
// 搜索表单数据
const queryFormData = reactive<ResourcePageQuery>({
name: undefined,
page_no: 1,
page_size: 10
})
page_size: 10,
});
// 对话框状态
const uploadDialogVisible = ref(false)
const createDirDialogVisible = ref(false)
const renameDialogVisible = ref(false)
const uploading = ref(false)
const uploadDialogVisible = ref(false);
const createDirDialogVisible = ref(false);
const renameDialogVisible = ref(false);
const uploading = ref(false);
// 上传相关
const uploadRef = ref()
const uploadFileList = ref<any[]>([])
const uploadRef = ref();
const uploadFileList = ref<any[]>([]);
// 表单数据
const createDirForm = reactive({
dir_name: ''
})
dir_name: "",
});
const renameForm = reactive({
new_name: '',
old_path: ''
})
new_name: "",
old_path: "",
});
// 计算属性
const currentQuery = computed(() => {
@@ -344,80 +428,80 @@ const currentQuery = computed(() => {
const query: any = {
include_hidden: showHiddenFiles.value,
page_no: pagination.page_no,
page_size: pagination.page_size
}
page_size: pagination.page_size,
};
// 如果当前路径不是根路径,则添加路径参数
if (currentPath.value && currentPath.value !== '/') {
if (currentPath.value && currentPath.value !== "/") {
// 对于文件夹导航,直接传递文件夹名称
query.path = currentPath.value
query.path = currentPath.value;
}
// 添加搜索关键词
if (queryFormData.name) {
query.name = queryFormData.name
query.name = queryFormData.name;
}
return query
})
return query;
});
// 加载文件列表
async function loadFileList() {
loading.value = true
loading.value = true;
try {
const response = await ResourceAPI.getResourceList(currentQuery.value)
const response = await ResourceAPI.getResourceList(currentQuery.value);
// 正确处理后端返回的分页数据结构
const pageResult = response.data?.data
const pageResult = response.data?.data;
if (pageResult && Array.isArray(pageResult.items)) {
fileList.value = pageResult.items
total.value = pageResult.total
fileList.value = pageResult.items;
total.value = pageResult.total;
if (pageResult.page_no !== undefined) {
pagination.page_no = pageResult.page_no
pagination.page_no = pageResult.page_no;
}
if (pageResult.page_size !== undefined) {
pagination.page_size = pageResult.page_size
pagination.page_size = pageResult.page_size;
}
} else {
fileList.value = []
total.value = 0
fileList.value = [];
total.value = 0;
}
} catch (error) {
console.error('Load file list error:', error)
fileList.value = []
total.value = 0
console.error("Load file list error:", error);
fileList.value = [];
total.value = 0;
} finally {
loading.value = false
loading.value = false;
}
}
// 面包屑点击处理
function handleBreadcrumbClick(item: any) {
// 更新当前路径为点击的面包屑项路径
currentPath.value = item.path
updateBreadcrumb()
loadFileList()
currentPath.value = item.path;
updateBreadcrumb();
loadFileList();
}
// 更新面包屑路径
function updateBreadcrumb() {
// 对于根路径,直接显示根目录
if (currentPath.value === '/') {
breadcrumbList.value = [{ name: '资源根目录', path: '/' }]
return
if (currentPath.value === "/") {
breadcrumbList.value = [{ name: "资源根目录", path: "/" }];
return;
}
// 对于嵌套路径,需要分解并构建面包屑
const parts = currentPath.value.split('/').filter(part => part !== '')
const parts = currentPath.value.split("/").filter((part) => part !== "");
breadcrumbList.value = [
{ name: '资源根目录', path: '/' },
{ name: "资源根目录", path: "/" },
...parts.map((part, index) => ({
name: part,
path: parts.slice(0, index + 1).join('/')
}))
]
path: parts.slice(0, index + 1).join("/"),
})),
];
}
// 文件名点击处理
@@ -425,16 +509,16 @@ function handleFileNameClick(row: ResourceItem) {
if (row.is_dir) {
// 如果当前在根路径,则直接使用文件夹名称
// 如果当前已在某个文件夹中,则拼接路径
if (currentPath.value === '/') {
currentPath.value = row.name
if (currentPath.value === "/") {
currentPath.value = row.name;
} else {
currentPath.value = currentPath.value + '/' + row.name
currentPath.value = currentPath.value + "/" + row.name;
}
updateBreadcrumb()
loadFileList()
updateBreadcrumb();
loadFileList();
} else {
// 文件预览,使用后端返回的完整URL
handleFilePreview(row)
handleFilePreview(row);
}
}
@@ -443,259 +527,255 @@ function handleItemClick(item: ResourceItem) {
if (item.is_dir) {
// 如果当前在根路径,则直接使用文件夹名称
// 如果当前已在某个文件夹中,则拼接路径
if (currentPath.value === '/') {
currentPath.value = item.name
if (currentPath.value === "/") {
currentPath.value = item.name;
} else {
currentPath.value = currentPath.value + '/' + item.name
currentPath.value = currentPath.value + "/" + item.name;
}
updateBreadcrumb()
loadFileList()
updateBreadcrumb();
loadFileList();
} else {
// 文件预览,使用后端返回的完整URL
handleFilePreview(item)
handleFilePreview(item);
}
}
// 文件预览
function handleFilePreview(file: ResourceItem) {
// 直接使用file_url字段进行预览
let previewUrl = file.file_url
let previewUrl = file.file_url;
// 如果是相对路径,构建完整URL
if (previewUrl && !previewUrl.startsWith('http')) {
previewUrl = `${window.location.origin}${previewUrl}`
if (previewUrl && !previewUrl.startsWith("http")) {
previewUrl = `${window.location.origin}${previewUrl}`;
}
// 所有文件类型统一直接打开预览
window.open(previewUrl, '_blank')
window.open(previewUrl, "_blank");
}
// 选择项变化处理
function handleSelectionChange(selection: ResourceItem[]) {
selectedItems.value = selection
selectedItems.value = selection;
}
// 打开上传对话框
function handleUpload() {
uploadDialogVisible.value = true
uploadFileList.value = []
uploadDialogVisible.value = true;
uploadFileList.value = [];
}
// 上传文件变化处理
function handleUploadChange(file: any, fileList: any[]) {
uploadFileList.value = fileList
uploadFileList.value = fileList;
}
// 确认上传文件
async function handleUploadConfirm() {
if (uploadFileList.value.length === 0) {
ElMessage.warning('请选择要上传的文件')
return
ElMessage.warning("请选择要上传的文件");
return;
}
try {
uploading.value = true
const formData = new FormData()
uploading.value = true;
const formData = new FormData();
uploadFileList.value.forEach((file: any) => {
formData.append('file', file.raw)
})
// 在根目录时传递空字符串作为target_path,与后端relative_path格式保持一致
const targetPath = currentPath.value === '/' ? '' : currentPath.value
formData.append('target_path', targetPath)
formData.append("file", file.raw);
});
await ResourceAPI.uploadFile(formData)
uploadDialogVisible.value = false
loadFileList()
// 在根目录时传递空字符串作为target_path,与后端relative_path格式保持一致
const targetPath = currentPath.value === "/" ? "" : currentPath.value;
formData.append("target_path", targetPath);
await ResourceAPI.uploadFile(formData);
uploadDialogVisible.value = false;
loadFileList();
} catch (error) {
console.error('Upload error:', error)
console.error("Upload error:", error);
} finally {
uploading.value = false
uploading.value = false;
}
}
// 关闭上传对话框
function handleUploadClose() {
uploadDialogVisible.value = false
uploadFileList.value = []
uploadDialogVisible.value = false;
uploadFileList.value = [];
}
// 打开新建目录对话框
function handleCreateDir() {
createDirForm.dir_name = ''
createDirDialogVisible.value = true
createDirForm.dir_name = "";
createDirDialogVisible.value = true;
}
// 确认创建目录
async function handleCreateDirConfirm() {
if (!createDirForm.dir_name.trim()) {
ElMessage.warning('请输入文件夹名称')
return
ElMessage.warning("请输入文件夹名称");
return;
}
try {
// 在根目录时传递空字符串作为parent_path,与后端relative_path格式保持一致
const parentPath = currentPath.value === '/' ? '' : currentPath.value
const parentPath = currentPath.value === "/" ? "" : currentPath.value;
await ResourceAPI.createDirectory({
parent_path: parentPath,
dir_name: createDirForm.dir_name.trim()
})
createDirDialogVisible.value = false
loadFileList()
dir_name: createDirForm.dir_name.trim(),
});
createDirDialogVisible.value = false;
loadFileList();
} catch (error) {
console.error('Create directory error:', error)
console.error("Create directory error:", error);
}
}
// 列表刷新
async function handleRefresh() {
await loadFileList()
await loadFileList();
}
// 查询(重置页码后获取数据)
async function handleQuery() {
queryFormData.page_no = 1
await loadFileList()
queryFormData.page_no = 1;
await loadFileList();
}
// 重置查询
async function handleResetQuery() {
queryFormRef.value.resetFields()
queryFormData.page_no = 1
isSearchMode.value = false // 退出搜索模式
await loadFileList()
queryFormRef.value.resetFields();
queryFormData.page_no = 1;
isSearchMode.value = false; // 退出搜索模式
await loadFileList();
}
// 显示隐藏文件切换
function handleShowHiddenChange() {
loadFileList()
loadFileList();
}
// 文件下载
async function handleDownload(item: ResourceItem) {
try {
// 使用file_url字段
const response = await ResourceAPI.downloadFile(item.file_url)
const blob = response.data
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = item.name
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
window.URL.revokeObjectURL(url)
const response = await ResourceAPI.downloadFile(item.file_url);
const blob = response.data;
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = item.name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('Download error:', error)
console.error("Download error:", error);
}
}
// 打开重命名对话框
function handleRename(item: ResourceItem) {
// 使用file_url字段
renameForm.old_path = item.file_url
renameForm.new_name = item.name
renameDialogVisible.value = true
renameForm.old_path = item.file_url;
renameForm.new_name = item.name;
renameDialogVisible.value = true;
}
// 确认重命名
async function handleRenameConfirm() {
if (!renameForm.new_name.trim()) {
ElMessage.warning('请输入新名称')
return
ElMessage.warning("请输入新名称");
return;
}
try {
await ResourceAPI.renameResource({
old_path: renameForm.old_path,
new_name: renameForm.new_name.trim()
})
renameDialogVisible.value = false
loadFileList()
new_name: renameForm.new_name.trim(),
});
renameDialogVisible.value = false;
loadFileList();
} catch (error) {
console.error('Rename error:', error)
console.error("Rename error:", error);
}
}
// 文件删除
async function handleDelete(item: ResourceItem) {
try {
await ElMessageBox.confirm(
`确定要删除 ${item.name} 吗?`,
'确认删除',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
await ElMessageBox.confirm(`确定要删除 ${item.name} 吗?`, "确认删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
// 使用file_url字段
await ResourceAPI.deleteResource([item.file_url])
loadFileList()
} catch (error) {
if (error !== 'cancel') {
console.error('Delete error:', error)
}
await ResourceAPI.deleteResource([item.file_url]);
loadFileList();
} catch (error) {
if (error !== "cancel") {
console.error("Delete error:", error);
}
}
}
// 分页处理
function handlePagination(params: { page: number; limit: number }) {
pagination.page_no = params.page
pagination.page_size = params.limit
loadFileList()
pagination.page_no = params.page;
pagination.page_size = params.limit;
loadFileList();
}
// 批量删除
async function handleBatchDelete() {
if (selectedItems.value.length === 0) {
ElMessage.warning('请选择要删除的文件')
return
ElMessage.warning("请选择要删除的文件");
return;
}
try {
await ElMessageBox.confirm(
`确定要删除选中的 ${selectedItems.value.length} 个文件吗?`,
'确认删除',
"确认删除",
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
)
);
// 使用file_url字段
const paths = selectedItems.value.map(item => item.file_url)
await ResourceAPI.deleteResource(paths)
loadFileList()
const paths = selectedItems.value.map((item) => item.file_url);
await ResourceAPI.deleteResource(paths);
loadFileList();
} catch (error) {
if (error !== 'cancel') {
console.error('Batch delete error:', error)
if (error !== "cancel") {
console.error("Batch delete error:", error);
}
}
}
// 格式化文件大小
function formatFileSize(size?: number | null) {
if (!size || size === null) return '-'
const units = ['B', 'KB', 'MB', 'GB', 'TB']
let unitIndex = 0
let fileSize = size
if (!size || size === null) return "-";
const units = ["B", "KB", "MB", "GB", "TB"];
let unitIndex = 0;
let fileSize = size;
while (fileSize >= 1024 && unitIndex < units.length - 1) {
fileSize /= 1024
unitIndex++
fileSize /= 1024;
unitIndex++;
}
return `${fileSize.toFixed(1)} ${units[unitIndex]}`
return `${fileSize.toFixed(1)} ${units[unitIndex]}`;
}
// 生命周期
onMounted(() => {
loadFileList()
})
loadFileList();
});
</script>
<style lang="scss" scoped>
@@ -713,7 +793,7 @@ onMounted(() => {
.file-name-clickable {
cursor: pointer;
color: var(--el-color-primary);
&:hover {
color: var(--el-color-primary-light-3);
text-decoration: underline;
@@ -727,7 +807,7 @@ onMounted(() => {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
height: calc(100vh - 200px);
gap: 20px;
.grid-item {
display: flex;
flex-direction: column;
@@ -750,7 +830,7 @@ onMounted(() => {
&.is-link {
cursor: pointer;
color: var(--el-color-primary);
&:hover {
color: var(--el-color-primary-light-3);
}
+136 -107
View File
@@ -1,11 +1,11 @@
<template>
<div class="app-container" >
<div class="app-container">
<el-row :gutter="16">
<el-col :span="12" class="mb-4">
<el-card :loading="loading" shadow="hover">
<el-card :loading="loading" shadow="hover">
<template #header>
<div class="flex items-center gap-2">
<el-icon><Cpu/></el-icon>
<el-icon><Cpu /></el-icon>
<span class="flex items-center gap-2">CPU使用情况</span>
<el-tooltip content="展示CPU核心数及使用率">
<el-icon><QuestionFilled /></el-icon>
@@ -19,18 +19,23 @@
<span>核心数</span>
<el-tooltip :content="(server.cpu?.cpu_num || 0).toFixed(1)">
<div class="text-center mb-4">
<el-progress
<el-progress
type="circle"
:percentage="100"
:format="() => `${server.cpu?.cpu_num || 0}`"
:format="() => `${server.cpu?.cpu_num || 0}`"
/>
</div>
</el-tooltip>
<el-descriptions :column="1" border>
<el-descriptions-item label="总核心数" >{{server.cpu?.cpu_num || 0}}</el-descriptions-item>
<el-descriptions-item label="已用核心" >{{Math.floor((server.cpu?.used || 0) * server.cpu?.cpu_num / 100)}}</el-descriptions-item>
<el-descriptions-item label="空闲核心" >{{Math.floor((server.cpu?.free || 0) * server.cpu?.cpu_num / 100)}}</el-descriptions-item>
<el-descriptions-item label="总核心数">
{{ server.cpu?.cpu_num || 0 }}
</el-descriptions-item>
<el-descriptions-item label="已用核心">
{{ Math.floor(((server.cpu?.used || 0) * server.cpu?.cpu_num) / 100) }}
</el-descriptions-item>
<el-descriptions-item label="空闲核心">
{{ Math.floor(((server.cpu?.free || 0) * server.cpu?.cpu_num) / 100) }}
</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
@@ -40,17 +45,29 @@
<span>使用率</span>
<el-tooltip :content="(server.cpu?.used || 0).toFixed(1) + '%'">
<div class="text-center mb-4">
<el-progress
type="circle"
:percentage="server.cpu?.used || 0"
:status="server.cpu?.used > 80 ? 'exception' : server.cpu?.used > 60 ? 'warning' : 'success'"
<el-progress
type="circle"
:percentage="server.cpu?.used || 0"
:status="
server.cpu?.used > 80
? 'exception'
: server.cpu?.used > 60
? 'warning'
: 'success'
"
/>
</div>
</el-tooltip>
<el-descriptions :column="1" border>
<el-descriptions-item label="用户使用率" >{{(server.cpu?.used || 0).toFixed(1) + '%'}}</el-descriptions-item>
<el-descriptions-item label="系统使用率" >{{(server.cpu?.sys || 0).toFixed(1) + '%'}}</el-descriptions-item>
<el-descriptions-item label="当前空闲率" >{{(server.cpu?.free || 0).toFixed(1) + '%'}}</el-descriptions-item>
<el-descriptions-item label="用户使用率">
{{ (server.cpu?.used || 0).toFixed(1) + "%" }}
</el-descriptions-item>
<el-descriptions-item label="系统使用率">
{{ (server.cpu?.sys || 0).toFixed(1) + "%" }}
</el-descriptions-item>
<el-descriptions-item label="当前空闲率">
{{ (server.cpu?.free || 0).toFixed(1) + "%" }}
</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
@@ -59,7 +76,7 @@
</el-col>
<el-col :span="12" class="mb-4">
<el-card :loading="loading" shadow="hover">
<el-card :loading="loading" shadow="hover">
<template #header>
<div class="flex items-center gap-2">
<el-icon><Memo /></el-icon>
@@ -76,17 +93,29 @@
<span>系统内存</span>
<el-tooltip :content="(server.mem?.usage || 0).toFixed(1) + '%'">
<div class="text-center mb-4">
<el-progress
type="circle"
:percentage="server.mem?.usage || 0"
:status="server.mem?.usage > 80 ? 'exception' : server.mem?.usage > 60 ? 'warning' : 'success'"
<el-progress
type="circle"
:percentage="server.mem?.usage || 0"
:status="
server.mem?.usage > 80
? 'exception'
: server.mem?.usage > 60
? 'warning'
: 'success'
"
/>
</div>
</el-tooltip>
<el-descriptions :column="1" border>
<el-descriptions-item label="总内存" >{{server.mem?.total}}</el-descriptions-item>
<el-descriptions-item label="已用内存" >{{server.mem?.used}}</el-descriptions-item>
<el-descriptions-item label="空闲内存" >{{server.mem?.free}}</el-descriptions-item>
<el-descriptions-item label="总内存">
{{ server.mem?.total }}
</el-descriptions-item>
<el-descriptions-item label="已用内存">
{{ server.mem?.used }}
</el-descriptions-item>
<el-descriptions-item label="空闲内存">
{{ server.mem?.free }}
</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
@@ -96,17 +125,29 @@
<span>Python内存</span>
<el-tooltip :content="(server.py?.memory_usage || 0).toFixed(1) + '%'">
<div class="text-center mb-4">
<el-progress
type="circle"
:percentage="server.py?.memory_usage || 0"
:status="server.py?.memory_usage > 80 ? 'exception' : server.py?.memory_usage > 60 ? 'warning' : 'success'"
<el-progress
type="circle"
:percentage="server.py?.memory_usage || 0"
:status="
server.py?.memory_usage > 80
? 'exception'
: server.py?.memory_usage > 60
? 'warning'
: 'success'
"
/>
</div>
</el-tooltip>
<el-descriptions :column="1" border>
<el-descriptions-item label="总内存" >{{server.py?.memory_total}}</el-descriptions-item>
<el-descriptions-item label="已用内存" >{{server.py?.memory_used}}</el-descriptions-item>
<el-descriptions-item label="空闲内存" >{{server.py?.memory_free}}</el-descriptions-item>
<el-descriptions-item label="总内存">
{{ server.py?.memory_total }}
</el-descriptions-item>
<el-descriptions-item label="已用内存">
{{ server.py?.memory_used }}
</el-descriptions-item>
<el-descriptions-item label="空闲内存">
{{ server.py?.memory_free }}
</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
@@ -115,7 +156,7 @@
</el-col>
<el-col :span="24" class="mb-4">
<el-card :loading="loading" >
<el-card :loading="loading">
<template #header>
<div class="flex items-center gap-2">
<el-icon><Monitor /></el-icon>
@@ -126,16 +167,24 @@
</div>
</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="服务器名称">{{ server.sys?.computer_name || '-' }}</el-descriptions-item>
<el-descriptions-item label="操作系统">{{ server.sys?.os_name || '-' }}</el-descriptions-item>
<el-descriptions-item label="服务器IP">{{ server.sys?.computer_ip || '-' }}</el-descriptions-item>
<el-descriptions-item label="系统架构">{{ server.sys?.os_arch || '-' }}</el-descriptions-item>
<el-descriptions-item label="服务器名称">
{{ server.sys?.computer_name || "-" }}
</el-descriptions-item>
<el-descriptions-item label="操作系统">
{{ server.sys?.os_name || "-" }}
</el-descriptions-item>
<el-descriptions-item label="服务器IP">
{{ server.sys?.computer_ip || "-" }}
</el-descriptions-item>
<el-descriptions-item label="系统架构">
{{ server.sys?.os_arch || "-" }}
</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
<el-col :span="24" class="mb-4">
<el-card :loading="loading" class="shadow-sm">
<el-card :loading="loading" class="shadow-sm">
<template #header>
<div class="flex items-center gap-2">
<el-icon><Dish /></el-icon>
@@ -146,12 +195,24 @@
</div>
</template>
<el-descriptions :column="3" border>
<el-descriptions-item label="Python名称">{{ server.py?.name || '-' }}</el-descriptions-item>
<el-descriptions-item label="Python版本">{{ server.py?.version || '-' }}</el-descriptions-item>
<el-descriptions-item label="启动时间">{{ server.py?.start_time || '-' }}</el-descriptions-item>
<el-descriptions-item label="运行时长">{{ server.py?.run_time || '-' }}</el-descriptions-item>
<el-descriptions-item label="安装路径">{{ server.py?.home || '-' }}</el-descriptions-item>
<el-descriptions-item label="项目路径">{{ server.sys?.user_dir || '-' }}</el-descriptions-item>
<el-descriptions-item label="Python名称">
{{ server.py?.name || "-" }}
</el-descriptions-item>
<el-descriptions-item label="Python版本">
{{ server.py?.version || "-" }}
</el-descriptions-item>
<el-descriptions-item label="启动时间">
{{ server.py?.start_time || "-" }}
</el-descriptions-item>
<el-descriptions-item label="运行时长">
{{ server.py?.run_time || "-" }}
</el-descriptions-item>
<el-descriptions-item label="安装路径">
{{ server.py?.home || "-" }}
</el-descriptions-item>
<el-descriptions-item label="项目路径">
{{ server.sys?.user_dir || "-" }}
</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
@@ -169,35 +230,19 @@
</el-tooltip>
</div>
</template>
<el-table :data="server.disks" border >
<el-table :data="server.disks" border>
<template #empty>
<el-empty :image-size="80" description="暂无数据" />
</template>
<el-table-column
label="盘符路径"
prop="dir_name"
:show-overflow-tooltip="true"
/>
<el-table-column
label="文件系统"
prop="sys_type_name"
align="center"
width="100"
/>
<el-table-column
label="盘符名称"
prop="type_name"
/>
<el-table-column
prop="usage"
label="使用率"
align="center"
>
<el-table-column label="盘符路径" prop="dir_name" :show-overflow-tooltip="true" />
<el-table-column label="文件系统" prop="sys_type_name" align="center" width="100" />
<el-table-column label="盘符名称" prop="type_name" />
<el-table-column prop="usage" label="使用率" align="center">
<template #default="{ row }">
<!-- 使用 element-plus 的 Progress 组件 -->
<div>
<el-progress
:percentage="Number(row.usage)"
<el-progress
:percentage="Number(row.usage)"
:status="row.usage > 80 ? 'exception' : row.usage > 60 ? 'warning' : 'success'"
:text-inside="true"
:stroke-width="16"
@@ -205,24 +250,9 @@
</div>
</template>
</el-table-column>
<el-table-column
label="总大小"
prop="total"
align="center"
width="100"
/>
<el-table-column
label="可用大小"
prop="free"
align="center"
width="100"
/>
<el-table-column
label="已用大小"
prop="used"
align="center"
width="100"
/>
<el-table-column label="总大小" prop="total" align="center" width="100" />
<el-table-column label="可用大小" prop="free" align="center" width="100" />
<el-table-column label="已用大小" prop="used" align="center" width="100" />
</el-table>
</el-card>
</el-col>
@@ -231,8 +261,7 @@
</template>
<script lang="ts" setup>
import ServerAPI, {type ServerInfo } from '@/api/module_monitor/server'
import ServerAPI, { type ServerInfo } from "@/api/module_monitor/server";
const loading = ref(false);
const server = ref<ServerInfo>({
@@ -240,33 +269,33 @@ const server = ref<ServerInfo>({
cpu_num: 0,
used: 0,
sys: 0,
free: 0
free: 0,
},
mem: {
total: '',
used: '',
free: '',
usage: 0
total: "",
used: "",
free: "",
usage: 0,
},
sys: {
computer_name: '',
os_name: '',
computer_ip: '',
os_arch: '',
user_dir: ''
computer_name: "",
os_name: "",
computer_ip: "",
os_arch: "",
user_dir: "",
},
py: {
name: '',
version: '',
start_time: '',
run_time: '',
home: '',
memory_total: '',
memory_used: '',
memory_free: '',
memory_usage: 0
name: "",
version: "",
start_time: "",
run_time: "",
home: "",
memory_total: "",
memory_used: "",
memory_free: "",
memory_usage: 0,
},
disks: []
disks: [],
});
async function getList() {
@@ -275,7 +304,7 @@ async function getList() {
const response = await ServerAPI.getServer();
server.value = response.data.data;
} catch (error) {
console.error('获取服务器信息失败:', error);
console.error("获取服务器信息失败:", error);
} finally {
loading.value = false;
}