From 36fe4a7f240aa794ca2eb45c333f540323e19124 Mon Sep 17 00:00:00 2001
From: Orion
Date: Fri, 5 Jun 2026 16:14:24 +0800
Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96=E6=9D=A1?=
=?UTF-8?q?=E4=BB=B6=E7=AD=9B=E9=80=89=E9=87=8D=E7=BD=AE=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/views/manage/FileManageView.vue | 38 ++++++++++++++---------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/src/views/manage/FileManageView.vue b/src/views/manage/FileManageView.vue
index 7b5a745..d358674 100644
--- a/src/views/manage/FileManageView.vue
+++ b/src/views/manage/FileManageView.vue
@@ -9,19 +9,6 @@
{{ t('fileManage.subtitle', { count: summary.totalFiles }) }}
-
-
-
-
-
- {{ t('fileManage.resetFilters') }}
-
-
@@ -59,12 +46,25 @@
{{ hasActiveFilters ? '已应用筛选条件' : '默认收起,需要时展开' }}
-
-
-
-
- {{ filterBodyVisible ? '收起筛选' : '展开筛选' }}
-
+
+
+
+
+
+ {{ t('fileManage.resetFilters') }}
+
+
+
+
+
+ {{ filterBodyVisible ? '收起筛选' : '展开筛选' }}
+
+
From 21a17012c80c8f1d9005fa86eec6f40a32a5c505 Mon Sep 17 00:00:00 2001
From: Orion
Date: Fri, 5 Jun 2026 16:18:17 +0800
Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=AE=E5=A4=8D=E5=8E=86?=
=?UTF-8?q?=E5=8F=B2=E6=96=87=E4=BB=B6=E8=BF=87=E6=9C=9F=E7=8A=B6=E6=80=81?=
=?UTF-8?q?=E6=98=BE=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/types/dashboard.ts | 1 +
src/views/manage/FileManageView.vue | 8 +++++++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/types/dashboard.ts b/src/types/dashboard.ts
index 164be95..5552a9a 100644
--- a/src/types/dashboard.ts
+++ b/src/types/dashboard.ts
@@ -114,6 +114,7 @@ export interface AnalyticsFileRow {
upload_count?: number
upload_traffic?: number | string
current: boolean
+ expired?: boolean
deleted?: boolean
created_at?: string
uploaded_at?: string
diff --git a/src/views/manage/FileManageView.vue b/src/views/manage/FileManageView.vue
index d358674..9a09a5b 100644
--- a/src/views/manage/FileManageView.vue
+++ b/src/views/manage/FileManageView.vue
@@ -703,7 +703,7 @@
{{ formatHistoryBytes(file.download_traffic) }}
|
- {{ file.deleted ? '已删除' : file.current ? '现存' : '历史' }}
+ {{ getHistoryStatusLabel(file) }}
|
@@ -1613,6 +1613,12 @@ const handleHistoryPageSizeChange = (size: number) => {
const formatHistoryBytes = (value: number | string | undefined) =>
formatFileSize(Number(value || 0), 1)
const formatHistoryDate = (value?: string) => (value ? formatTimestamp(value) : '-')
+const getHistoryStatusLabel = (file: AnalyticsFileRow) => {
+ if (file.deleted) return '已删除'
+ if (file.expired) return '已过期'
+ if (file.current) return '现存'
+ return '历史'
+}
const primaryTextClass = computed(() => (isDarkMode.value ? 'text-white' : 'text-gray-900'))
const mutedTextClass = computed(() => (isDarkMode.value ? 'text-gray-400' : 'text-gray-500'))
From 8092dbc59390f2b94fb08dfcacb3ed35d4666ab5 Mon Sep 17 00:00:00 2001
From: Orion
Date: Fri, 5 Jun 2026 16:23:00 +0800
Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96=E8=B6=8B?=
=?UTF-8?q?=E5=8A=BF=E5=9B=BE=E6=82=AC=E6=B5=AE=E5=B9=B3=E7=A7=BB=E5=8A=A8?=
=?UTF-8?q?=E7=94=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/SmoothTrendCanvas.vue | 57 +++++++++++++++++----
1 file changed, 47 insertions(+), 10 deletions(-)
diff --git a/src/components/common/SmoothTrendCanvas.vue b/src/components/common/SmoothTrendCanvas.vue
index 96fdbf6..7433ade 100644
--- a/src/components/common/SmoothTrendCanvas.vue
+++ b/src/components/common/SmoothTrendCanvas.vue
@@ -70,16 +70,20 @@ const offset = ref(0)
const windowSize = ref(30)
const drag = ref(null)
const hoverX = ref(null)
+const hoverVisual = ref<{ x: number; downloadY: number; uploadY: number } | null>(null)
const velocity = ref(0)
const rafId = ref(null)
const inertiaRafId = ref(null)
const lastInertiaTime = ref(0)
+const lastHoverTime = ref(0)
const tooltip = ref(null)
const INERTIA_REFERENCE_FRAME_MS = 1000 / 120
const INERTIA_MAX_FRAME_MS = 1000 / 30
const INERTIA_DECAY_PER_120HZ_FRAME = 0.96
const INERTIA_STOP_VELOCITY = 0.01
+const HOVER_EASE_PER_120HZ_FRAME = 0.34
+const HOVER_SNAP_DISTANCE = 0.2
const panelClass = computed(() =>
props.isDarkMode
@@ -118,13 +122,13 @@ const clampState = () => {
const scheduleDraw = () => {
if (rafId.value !== null) return
- rafId.value = window.requestAnimationFrame(() => {
+ rafId.value = window.requestAnimationFrame((timestamp) => {
rafId.value = null
- draw()
+ draw(timestamp)
})
}
-const draw = () => {
+const draw = (timestamp = performance.now()) => {
const canvas = canvasRef.value
if (!canvas) return
const ctx = canvas.getContext('2d')
@@ -266,19 +270,40 @@ const draw = () => {
const uploadTraffic = Number(row.uploadTraffic || 0)
const downloadY = padTop + height - (downloads / max) * height
const uploadY = padTop + height - (uploads / max) * height
- const anchorY = Math.min(downloadY, uploadY)
+ const elapsed = lastHoverTime.value
+ ? Math.min(timestamp - lastHoverTime.value, INERTIA_MAX_FRAME_MS)
+ : INERTIA_REFERENCE_FRAME_MS
+ lastHoverTime.value = timestamp
+
+ if (!hoverVisual.value) {
+ hoverVisual.value = { x: snapX, downloadY, uploadY }
+ } else {
+ const ease = 1 - Math.pow(1 - HOVER_EASE_PER_120HZ_FRAME, elapsed / INERTIA_REFERENCE_FRAME_MS)
+ const nextX = hoverVisual.value.x + (snapX - hoverVisual.value.x) * ease
+ const nextDownloadY = hoverVisual.value.downloadY + (downloadY - hoverVisual.value.downloadY) * ease
+ const nextUploadY = hoverVisual.value.uploadY + (uploadY - hoverVisual.value.uploadY) * ease
+ hoverVisual.value = {
+ x: Math.abs(nextX - snapX) < HOVER_SNAP_DISTANCE ? snapX : nextX,
+ downloadY:
+ Math.abs(nextDownloadY - downloadY) < HOVER_SNAP_DISTANCE ? downloadY : nextDownloadY,
+ uploadY: Math.abs(nextUploadY - uploadY) < HOVER_SNAP_DISTANCE ? uploadY : nextUploadY
+ }
+ }
+
+ const visual = hoverVisual.value
+ const anchorY = Math.min(visual.downloadY, visual.uploadY)
const placeBelow = anchorY < 104
const tooltipInset = Math.min(mobile ? 116 : 132, Math.max(48, rect.width / 2 - 8))
ctx.strokeStyle = props.isDarkMode ? 'rgba(148, 163, 184, 0.35)' : 'rgba(91, 121, 132, 0.28)'
ctx.lineWidth = 1
ctx.beginPath()
- ctx.moveTo(snapX, padTop)
- ctx.lineTo(snapX, padTop + height)
+ ctx.moveTo(visual.x, padTop)
+ ctx.lineTo(visual.x, padTop + height)
ctx.stroke()
tooltip.value = {
- left: clamp(snapX, tooltipInset, rect.width - tooltipInset),
+ left: clamp(visual.x, tooltipInset, rect.width - tooltipInset),
top: placeBelow ? anchorY + 16 : anchorY - 12,
date: row.date,
downloads,
@@ -287,9 +312,19 @@ const draw = () => {
uploadTraffic: formatBytes(uploadTraffic),
transform: placeBelow ? 'translate(-50%, 0)' : 'translate(-50%, -100%)'
}
- drawHoverPoint({ x: snapX, y: downloadY }, downloadColor)
- drawHoverPoint({ x: snapX, y: uploadY }, uploadColor)
+ drawHoverPoint({ x: visual.x, y: visual.downloadY }, downloadColor)
+ drawHoverPoint({ x: visual.x, y: visual.uploadY }, uploadColor)
+
+ if (
+ Math.abs(visual.x - snapX) >= HOVER_SNAP_DISTANCE ||
+ Math.abs(visual.downloadY - downloadY) >= HOVER_SNAP_DISTANCE ||
+ Math.abs(visual.uploadY - uploadY) >= HOVER_SNAP_DISTANCE
+ ) {
+ scheduleDraw()
+ }
} else {
+ hoverVisual.value = null
+ lastHoverTime.value = 0
tooltip.value = null
}
}
@@ -333,7 +368,7 @@ const applyInertia = (timestamp: number) => {
lastInertiaTime.value = timestamp
pan(velocity.value * elapsed, false)
- draw()
+ draw(timestamp)
velocity.value *= Math.pow(
INERTIA_DECAY_PER_120HZ_FRAME,
elapsed / INERTIA_REFERENCE_FRAME_MS
@@ -385,6 +420,8 @@ const handlePointerCancel = () => {
const handlePointerLeave = () => {
drag.value = null
hoverX.value = null
+ hoverVisual.value = null
+ lastHoverTime.value = 0
scheduleDraw()
}