feat: add user download traffic reports and restore OSS direct downloads
This commit is contained in:
@@ -2330,6 +2330,163 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下载流量额度与统计 -->
|
||||
<div v-if="user && !user.is_admin" class="settings-section settings-download-traffic" style="margin-bottom: 40px;">
|
||||
<h3 class="settings-section-title" style="margin-bottom: 20px;">
|
||||
<i class="fas fa-tachometer-alt"></i> 下载流量额度与统计
|
||||
</h3>
|
||||
<div class="settings-panel" style="background: var(--bg-card); border: 1px solid var(--glass-border); border-radius: 12px; padding: 18px;">
|
||||
<div style="display: flex; justify-content: space-between; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 12px;">
|
||||
<div style="font-size: 13px; color: var(--text-secondary);">
|
||||
管理员设置的下载流量限制会在这里实时展示,单位自动按 B/KB/MB/GB/TB 切换
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; align-items: center; flex-wrap: wrap;">
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
style="padding: 5px 10px; font-size: 12px;"
|
||||
:class="{ 'btn-primary': downloadTrafficReport.days === 7 }"
|
||||
@click="setDownloadTrafficReportDays(7)">
|
||||
近7天
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
style="padding: 5px 10px; font-size: 12px;"
|
||||
:class="{ 'btn-primary': downloadTrafficReport.days === 30 }"
|
||||
@click="setDownloadTrafficReportDays(30)">
|
||||
近30天
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
style="padding: 5px 10px; font-size: 12px;"
|
||||
:class="{ 'btn-primary': downloadTrafficReport.days === 90 }"
|
||||
@click="setDownloadTrafficReportDays(90)">
|
||||
近90天
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
style="padding: 5px 10px; font-size: 12px;"
|
||||
:class="{ 'btn-primary': downloadTrafficReport.days === 180 }"
|
||||
@click="setDownloadTrafficReportDays(180)">
|
||||
近180天
|
||||
</button>
|
||||
<button class="btn btn-secondary" style="padding: 5px 10px; font-size: 12px;" @click="loadDownloadTrafficReport(downloadTrafficReport.days)" :disabled="downloadTrafficReport.loading">
|
||||
<i :class="downloadTrafficReport.loading ? 'fas fa-sync-alt fa-spin' : 'fas fa-sync-alt'"></i>
|
||||
{{ downloadTrafficReport.loading ? '加载中...' : '刷新' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="downloadTrafficReport.error" style="margin-bottom: 10px; padding: 10px 12px; border-radius: 8px; background: rgba(239,68,68,0.1); color: #ef4444; font-size: 13px;">
|
||||
<i class="fas fa-exclamation-triangle"></i> {{ downloadTrafficReport.error }}
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; margin-bottom: 12px;">
|
||||
<div style="padding: 12px; border-radius: 10px; background: rgba(59,130,246,0.1); border: 1px solid rgba(59,130,246,0.25);">
|
||||
<div style="font-size: 12px; color: var(--text-muted); margin-bottom: 6px;">当前限制</div>
|
||||
<div style="font-size: 18px; font-weight: 700; color: #3b82f6;">
|
||||
{{ downloadTrafficIsUnlimited ? '不限' : formatBytes(downloadTrafficQuotaBytes) }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 12px; border-radius: 10px; background: rgba(245,158,11,0.1); border: 1px solid rgba(245,158,11,0.28);">
|
||||
<div style="font-size: 12px; color: var(--text-muted); margin-bottom: 6px;">已使用</div>
|
||||
<div style="font-size: 18px; font-weight: 700; color: #f59e0b;">
|
||||
{{ formatBytes(downloadTrafficUsedBytes) }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 12px; border-radius: 10px; background: rgba(16,185,129,0.1); border: 1px solid rgba(16,185,129,0.26);">
|
||||
<div style="font-size: 12px; color: var(--text-muted); margin-bottom: 6px;">剩余可用</div>
|
||||
<div style="font-size: 18px; font-weight: 700; color: #10b981;">
|
||||
{{ downloadTrafficIsUnlimited ? '不限' : formatBytes(downloadTrafficRemainingBytes || 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 12px; border-radius: 10px; background: rgba(99,102,241,0.12); border: 1px solid rgba(99,102,241,0.28);">
|
||||
<div style="font-size: 12px; color: var(--text-muted); margin-bottom: 6px;">策略</div>
|
||||
<div style="font-size: 14px; font-weight: 700; color: #6366f1;">
|
||||
{{ getDownloadResetCycleText(downloadTrafficResetCycle) }}
|
||||
</div>
|
||||
<div style="font-size: 12px; color: var(--text-secondary); margin-top: 4px;">
|
||||
到期: {{ downloadTrafficExpiresAt ? formatDate(downloadTrafficExpiresAt) : '无' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!downloadTrafficIsUnlimited" style="margin-bottom: 12px;">
|
||||
<div style="display: flex; justify-content: space-between; font-size: 12px; color: var(--text-secondary); margin-bottom: 6px;">
|
||||
<span>额度使用率</span>
|
||||
<span>{{ downloadTrafficUsagePercentage }}%</span>
|
||||
</div>
|
||||
<div style="height: 10px; background: rgba(148,163,184,0.24); border-radius: 999px; overflow: hidden;">
|
||||
<div :style="{ width: downloadTrafficUsagePercentage + '%', height: '100%', background: downloadTrafficUsagePercentage > 90 ? '#ef4444' : (downloadTrafficUsagePercentage > 75 ? '#f59e0b' : '#22c55e') }"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px; margin-bottom: 12px;">
|
||||
<div style="padding: 10px; border-radius: 8px; background: var(--bg-secondary); border: 1px solid var(--glass-border);">
|
||||
<div style="font-size: 12px; color: var(--text-muted);">今天使用</div>
|
||||
<div style="font-weight: 700; color: var(--text-primary); margin-top: 4px;">
|
||||
{{ formatBytes(downloadTrafficReport.summary?.today?.bytes_used || 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 10px; border-radius: 8px; background: var(--bg-secondary); border: 1px solid var(--glass-border);">
|
||||
<div style="font-size: 12px; color: var(--text-muted);">近7天</div>
|
||||
<div style="font-weight: 700; color: var(--text-primary); margin-top: 4px;">
|
||||
{{ formatBytes(downloadTrafficReport.summary?.last_7_days?.bytes_used || 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 10px; border-radius: 8px; background: var(--bg-secondary); border: 1px solid var(--glass-border);">
|
||||
<div style="font-size: 12px; color: var(--text-muted);">近30天</div>
|
||||
<div style="font-weight: 700; color: var(--text-primary); margin-top: 4px;">
|
||||
{{ formatBytes(downloadTrafficReport.summary?.last_30_days?.bytes_used || 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 10px; border-radius: 8px; background: var(--bg-secondary); border: 1px solid var(--glass-border);">
|
||||
<div style="font-size: 12px; color: var(--text-muted);">所选区间</div>
|
||||
<div style="font-weight: 700; color: var(--text-primary); margin-top: 4px;">
|
||||
{{ formatBytes(downloadTrafficReport.summary?.selected_range?.bytes_used || 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 10px; border-radius: 8px; background: var(--bg-secondary); border: 1px solid var(--glass-border);">
|
||||
<div style="font-size: 12px; color: var(--text-muted);">历史累计</div>
|
||||
<div style="font-weight: 700; color: var(--text-primary); margin-top: 4px;">
|
||||
{{ formatBytes(downloadTrafficReport.summary?.all_time?.bytes_used || 0) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--glass-border); border-radius: 10px; overflow: hidden;">
|
||||
<div style="padding: 10px 12px; background: var(--bg-secondary); border-bottom: 1px solid var(--glass-border); font-size: 13px; color: var(--text-secondary); display: flex; justify-content: space-between; align-items: center;">
|
||||
<span>按天用量明细(最近 {{ downloadTrafficReport.days }} 天)</span>
|
||||
<span v-if="downloadTrafficReport.summary?.peak_day">峰值: {{ formatReportDateLabel(downloadTrafficReport.summary.peak_day.date) }} / {{ formatBytes(downloadTrafficReport.summary.peak_day.bytes_used || 0) }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="downloadTrafficReport.loading && downloadTrafficDailyRowsDesc.length === 0" style="padding: 16px; text-align: center; color: var(--text-muted);">
|
||||
<i class="fas fa-spinner fa-spin"></i> 报表加载中...
|
||||
</div>
|
||||
<div v-else-if="downloadTrafficDailyRowsDesc.length === 0" style="padding: 16px; text-align: center; color: var(--text-muted);">
|
||||
暂无下载流量记录
|
||||
</div>
|
||||
<div v-else style="max-height: 280px; overflow: auto;">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<thead>
|
||||
<tr style="background: rgba(148,163,184,0.12);">
|
||||
<th style="padding: 10px; text-align: left; font-size: 12px; color: var(--text-secondary);">日期</th>
|
||||
<th style="padding: 10px; text-align: right; font-size: 12px; color: var(--text-secondary);">下载流量</th>
|
||||
<th style="padding: 10px; text-align: right; font-size: 12px; color: var(--text-secondary);">下载次数</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in downloadTrafficDailyRowsDesc" :key="row.date" style="border-top: 1px solid var(--glass-border);">
|
||||
<td style="padding: 9px 10px; color: var(--text-primary); font-size: 13px;">{{ formatReportDateLabel(row.date) }}</td>
|
||||
<td style="padding: 9px 10px; text-align: right; color: var(--text-primary); font-size: 13px; font-weight: 600;">{{ formatBytes(row.bytes_used || 0) }}</td>
|
||||
<td style="padding: 9px 10px; text-align: right; color: var(--text-secondary); font-size: 13px;">{{ row.download_count || 0 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 界面设置 -->
|
||||
<h3 class="settings-section-title settings-section-gap" style="margin: 40px 0 20px 0;"><i class="fas fa-palette"></i> 界面设置</h3>
|
||||
|
||||
Reference in New Issue
Block a user