From 2cde31f8227e5823fccbfa5c80af6ab3776ec7e4 Mon Sep 17 00:00:00 2001
From: Orion
Date: Fri, 5 Jun 2026 16:03:54 +0800
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96=E7=AE=A1?=
=?UTF-8?q?=E7=90=86=E9=A1=B5=E4=BA=A4=E4=BA=92=E4=B8=8E=E5=8E=86=E5=8F=B2?=
=?UTF-8?q?=E5=90=8C=E6=AD=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/DataTable.vue | 11 ++-
src/components/common/SmoothTrendCanvas.vue | 46 ++++------
src/i18n/locales/en-US.ts | 8 +-
src/i18n/locales/zh-CN.ts | 8 +-
src/views/manage/DashboardView.vue | 37 ++++++--
src/views/manage/FileManageView.vue | 97 +++++++++++++++------
6 files changed, 131 insertions(+), 76 deletions(-)
diff --git a/src/components/common/DataTable.vue b/src/components/common/DataTable.vue
index 806d4ff..a07731c 100644
--- a/src/components/common/DataTable.vue
+++ b/src/components/common/DataTable.vue
@@ -6,9 +6,14 @@
class="border-b px-6 py-4"
:class="[isDarkMode ? 'border-gray-700/80' : 'border-[#d9e4e6]']"
>
-
- {{ title }}
-
+
+
+ {{ title }}
+
+
+
+
+
diff --git a/src/components/common/SmoothTrendCanvas.vue b/src/components/common/SmoothTrendCanvas.vue
index b6e46ae..96fdbf6 100644
--- a/src/components/common/SmoothTrendCanvas.vue
+++ b/src/components/common/SmoothTrendCanvas.vue
@@ -100,8 +100,6 @@ const formatBytes = (value: number | string | undefined) => {
return `${(bytes / 1024 ** index).toFixed(index === 0 ? 0 : 1)} ${units[index]}`
}
-const lerp = (start: number, end: number, amount: number) => start + (end - start) * amount
-
const clamp = (value: number, min: number, max: number) => Math.max(min, Math.min(max, value))
const getDefaultWindow = () => {
@@ -259,52 +257,38 @@ const draw = () => {
if (hoverX.value !== null && points.length && total) {
const hoverRatio = clamp((hoverX.value - padLeft) / width, 0, 1)
- const sourceIndex = clamp(offset.value + hoverRatio * denominator, 0, total - 1)
- const leftIndex = Math.floor(sourceIndex)
- const rightIndex = Math.min(total - 1, Math.ceil(sourceIndex))
- const mix = sourceIndex - leftIndex
- const leftRow = props.rows[leftIndex]
- const rightRow = props.rows[rightIndex] || leftRow
- const interpolatedX = padLeft + ((sourceIndex - offset.value) / denominator) * width
- const downloads = lerp(Number(leftRow.downloads || 0), Number(rightRow.downloads || 0), mix)
- const uploads = lerp(Number(leftRow.uploads || 0), Number(rightRow.uploads || 0), mix)
- const downloadTraffic = lerp(
- Number(leftRow.downloadTraffic || 0),
- Number(rightRow.downloadTraffic || 0),
- mix
- )
- const uploadTraffic = lerp(
- Number(leftRow.uploadTraffic || 0),
- Number(rightRow.uploadTraffic || 0),
- mix
- )
+ const sourceIndex = Math.round(clamp(offset.value + hoverRatio * denominator, 0, total - 1))
+ const row = props.rows[sourceIndex]
+ const snapX = padLeft + ((sourceIndex - offset.value) / denominator) * width
+ const downloads = Number(row.downloads || 0)
+ const uploads = Number(row.uploads || 0)
+ const downloadTraffic = Number(row.downloadTraffic || 0)
+ 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 placeBelow = anchorY < 104
const tooltipInset = Math.min(mobile ? 116 : 132, Math.max(48, rect.width / 2 - 8))
- const dateText =
- leftRow.date === rightRow.date ? leftRow.date : `${leftRow.date} - ${rightRow.date}`
ctx.strokeStyle = props.isDarkMode ? 'rgba(148, 163, 184, 0.35)' : 'rgba(91, 121, 132, 0.28)'
ctx.lineWidth = 1
ctx.beginPath()
- ctx.moveTo(interpolatedX, padTop)
- ctx.lineTo(interpolatedX, padTop + height)
+ ctx.moveTo(snapX, padTop)
+ ctx.lineTo(snapX, padTop + height)
ctx.stroke()
tooltip.value = {
- left: clamp(interpolatedX, tooltipInset, rect.width - tooltipInset),
+ left: clamp(snapX, tooltipInset, rect.width - tooltipInset),
top: placeBelow ? anchorY + 16 : anchorY - 12,
- date: dateText,
- downloads: Math.round(downloads),
- uploads: Math.round(uploads),
+ date: row.date,
+ downloads,
+ uploads,
downloadTraffic: formatBytes(downloadTraffic),
uploadTraffic: formatBytes(uploadTraffic),
transform: placeBelow ? 'translate(-50%, 0)' : 'translate(-50%, -100%)'
}
- drawHoverPoint({ x: interpolatedX, y: downloadY }, downloadColor)
- drawHoverPoint({ x: interpolatedX, y: uploadY }, uploadColor)
+ drawHoverPoint({ x: snapX, y: downloadY }, downloadColor)
+ drawHoverPoint({ x: snapX, y: uploadY }, uploadColor)
} else {
tooltip.value = null
}
diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts
index 584e374..ca3bce4 100644
--- a/src/i18n/locales/en-US.ts
+++ b/src/i18n/locales/en-US.ts
@@ -567,10 +567,10 @@ export default {
permanent: 'Permanent'
},
insightActions: {
- monitor: 'Keep monitoring',
- extend_or_delete: 'Extend retention or clean up',
- inspect_storage: 'Inspect storage path and file name',
- extend_expiration: 'Extend expiration if needed'
+ monitor: 'No action needed',
+ extend_or_delete: 'Extend or clean up',
+ inspect_storage: 'Check storage info',
+ extend_expiration: 'Extend validity'
},
insightReasons: {
expired: 'Past expiration time',
diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts
index db11836..f719c97 100644
--- a/src/i18n/locales/zh-CN.ts
+++ b/src/i18n/locales/zh-CN.ts
@@ -564,10 +564,10 @@ export default {
permanent: '永久有效'
},
insightActions: {
- monitor: '保持观察',
- extend_or_delete: '延长有效期或清理记录',
- inspect_storage: '检查存储路径与文件名',
- extend_expiration: '按需延长过期时间'
+ monitor: '无需处理',
+ extend_or_delete: '建议延长或清理',
+ inspect_storage: '检查存储信息',
+ extend_expiration: '建议延长有效期'
},
insightReasons: {
expired: '已超过过期时间',
diff --git a/src/views/manage/DashboardView.vue b/src/views/manage/DashboardView.vue
index 82a0ef0..4b2b5b9 100644
--- a/src/views/manage/DashboardView.vue
+++ b/src/views/manage/DashboardView.vue
@@ -10,11 +10,13 @@
{{ t('admin.dashboard.lastUpdated', { time: lastUpdatedText }) }}
-
+
- {{ isLoading ? t('admin.dashboard.refreshing') : t('admin.dashboard.refresh') }}
+ {{
+ isDashboardRefreshing ? t('admin.dashboard.refreshing') : t('admin.dashboard.refresh')
+ }}
@@ -260,13 +262,28 @@
/>
-
+
放大
-
+
缩小
-
+
重置
@@ -476,6 +493,7 @@ const analyticsStart = ref(daysAgo(120))
const analyticsEnd = ref(today())
const analyticsData = ref(null)
const analyticsError = ref('')
+const isAnalyticsLoading = ref(false)
const analyticsWindowText = ref('无数据')
const analyticsDailyRows = computed(() => analyticsData.value?.daily || [])
const analyticsTopFiles = computed(() => analyticsData.value?.topFiles || [])
@@ -497,12 +515,19 @@ const formatBytes = (value: number | string | undefined) => formatFileSize(Numbe
const fetchAnalyticsData = async () => {
analyticsError.value = ''
+ isAnalyticsLoading.value = true
try {
analyticsData.value = await fetchAnalytics(analyticsStart.value, analyticsEnd.value)
} catch (error) {
analyticsError.value = error instanceof Error ? error.message : '统计加载失败'
+ } finally {
+ isAnalyticsLoading.value = false
}
}
+const isDashboardRefreshing = computed(() => isLoading.value || isAnalyticsLoading.value)
+const refreshDashboardPage = async () => {
+ await Promise.all([fetchDashboardData(), fetchAnalyticsData()])
+}
const healthActions = computed(() => [
{
@@ -648,6 +673,6 @@ const PolicyRow = defineComponent({
})
onMounted(() => {
- void Promise.all([fetchDashboardData(), fetchAnalyticsData()])
+ void refreshDashboardPage()
})
diff --git a/src/views/manage/FileManageView.vue b/src/views/manage/FileManageView.vue
index a8d7b21..7b5a745 100644
--- a/src/views/manage/FileManageView.vue
+++ b/src/views/manage/FileManageView.vue
@@ -10,12 +10,6 @@
-
-
-
-
- {{ t('fileManage.refresh') }}
-
+
+
+
+
+
+ {{ t('fileManage.refresh') }}
+
+
@@ -330,7 +332,7 @@
size="sm"
:disabled="!hasSelectedFiles || isBatchUpdating || isBatchPolicyActionRunning"
:loading="isBatchDeleting"
- @click="deleteSelectedFiles"
+ @click="deleteSelectedFilesAndRefreshHistory"
>
@@ -357,7 +359,7 @@
{{ t('fileManage.loadError') }}
-
+
@@ -437,6 +439,12 @@
>
{{ file.displayHealthState }}
+
+ {{ file.displayHealthAction }}
+
@@ -553,7 +561,7 @@
? 'bg-[#dc2626] text-white shadow-[#7f1d1d]/35 hover:bg-[#b91c1c]'
: 'bg-[#dc2626] text-white shadow-[#f87171]/35 hover:bg-[#b91c1c]'
]"
- @click="deleteFile(file.id)"
+ @click="deleteFileAndRefreshHistory(file.id)"
>
{{ t('common.delete') }}
@@ -942,12 +950,20 @@
{{ getInsightStateLabel(selectedFileDetail.statusInsightState) }}
-
- {{ getInsightSeverityLabel(selectedFileDetail.statusInsightSeverity) }}
-
+
+
+ {{ getInsightSeverityLabel(selectedFileDetail.statusInsightSeverity) }}
+
+
+ {{ selectedFileDetail.displayHealthAction }}
+
+
@@ -966,11 +982,11 @@
v-for="action in detailPolicyActionOptions"
:key="action.action"
type="button"
- class="flex min-h-16 items-start gap-2 rounded-lg border px-3 py-2 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60"
- :class="detailPolicyActionClass"
+ class="theme-2026-card-hover flex min-h-16 items-start gap-2 rounded-[30px] border px-3 py-2 text-left shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:shadow-md"
+ :class="[detailPolicyActionClass, cardHoverClass]"
:disabled="isDetailPolicyActionRunning || isDetailLoading || action.disabled"
:title="action.description"
- @click="applyDetailPolicyAction(action.action)"
+ @click="applyDetailPolicyActionAndRefreshHistory(action.action)"
>
{{ t('common.cancel') }}
-
+
@@ -1330,7 +1346,7 @@
{{ t('common.cancel') }}
-
+
@@ -1525,25 +1541,25 @@ const {
copyDetailRetrieveLink,
copyText,
clearSelection,
- deleteFile,
- deleteSelectedFiles,
+ deleteFile: deleteFileRaw,
+ deleteSelectedFiles: deleteSelectedFilesRaw,
deleteSelectedViewPreset,
downloadFile,
exportPreviewText,
handlePageChange,
handlePageSizeChange,
handleSearch,
- applyDetailPolicyAction,
- applySelectedPolicyAction,
- handleBatchUpdate,
- handleUpdate,
+ applyDetailPolicyAction: applyDetailPolicyActionRaw,
+ applySelectedPolicyAction: applySelectedPolicyActionRaw,
+ handleBatchUpdate: handleBatchUpdateRaw,
+ handleUpdate: handleUpdateRaw,
loadViewPresets,
loadFiles,
openBatchEditModal,
openEditModal,
openFileDetail,
openTextPreview,
- refreshFiles,
+ refreshFiles: refreshFileList,
resetFilters,
saveCurrentViewPreset,
setHealthFilter,
@@ -1575,6 +1591,21 @@ const loadHistoryFiles = async () => {
isHistoryLoading.value = false
}
}
+const runFileMutationAndRefreshHistory = async (operation: () => Promise) => {
+ await operation()
+ await loadHistoryFiles()
+}
+const handleUpdateAndRefreshHistory = () => runFileMutationAndRefreshHistory(handleUpdateRaw)
+const handleBatchUpdateAndRefreshHistory = () =>
+ runFileMutationAndRefreshHistory(handleBatchUpdateRaw)
+const deleteFileAndRefreshHistory = (id: number) =>
+ runFileMutationAndRefreshHistory(() => deleteFileRaw(id))
+const deleteSelectedFilesAndRefreshHistory = () =>
+ runFileMutationAndRefreshHistory(deleteSelectedFilesRaw)
+const applySelectedPolicyActionAndRefreshHistory = (action: AdminFilePolicyAction) =>
+ runFileMutationAndRefreshHistory(() => applySelectedPolicyActionRaw(action))
+const applyDetailPolicyActionAndRefreshHistory = (action: AdminFilePolicyAction) =>
+ runFileMutationAndRefreshHistory(() => applyDetailPolicyActionRaw(action))
const handleHistoryPageSizeChange = (size: number) => {
historyPageSize.value = size
historyPage.value = 1
@@ -1992,6 +2023,16 @@ const getInsightBadgeClass = (severity: AdminFileInsightSeverity) => {
return isDarkMode.value ? darkClasses[severity] : lightClasses[severity]
}
+const getInsightActionBadgeClass = (action: string) => {
+ if (action === 'extend_or_delete' || action === 'extend_expiration') {
+ return isDarkMode.value ? 'bg-amber-900/40 text-amber-200' : 'bg-amber-100 text-amber-700'
+ }
+ if (action === 'inspect_storage') {
+ return isDarkMode.value ? 'bg-red-900/40 text-red-200' : 'bg-red-100 text-red-700'
+ }
+ return isDarkMode.value ? 'bg-gray-700 text-gray-300' : 'bg-gray-100 text-gray-700'
+}
+
const getTimelineDotClass = (severity: AdminFileInsightSeverity) => {
const classes: Record = {
success: 'bg-emerald-500',