114 lines
3.4 KiB
Vue
114 lines
3.4 KiB
Vue
<template>
|
|
<div class="server-partitions-detail">
|
|
<div v-if="isLoading && partitions.length < 1" class="loading-wrap">
|
|
<t-loading text="加载磁盘详情..." />
|
|
</div>
|
|
<template v-else-if="partitions.length">
|
|
<t-cell-group
|
|
v-for="item in partitions"
|
|
:key="item.uuid"
|
|
:title="resolvePartitionTitle(item)"
|
|
theme="card"
|
|
>
|
|
<t-cell title="磁盘名称" :note="Text.display(item.diskName)" />
|
|
<t-cell-info label="磁盘型号" :value="Text.display(item.diskModel)" />
|
|
<t-cell-info label="磁盘序列号" :value="Text.display(item.diskSerial)" />
|
|
<t-cell title="分区 ID" :note="Text.display(item.partitionId)" />
|
|
<t-cell-info label="分区名称" :value="Text.display(item.partitionName)" />
|
|
<t-cell title="分区类型" :note="Text.display(item.partitionType)" />
|
|
<t-cell-info label="UUID" :value="Text.display(item.uuid)" />
|
|
<t-cell title="总容量" :note="IOSize.format(item.total)" />
|
|
<t-cell title="已使用" :note="IOSize.format(item.used)" />
|
|
<t-cell title="可用容量" :note="IOSize.format(item.total - item.used)" />
|
|
<t-cell title="使用率" :note="Text.unit(item.used / item.total * 100, '%')" />
|
|
<t-cell
|
|
v-if="typeof item.transferTimeMs === 'number'"
|
|
title="传输耗时"
|
|
:note="Text.unit(item.transferTimeMs, '毫秒')"
|
|
/>
|
|
<t-cell v-if="item.healthStatus" title="健康状态" :note="item.healthStatus" />
|
|
</t-cell-group>
|
|
</template>
|
|
<t-empty v-else description="暂无磁盘详情数据" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { Toast } from "tdesign-mobile-vue";
|
|
import SystemAPI from "@/api/SystemAPI";
|
|
import type { SystemStatusSnapshot, SystemStatusSnapshotView } from "@/types/System";
|
|
import { IOSize, Text } from "timi-web";
|
|
|
|
type Partition = NonNullable<SystemStatusSnapshot["storagePartitions"]>[number];
|
|
|
|
defineOptions({
|
|
name: "ServerPartitionsDetail"
|
|
});
|
|
|
|
const isLoading = ref(false);
|
|
const snapshotView = ref<SystemStatusSnapshotView | null>(null);
|
|
const refreshIntervalMs = 3000;
|
|
let refreshTimer: ReturnType<typeof setInterval> | null = null;
|
|
|
|
const partitions = computed<Partition[]>(() => {
|
|
const list = snapshotView.value?.snapshot?.storagePartitions || [];
|
|
return list.slice().sort((left, right) => {
|
|
return (right.used / Math.max(right.total, 1)) - (left.used / Math.max(left.total, 1));
|
|
});
|
|
});
|
|
|
|
onMounted(() => {
|
|
void refreshDetail();
|
|
refreshTimer = setInterval(() => {
|
|
void refreshDetail();
|
|
}, refreshIntervalMs);
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
if (!refreshTimer) {
|
|
return;
|
|
}
|
|
clearInterval(refreshTimer);
|
|
refreshTimer = null;
|
|
});
|
|
|
|
function resolvePartitionTitle(item: Partition): string {
|
|
const mountPoint = Text.display(item.mountPoint);
|
|
const partitionId = Text.display(item.partitionId);
|
|
if (partitionId !== "-") {
|
|
return `${mountPoint} (${partitionId})`;
|
|
}
|
|
return mountPoint;
|
|
}
|
|
|
|
async function refreshDetail(): Promise<void> {
|
|
if (isLoading.value) {
|
|
return;
|
|
}
|
|
isLoading.value = true;
|
|
try {
|
|
snapshotView.value = await SystemAPI.getStatus("storage");
|
|
} catch (error) {
|
|
Toast({
|
|
theme: "error",
|
|
message: error instanceof Error ? error.message : "请求失败,请稍后重试"
|
|
});
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.server-partitions-detail {
|
|
padding: var(--app-nav-offset) 0 1rem 0;
|
|
|
|
.loading-wrap {
|
|
display: flex;
|
|
min-height: 4rem;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
}
|
|
</style>
|