Files
filefast/frontend/src/components/AdminPanel.vue

222 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import GlassCard from './GlassCard.vue'
import LocalIcon from './LocalIcon.vue'
defineProps({
stats: {
type: Array,
required: true,
},
records: {
type: Array,
required: true,
},
fileLimit: {
type: Number,
required: true,
},
minioCapacity: {
type: Number,
required: true,
},
})
defineEmits(['exit', 'save-config', 'update:file-limit', 'update:minio-capacity'])
function formatLimitValue(limitMb) {
const value = Number(limitMb) || 0
if (value >= 1024) {
return `${(value / 1024).toFixed(value % 1024 === 0 ? 0 : 1)} GB`
}
return `${value} MB`
}
function formatCapacityValue(capacityGb) {
const value = Number(capacityGb) || 0
if (value >= 1024) {
return `${(value / 1024).toFixed(value % 1024 === 0 ? 0 : 1)} TB`
}
return `${value} GB`
}
function getStatStyle(tone) {
if (tone === 'blue') {
return { color: 'var(--accent-blue)' }
}
if (tone === 'cyan') {
return { color: 'var(--accent-cyan)' }
}
if (tone === 'success') {
return { color: 'var(--success-green)' }
}
if (tone === 'danger') {
return { color: 'var(--danger-red)' }
}
return { color: 'var(--text-main)' }
}
function getRecordStyle(tone) {
if (tone === 'success') {
return { color: 'var(--success-green)', fontWeight: 500 }
}
if (tone === 'primary') {
return { color: 'var(--accent-blue)', fontWeight: 500 }
}
return { color: 'var(--danger-red)', fontWeight: 500 }
}
function getFluidStyle(percent) {
const value = Number(percent) || 0
return {
'--fluid-level': `${Math.max(0, Math.min(value, 100))}%`,
}
}
</script>
<template>
<div class="admin-panel active">
<div class="card admin-header-card">
<div class="transfer-head transfer-head-compact">
<div class="connected-to">
<h2 class="admin-title">管理控制台</h2>
<p class="admin-subtitle">AirShare Pro System Dashboard</p>
</div>
<button class="btn-small-primary" type="button" @click="$emit('exit')">退出管理</button>
</div>
</div>
<div class="main-grid admin-summary-grid">
<GlassCard class="admin-stats-card" title="系统运行状态">
<div class="admin-stats-panel">
<div class="admin-stats-row">
<div
v-for="stat in stats"
:key="stat.label"
class="admin-stat-item"
:class="{ 'admin-stat-item-fluid': stat.kind === 'minio' }"
>
<template v-if="stat.kind === 'minio'">
<div class="admin-fluid-card" :style="getFluidStyle(stat.percent)">
<div class="admin-fluid-fill">
<span class="admin-fluid-wave admin-fluid-wave-a"></span>
<span class="admin-fluid-wave admin-fluid-wave-b"></span>
</div>
<div class="admin-fluid-content">
<div class="admin-fluid-icon">
<LocalIcon name="save" size="18" />
</div>
<div class="admin-fluid-copy">
<h3 :style="getStatStyle(stat.tone)">{{ stat.value }}</h3>
<p>{{ stat.label }}</p>
<small>{{ stat.detail }}</small>
</div>
</div>
</div>
</template>
<template v-else>
<span class="admin-stat-kicker">实时指标</span>
<h3 :style="getStatStyle(stat.tone)">
{{ stat.value }}<span v-if="stat.suffix" class="stat-suffix">{{ stat.suffix }}</span>
</h3>
<p>{{ stat.label }}</p>
</template>
</div>
</div>
</div>
</GlassCard>
<GlassCard class="admin-config-card" title="核心参数配置">
<div class="admin-config-stack">
<div class="text-input-group admin-config-row admin-config-row-field admin-config-row-last">
<div class="admin-field-meta">
<label class="admin-field-label" for="admin-file-limit">单文件大小限制</label>
<p class="admin-field-hint">单位为 MB超过该阈值的文件会按当前后端策略处理</p>
</div>
<div class="admin-field-control-row">
<input
id="admin-file-limit"
:value="fileLimit"
min="1"
placeholder="10240"
type="number"
@input="$emit('update:file-limit', Number($event.target.value) || 0)"
/>
<button title="保存配置" type="button" @click="$emit('save-config')">
<LocalIcon name="save" size="18" />
</button>
</div>
</div>
<div class="text-input-group admin-config-row admin-config-row-field admin-config-row-last">
<div class="admin-field-meta">
<label class="admin-field-label" for="admin-minio-capacity">MinIO 总容量</label>
<p class="admin-field-hint">单位为 GB用于容量卡和液位比例计算</p>
</div>
<div class="admin-field-control-row">
<input
id="admin-minio-capacity"
:value="minioCapacity"
min="1"
placeholder="120"
type="number"
@input="$emit('update:minio-capacity', Number($event.target.value) || 0)"
/>
<button title="保存配置" type="button" @click="$emit('save-config')">
<LocalIcon name="save" size="18" />
</button>
</div>
</div>
<div class="admin-config-insights">
<div class="admin-config-highlight">
<span class="admin-config-badge">ACTIVE POLICY</span>
<h3>{{ formatLimitValue(fileLimit) }}</h3>
<p>当前单文件阈值超过该体积后文件会按后端已设定的传输与存档策略处理</p>
</div>
<div class="admin-config-highlight">
<span class="admin-config-badge">MINIO CAPACITY</span>
<h3>{{ formatCapacityValue(minioCapacity) }}</h3>
<p>当前 MinIO 总容量基线用于后台容量展示与液位占比计算</p>
</div>
</div>
</div>
</GlassCard>
</div>
<GlassCard class="admin-table-card" title="最近传输记录Top 5">
<div class="admin-table-wrapper">
<table class="admin-table">
<thead>
<tr>
<th>时间</th>
<th>发送端特征</th>
<th>传输类型</th>
<th>数据量</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr v-for="record in records" :key="`${record.time}-${record.peer}`">
<td>{{ record.time }}</td>
<td>{{ record.peer }}</td>
<td class="admin-record-type-cell">
<span class="admin-record-type" :title="record.type">{{ record.type }}</span>
</td>
<td>{{ record.size }}</td>
<td :style="getRecordStyle(record.tone)">{{ record.status }}</td>
</tr>
</tbody>
</table>
</div>
</GlassCard>
</div>
</template>