fix(admin-spa): 修复仪表板时区处理 - 前端显示系统时区,发送UTC时间

- 简化 getSystemTimezoneDay 函数,正确处理系统时区到UTC的转换
- 修复 setDateFilterPreset 中昨天/前天的计算逻辑,使用本地时间计算日期
- 确保自定义时间选择器显示系统时区时间,但发送UTC时间给后端
- API Keys页面已经使用正确的时间范围参数,无需修改
This commit is contained in:
shaw
2025-07-30 12:05:30 +08:00
parent 6eae292922
commit 4eedc2e6b5
3 changed files with 93 additions and 52 deletions

View File

@@ -1 +0,0 @@
pre[data-v-3d91f349]{white-space:pre-wrap;word-wrap:break-word}.tab-content[data-v-518dfd83]{min-height:calc(100vh - 300px)}.table-container[data-v-518dfd83]{overflow-x:auto;border-radius:12px;border:1px solid rgba(0,0,0,.05)}.table-row[data-v-518dfd83]{transition:all .2s ease}.table-row[data-v-518dfd83]:hover{background-color:#00000005}.loading-spinner[data-v-518dfd83]{width:24px;height:24px;border:2px solid #e5e7eb;border-top:2px solid #3b82f6;border-radius:50%;animation:spin-518dfd83 1s linear infinite}@keyframes spin-518dfd83{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.api-key-date-picker[data-v-518dfd83] .el-input__inner{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.api-key-date-picker[data-v-518dfd83] .el-input__inner:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.api-key-date-picker[data-v-518dfd83] .el-range-separator{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}

View File

@@ -18,7 +18,7 @@
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
<script type="module" crossorigin src="/admin-next/assets/index-CJ53_Ylg.js"></script>
<script type="module" crossorigin src="/admin-next/assets/index-DiDOcr6H.js"></script>
<link rel="modulepreload" crossorigin href="/admin-next/assets/vue-vendor-CKToUHZx.js">
<link rel="modulepreload" crossorigin href="/admin-next/assets/vendor-BDiMbLwQ.js">
<link rel="modulepreload" crossorigin href="/admin-next/assets/element-plus-B8Fs_0jW.js">

View File

@@ -91,29 +91,27 @@ export const useDashboardStore = defineStore('dashboard', () => {
}
// 辅助函数获取系统时区某一天的起止UTC时间
// 输入:一个日期(以用户本地时间表示)
// 输入:一个系统时区的日期对象
// 输出该日期在系统时区的0点/23:59对应的UTC时间
function getSystemTimezoneDay(localDate, startOfDay = true) {
function getSystemTimezoneDay(systemTzDate, startOfDay = true) {
const offset = dashboardData.value.systemTimezone || 8
// 获取用户本地日期的年月日
const year = localDate.getFullYear()
const month = localDate.getMonth()
const day = localDate.getDate()
// 获取系统时区日期的年月日
const year = systemTzDate.getFullYear()
const month = systemTzDate.getMonth()
const day = systemTzDate.getDate()
// 创建UTC时间表示系统时区的日期
// 创建系统时区的日期时间
if (startOfDay) {
// 系统时区YYYY-MM-DD 00:00:00 对应的UTC时间
const utcDate = new Date(Date.UTC(year, month, day, 0, 0, 0, 0))
// 减去系统时区偏移得到UTC时间
utcDate.setUTCHours(utcDate.getUTCHours() - offset)
return utcDate
// 系统时区 YYYY-MM-DD 00:00:00
const systemDateTime = new Date(year, month, day, 0, 0, 0, 0)
// 转换为UTC时间减去时区偏移
return new Date(systemDateTime.getTime() - offset * 3600000)
} else {
// 系统时区YYYY-MM-DD 23:59:59.999 对应的UTC时间
const utcDate = new Date(Date.UTC(year, month, day, 23, 59, 59, 999))
// 减去系统时区偏移得到UTC时间
utcDate.setUTCHours(utcDate.getUTCHours() - offset)
return utcDate
// 系统时区 YYYY-MM-DD 23:59:59.999
const systemDateTime = new Date(year, month, day, 23, 59, 59, 999)
// 转换为UTC时间减去时区偏移
return new Date(systemDateTime.getTime() - offset * 3600000)
}
}
@@ -182,9 +180,24 @@ export const useDashboardStore = defineStore('dashboard', () => {
url += `granularity=hour`
if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) {
// 使用自定义时间范围
url += `&startDate=${encodeURIComponent(dateFilter.value.customRange[0])}`
url += `&endDate=${encodeURIComponent(dateFilter.value.customRange[1])}`
// 使用自定义时间范围 - 需要将系统时区时间转换为UTC
const convertToUTC = (systemTzTimeStr) => {
const systemTz = dashboardData.value.systemTimezone || 8
// 解析系统时区时间字符串
const [datePart, timePart] = systemTzTimeStr.split(' ')
const [year, month, day] = datePart.split('-').map(Number)
const [hours, minutes, seconds] = timePart.split(':').map(Number)
// 创建系统时区的Date对象
const systemDate = new Date(year, month - 1, day, hours, minutes, seconds)
// 转换为UTC
const utcTime = systemDate.getTime() - (systemTz * 3600000)
return new Date(utcTime).toISOString()
}
url += `&startDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[0]))}`
url += `&endDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[1]))}`
} else {
// 使用预设计算时间范围与loadApiKeysTrend保持一致
const now = new Date()
@@ -199,14 +212,14 @@ export const useDashboardStore = defineStore('dashboard', () => {
break
case 'yesterday':
// 昨天:基于系统时区的昨天
const yesterday = new Date(now)
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
startTime = getSystemTimezoneDay(yesterday, true)
endTime = getSystemTimezoneDay(yesterday, false)
break
case 'dayBefore':
// 前天:基于系统时区的前天
const dayBefore = new Date(now)
const dayBefore = new Date()
dayBefore.setDate(dayBefore.getDate() - 2)
startTime = getSystemTimezoneDay(dayBefore, true)
endTime = getSystemTimezoneDay(dayBefore, false)
@@ -260,9 +273,24 @@ export const useDashboardStore = defineStore('dashboard', () => {
url += `granularity=hour`
if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) {
// 使用自定义时间范围
url += `&startDate=${encodeURIComponent(dateFilter.value.customRange[0])}`
url += `&endDate=${encodeURIComponent(dateFilter.value.customRange[1])}`
// 使用自定义时间范围 - 需要将系统时区时间转换为UTC
const convertToUTC = (systemTzTimeStr) => {
const systemTz = dashboardData.value.systemTimezone || 8
// 解析系统时区时间字符串
const [datePart, timePart] = systemTzTimeStr.split(' ')
const [year, month, day] = datePart.split('-').map(Number)
const [hours, minutes, seconds] = timePart.split(':').map(Number)
// 创建系统时区的Date对象
const systemDate = new Date(year, month - 1, day, hours, minutes, seconds)
// 转换为UTC
const utcTime = systemDate.getTime() - (systemTz * 3600000)
return new Date(utcTime).toISOString()
}
url += `&startDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[0]))}`
url += `&endDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[1]))}`
} else {
// 使用预设计算时间范围与setDateFilterPreset保持一致
const now = new Date()
@@ -277,14 +305,14 @@ export const useDashboardStore = defineStore('dashboard', () => {
break
case 'yesterday':
// 昨天:基于系统时区的昨天
const yesterday = new Date(now)
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
startTime = getSystemTimezoneDay(yesterday, true)
endTime = getSystemTimezoneDay(yesterday, false)
break
case 'dayBefore':
// 前天:基于系统时区的前天
const dayBefore = new Date(now)
const dayBefore = new Date()
dayBefore.setDate(dayBefore.getDate() - 2)
startTime = getSystemTimezoneDay(dayBefore, true)
endTime = getSystemTimezoneDay(dayBefore, false)
@@ -346,18 +374,20 @@ export const useDashboardStore = defineStore('dashboard', () => {
startDate = new Date(now.getTime() - 24 * 60 * 60 * 1000)
break
case 'yesterday':
// 昨天:基于系统时区的昨天
const yesterday2 = new Date(now)
yesterday2.setDate(yesterday2.getDate() - 1)
startDate = getSystemTimezoneDay(yesterday2, true)
endDate = getSystemTimezoneDay(yesterday2, false)
// 昨天:获取本地时间的昨天
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
// 转换为系统时区的昨天0点和23:59
startDate = getSystemTimezoneDay(yesterday, true)
endDate = getSystemTimezoneDay(yesterday, false)
break
case 'dayBefore':
// 前天:基于系统时区的前天
const dayBefore2 = new Date(now)
dayBefore2.setDate(dayBefore2.getDate() - 2)
startDate = getSystemTimezoneDay(dayBefore2, true)
endDate = getSystemTimezoneDay(dayBefore2, false)
// 前天:获取本地时间的前天
const dayBefore = new Date()
dayBefore.setDate(dayBefore.getDate() - 2)
// 转换为系统时区的前天0点和23:59
startDate = getSystemTimezoneDay(dayBefore, true)
endDate = getSystemTimezoneDay(dayBefore, false)
break
}
} else {
@@ -381,19 +411,21 @@ export const useDashboardStore = defineStore('dashboard', () => {
dateFilter.value.customEnd = endDate.toISOString().split('T')[0]
// 设置 customRange 为 Element Plus 需要的格式
const formatDate = (date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
// 显示系统时区的时间
const formatDateForDisplay = (utcDate) => {
const systemTzDate = getDateInSystemTimezone(utcDate)
const year = systemTzDate.getUTCFullYear()
const month = String(systemTzDate.getUTCMonth() + 1).padStart(2, '0')
const day = String(systemTzDate.getUTCDate()).padStart(2, '0')
const hours = String(systemTzDate.getUTCHours()).padStart(2, '0')
const minutes = String(systemTzDate.getUTCMinutes()).padStart(2, '0')
const seconds = String(systemTzDate.getUTCSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
dateFilter.value.customRange = [
formatDate(startDate),
formatDate(endDate)
formatDateForDisplay(startDate),
formatDateForDisplay(endDate)
]
}
@@ -409,9 +441,19 @@ export const useDashboardStore = defineStore('dashboard', () => {
dateFilter.value.customStart = value[0].split(' ')[0]
dateFilter.value.customEnd = value[1].split(' ')[0]
// 检查日期范围限制
const start = new Date(value[0])
const end = new Date(value[1])
// 检查日期范围限制 - value中的时间已经是系统时区时间
const systemTz = dashboardData.value.systemTimezone || 8
// 解析系统时区时间
const parseSystemTime = (timeStr) => {
const [datePart, timePart] = timeStr.split(' ')
const [year, month, day] = datePart.split('-').map(Number)
const [hours, minutes, seconds] = timePart.split(':').map(Number)
return new Date(year, month - 1, day, hours, minutes, seconds)
}
const start = parseSystemTime(value[0])
const end = parseSystemTime(value[1])
if (trendGranularity.value === 'hour') {
// 小时粒度:限制 24 小时
@@ -433,7 +475,7 @@ export const useDashboardStore = defineStore('dashboard', () => {
refreshChartsData()
} else if (value === null) {
// 清空时恢复默认
setDateFilterPreset(trendGranularity.value === 'hour' ? '7days' : '7days')
setDateFilterPreset(trendGranularity.value === 'hour' ? 'last24h' : '7days')
}
}