Reorder dashboard cards and expand trend metrics
This commit is contained in:
+174
-161
@@ -84,164 +84,9 @@
|
||||
</StatCard>
|
||||
</div>
|
||||
|
||||
<section
|
||||
class="mt-6 rounded-lg p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||
:class="[panelClass, cardHoverClass]"
|
||||
>
|
||||
<div class="mb-5 flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold" :class="[primaryTextClass]">下载趋势</h3>
|
||||
<p class="text-sm" :class="[mutedTextClass]">
|
||||
{{ analyticsWindowText }},下载流量 {{ analyticsDownloadTrafficText }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<input
|
||||
v-model="analyticsStart"
|
||||
type="date"
|
||||
min="1900-01-01"
|
||||
max="2100-12-31"
|
||||
class="h-11 rounded-lg border px-3 text-sm"
|
||||
:class="[fieldClass]"
|
||||
@change="fetchAnalyticsData"
|
||||
/>
|
||||
<input
|
||||
v-model="analyticsEnd"
|
||||
type="date"
|
||||
min="1900-01-01"
|
||||
max="2100-12-31"
|
||||
class="h-11 rounded-lg border px-3 text-sm"
|
||||
:class="[fieldClass]"
|
||||
@change="fetchAnalyticsData"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="h-11 rounded-lg border px-3 text-sm font-medium transition-colors"
|
||||
:class="detailActionClass"
|
||||
@click="trendCanvas?.zoom(0.75)"
|
||||
>
|
||||
放大
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="h-11 rounded-lg border px-3 text-sm font-medium transition-colors"
|
||||
:class="detailActionClass"
|
||||
@click="trendCanvas?.zoom(1.33)"
|
||||
>
|
||||
缩小
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="h-11 rounded-lg border px-3 text-sm font-medium transition-colors"
|
||||
:class="detailActionClass"
|
||||
@click="trendCanvas?.resetWindow()"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="analyticsError"
|
||||
class="mb-4 rounded-lg border px-4 py-3 text-sm"
|
||||
:class="[
|
||||
isDarkMode
|
||||
? 'border-red-900/50 bg-red-950/30 text-red-200'
|
||||
: 'border-red-200 bg-red-50 text-red-700'
|
||||
]"
|
||||
>
|
||||
{{ analyticsError }}
|
||||
</div>
|
||||
<SmoothTrendCanvas
|
||||
ref="trendCanvas"
|
||||
:rows="analyticsDailyRows"
|
||||
:is-dark-mode="isDarkMode"
|
||||
@window-change="analyticsWindowText = $event"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="mt-6 rounded-lg p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||
:class="[panelClass, cardHoverClass]"
|
||||
>
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold" :class="[primaryTextClass]">热门下载 Top 5</h3>
|
||||
<p class="text-sm" :class="[mutedTextClass]">统计当前日期范围内的下载次数和流量</p>
|
||||
</div>
|
||||
<DownloadCloudIcon
|
||||
class="h-5 w-5"
|
||||
:class="[isDarkMode ? 'text-indigo-300' : 'text-indigo-500']"
|
||||
/>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table
|
||||
class="min-w-full divide-y"
|
||||
:class="[isDarkMode ? 'divide-gray-700' : 'divide-gray-200']"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
class="px-4 py-3 text-left text-xs font-semibold uppercase"
|
||||
:class="[mutedTextClass]"
|
||||
>
|
||||
文件
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left text-xs font-semibold uppercase"
|
||||
:class="[mutedTextClass]"
|
||||
>
|
||||
下载
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left text-xs font-semibold uppercase"
|
||||
:class="[mutedTextClass]"
|
||||
>
|
||||
流量
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left text-xs font-semibold uppercase"
|
||||
:class="[mutedTextClass]"
|
||||
>
|
||||
状态
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y" :class="[isDarkMode ? 'divide-gray-700' : 'divide-gray-100']">
|
||||
<tr v-if="analyticsTopFiles.length === 0">
|
||||
<td colspan="4" class="px-4 py-8 text-center text-sm" :class="[mutedTextClass]">
|
||||
暂无下载记录
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-for="file in analyticsTopFiles"
|
||||
:key="file.code"
|
||||
:class="[isDarkMode ? 'hover:bg-gray-700/60' : 'hover:bg-gray-50']"
|
||||
>
|
||||
<td
|
||||
class="max-w-xs truncate px-4 py-3 text-sm font-medium"
|
||||
:class="[primaryTextClass]"
|
||||
:title="file.name"
|
||||
>
|
||||
{{ file.name }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm" :class="[primaryTextClass]">
|
||||
{{ file.download_count || 0 }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm" :class="[primaryTextClass]">
|
||||
{{ formatBytes(file.download_traffic) }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm" :class="[mutedTextClass]">
|
||||
{{ file.current ? '现存' : '历史' }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div v-if="dashboardData.hasExtendedStats" class="mt-6 grid grid-cols-1 gap-6 xl:grid-cols-3">
|
||||
<section
|
||||
class="rounded-lg p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg xl:col-span-2"
|
||||
class="rounded-[30px] p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg xl:col-span-2"
|
||||
:class="[panelClass, cardHoverClass]"
|
||||
>
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
@@ -285,7 +130,7 @@
|
||||
v-for="action in healthActions"
|
||||
:key="action.key"
|
||||
type="button"
|
||||
class="group flex min-h-28 flex-col justify-between rounded-lg border p-4 text-left transition-colors"
|
||||
class="group flex min-h-28 flex-col justify-between rounded-[30px] border p-4 text-left transition-colors"
|
||||
:class="getHealthActionClass(action.tone)"
|
||||
@click="openHealthQueue(action.health)"
|
||||
>
|
||||
@@ -307,7 +152,7 @@
|
||||
|
||||
<div class="mt-6 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div
|
||||
class="rounded-lg border p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md"
|
||||
class="rounded-[30px] border p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md"
|
||||
:class="[subtlePanelClass, cardHoverClass]"
|
||||
>
|
||||
<p class="text-sm" :class="[mutedTextClass]">
|
||||
@@ -324,7 +169,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-lg border p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md"
|
||||
class="rounded-[30px] border p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md"
|
||||
:class="[subtlePanelClass, cardHoverClass]"
|
||||
>
|
||||
<p class="text-sm" :class="[mutedTextClass]">
|
||||
@@ -343,7 +188,7 @@
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="rounded-lg p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||
class="rounded-[30px] p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||
:class="[panelClass, cardHoverClass]"
|
||||
>
|
||||
<div class="mb-5">
|
||||
@@ -389,6 +234,163 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 grid grid-cols-1 gap-6 xl:grid-cols-2">
|
||||
<section
|
||||
class="rounded-[30px] p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||
:class="[panelClass, cardHoverClass]"
|
||||
>
|
||||
<div class="mb-5 flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold" :class="[primaryTextClass]">下载/上传趋势</h3>
|
||||
<p class="text-sm" :class="[mutedTextClass]">
|
||||
{{ analyticsSummaryText }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<input
|
||||
v-model="analyticsStart"
|
||||
type="date"
|
||||
min="1900-01-01"
|
||||
max="2100-12-31"
|
||||
class="h-11 rounded-lg border px-3 text-sm"
|
||||
:class="[fieldClass]"
|
||||
@change="fetchAnalyticsData"
|
||||
/>
|
||||
<input
|
||||
v-model="analyticsEnd"
|
||||
type="date"
|
||||
min="1900-01-01"
|
||||
max="2100-12-31"
|
||||
class="h-11 rounded-lg border px-3 text-sm"
|
||||
:class="[fieldClass]"
|
||||
@change="fetchAnalyticsData"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="h-11 rounded-lg border px-3 text-sm font-medium transition-colors"
|
||||
:class="detailActionClass"
|
||||
@click="trendCanvas?.zoom(0.75)"
|
||||
>
|
||||
放大
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="h-11 rounded-lg border px-3 text-sm font-medium transition-colors"
|
||||
:class="detailActionClass"
|
||||
@click="trendCanvas?.zoom(1.33)"
|
||||
>
|
||||
缩小
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="h-11 rounded-lg border px-3 text-sm font-medium transition-colors"
|
||||
:class="detailActionClass"
|
||||
@click="trendCanvas?.resetWindow()"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="analyticsError"
|
||||
class="mb-4 rounded-lg border px-4 py-3 text-sm"
|
||||
:class="[
|
||||
isDarkMode
|
||||
? 'border-red-900/50 bg-red-950/30 text-red-200'
|
||||
: 'border-red-200 bg-red-50 text-red-700'
|
||||
]"
|
||||
>
|
||||
{{ analyticsError }}
|
||||
</div>
|
||||
<SmoothTrendCanvas
|
||||
ref="trendCanvas"
|
||||
:rows="analyticsDailyRows"
|
||||
:is-dark-mode="isDarkMode"
|
||||
@window-change="analyticsWindowText = $event"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="rounded-[30px] p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||
:class="[panelClass, cardHoverClass]"
|
||||
>
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold" :class="[primaryTextClass]">热门下载 Top 5</h3>
|
||||
<p class="text-sm" :class="[mutedTextClass]">统计当前日期范围内的下载次数和流量</p>
|
||||
</div>
|
||||
<DownloadCloudIcon
|
||||
class="h-5 w-5"
|
||||
:class="[isDarkMode ? 'text-indigo-300' : 'text-indigo-500']"
|
||||
/>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table
|
||||
class="min-w-full divide-y"
|
||||
:class="[isDarkMode ? 'divide-gray-700' : 'divide-gray-200']"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
class="px-4 py-3 text-left text-xs font-semibold uppercase"
|
||||
:class="[mutedTextClass]"
|
||||
>
|
||||
文件
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left text-xs font-semibold uppercase"
|
||||
:class="[mutedTextClass]"
|
||||
>
|
||||
下载
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left text-xs font-semibold uppercase"
|
||||
:class="[mutedTextClass]"
|
||||
>
|
||||
流量
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left text-xs font-semibold uppercase"
|
||||
:class="[mutedTextClass]"
|
||||
>
|
||||
状态
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y" :class="[isDarkMode ? 'divide-gray-700' : 'divide-gray-100']">
|
||||
<tr v-if="analyticsTopFiles.length === 0">
|
||||
<td colspan="4" class="px-4 py-8 text-center text-sm" :class="[mutedTextClass]">
|
||||
暂无下载记录
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-for="file in analyticsTopFiles"
|
||||
:key="file.code"
|
||||
:class="[isDarkMode ? 'hover:bg-gray-700/60' : 'hover:bg-gray-50']"
|
||||
>
|
||||
<td
|
||||
class="max-w-xs truncate px-4 py-3 text-sm font-medium"
|
||||
:class="[primaryTextClass]"
|
||||
:title="file.name"
|
||||
>
|
||||
{{ file.name }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm" :class="[primaryTextClass]">
|
||||
{{ file.download_count || 0 }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm" :class="[primaryTextClass]">
|
||||
{{ formatBytes(file.download_traffic) }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm" :class="[mutedTextClass]">
|
||||
{{ file.current ? '现存' : '历史' }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<footer
|
||||
class="mt-6 flex flex-col gap-2 border-t pt-4 text-xs sm:flex-row sm:items-center sm:justify-between"
|
||||
:class="[isDarkMode ? 'border-gray-800 text-gray-500' : 'border-gray-200 text-gray-500']"
|
||||
@@ -495,9 +497,20 @@ const analyticsError = ref('')
|
||||
const analyticsWindowText = ref('无数据')
|
||||
const analyticsDailyRows = computed(() => analyticsData.value?.daily || [])
|
||||
const analyticsTopFiles = computed<AnalyticsFileRow[]>(() => analyticsData.value?.topFiles || [])
|
||||
const analyticsUploadTrafficText = computed(() =>
|
||||
formatBytes(analyticsData.value?.totals?.uploadTraffic || 0)
|
||||
)
|
||||
const analyticsDownloadTrafficText = computed(() =>
|
||||
formatBytes(analyticsData.value?.totals?.downloadTraffic || 0)
|
||||
)
|
||||
const analyticsSummaryText = computed(
|
||||
() =>
|
||||
`${analyticsWindowText.value},下载 ${analyticsData.value?.totals?.totalDownloads || 0} 次 / ${
|
||||
analyticsDownloadTrafficText.value
|
||||
},上传 ${analyticsData.value?.totals?.totalUploads || 0} 次 / ${
|
||||
analyticsUploadTrafficText.value
|
||||
}`
|
||||
)
|
||||
const formatBytes = (value: number | string | undefined) => formatFileSize(Number(value || 0), 1)
|
||||
|
||||
const fetchAnalyticsData = async () => {
|
||||
@@ -611,7 +624,7 @@ const MetricProgress = defineComponent({
|
||||
'div',
|
||||
{
|
||||
class:
|
||||
'rounded-lg border border-gray-200/60 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:border-indigo-200 hover:shadow-md hover:shadow-gray-200/80 dark:border-gray-700 dark:hover:border-indigo-500/40 dark:hover:bg-gray-800 dark:hover:shadow-black/20'
|
||||
'rounded-[30px] border border-gray-200/60 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:border-indigo-200 hover:shadow-md hover:shadow-gray-200/80 dark:border-gray-700 dark:hover:border-indigo-500/40 dark:hover:bg-gray-800 dark:hover:shadow-black/20'
|
||||
},
|
||||
[
|
||||
h('div', { class: 'mb-2 flex items-center justify-between text-sm' }, [
|
||||
|
||||
Reference in New Issue
Block a user