fix: 保持仪表盘趋势图非负并纠正小时区间

- 小时粒度请求使用用户选择的起止时间,避免近24小时被截成整天
  - 修正日期展示格式化逻辑,减少时区偏移导致的窗口错位
  - 趋势图 Y 轴(Token/请求数/费用等)强制最小值为 0,防止出现负刻度
This commit is contained in:
atoz03
2025-12-04 17:05:36 +08:00
parent 4919e392a5
commit 95ef04c1a3
2 changed files with 25 additions and 81 deletions

View File

@@ -306,26 +306,9 @@ export const useDashboardStore = defineStore('dashboard', () => {
url += `granularity=hour` url += `granularity=hour`
if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) { if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) {
// 使用自定义时间范围 - 需要将系统时区时间转换为UTC // 使用自定义时间范围 - 直接按系统时区字符串传递,避免额外时区偏移导致窗口错位
const convertToUTC = (systemTzTimeStr) => { url += `&startDate=${encodeURIComponent(dateFilter.value.customRange[0])}`
// 固定使用UTC+8因为后端系统时区是UTC+8 url += `&endDate=${encodeURIComponent(dateFilter.value.customRange[1])}`
const systemTz = 8
// 解析系统时区时间字符串
const [datePart, timePart] = systemTzTimeStr.split(' ')
const [year, month, day] = datePart.split('-').map(Number)
const [hours, minutes, seconds] = timePart.split(':').map(Number)
// 创建UTC时间使其在系统时区显示为用户选择的时间
// 例如:用户选择 UTC+8 的 2025-07-25 00:00:00
// 对应的UTC时间是 2025-07-24 16:00:00
const utcDate = new Date(
Date.UTC(year, month - 1, day, hours - systemTz, minutes, seconds)
)
return utcDate.toISOString()
}
url += `&startDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[0]))}`
url += `&endDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[1]))}`
} else { } else {
// 使用预设计算时间范围与loadApiKeysTrend保持一致 // 使用预设计算时间范围与loadApiKeysTrend保持一致
const now = new Date() const now = new Date()
@@ -397,21 +380,9 @@ export const useDashboardStore = defineStore('dashboard', () => {
// 如果是自定义时间范围或小时粒度,传递具体的时间参数 // 如果是自定义时间范围或小时粒度,传递具体的时间参数
if (dateFilter.value.type === 'custom' || currentGranularity === 'hour') { if (dateFilter.value.type === 'custom' || currentGranularity === 'hour') {
if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) { if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) {
// 系统时区时间转换为UTC // 系统时区字符串直传,避免额外时区转换
const convertToUTC = (systemTzTimeStr) => { url += `&startDate=${encodeURIComponent(dateFilter.value.customRange[0])}`
const systemTz = 8 url += `&endDate=${encodeURIComponent(dateFilter.value.customRange[1])}`
const [datePart, timePart] = systemTzTimeStr.split(' ')
const [year, month, day] = datePart.split('-').map(Number)
const [hours, minutes, seconds] = timePart.split(':').map(Number)
const utcDate = new Date(
Date.UTC(year, month - 1, day, hours - systemTz, minutes, seconds)
)
return utcDate.toISOString()
}
url += `&startDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[0]))}`
url += `&endDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[1]))}`
} else if (currentGranularity === 'hour' && dateFilter.value.type === 'preset') { } else if (currentGranularity === 'hour' && dateFilter.value.type === 'preset') {
// 小时粒度的预设时间范围 // 小时粒度的预设时间范围
const now = new Date() const now = new Date()
@@ -497,26 +468,9 @@ export const useDashboardStore = defineStore('dashboard', () => {
url += `granularity=hour` url += `granularity=hour`
if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) { if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) {
// 使用自定义时间范围 - 需要将系统时区时间转换为UTC // 使用自定义时间范围 - 系统时区字符串直传
const convertToUTC = (systemTzTimeStr) => { url += `&startDate=${encodeURIComponent(dateFilter.value.customRange[0])}`
// 固定使用UTC+8因为后端系统时区是UTC+8 url += `&endDate=${encodeURIComponent(dateFilter.value.customRange[1])}`
const systemTz = 8
// 解析系统时区时间字符串
const [datePart, timePart] = systemTzTimeStr.split(' ')
const [year, month, day] = datePart.split('-').map(Number)
const [hours, minutes, seconds] = timePart.split(':').map(Number)
// 创建UTC时间使其在系统时区显示为用户选择的时间
// 例如:用户选择 UTC+8 的 2025-07-25 00:00:00
// 对应的UTC时间是 2025-07-24 16:00:00
const utcDate = new Date(
Date.UTC(year, month - 1, day, hours - systemTz, minutes, seconds)
)
return utcDate.toISOString()
}
url += `&startDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[0]))}`
url += `&endDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[1]))}`
} else { } else {
// 使用预设计算时间范围与setDateFilterPreset保持一致 // 使用预设计算时间范围与setDateFilterPreset保持一致
const now = new Date() const now = new Date()
@@ -604,20 +558,8 @@ export const useDashboardStore = defineStore('dashboard', () => {
url += `granularity=hour` url += `granularity=hour`
if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) { if (dateFilter.value.customRange && dateFilter.value.customRange.length === 2) {
const convertToUTC = (systemTzTimeStr) => { url += `&startDate=${encodeURIComponent(dateFilter.value.customRange[0])}`
const systemTz = 8 url += `&endDate=${encodeURIComponent(dateFilter.value.customRange[1])}`
const [datePart, timePart] = systemTzTimeStr.split(' ')
const [year, month, day] = datePart.split('-').map(Number)
const [hours, minutes, seconds] = timePart.split(':').map(Number)
const utcDate = new Date(
Date.UTC(year, month - 1, day, hours - systemTz, minutes, seconds)
)
return utcDate.toISOString()
}
url += `&startDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[0]))}`
url += `&endDate=${encodeURIComponent(convertToUTC(dateFilter.value.customRange[1]))}`
} else { } else {
const now = new Date() const now = new Date()
let startTime let startTime
@@ -750,17 +692,14 @@ export const useDashboardStore = defineStore('dashboard', () => {
} }
const formatDateForDisplay = (date) => { const formatDateForDisplay = (date) => {
// 固定使用UTC+8来显示时间 // 使用本地时间直接格式化,避免多余的时区偏移导致日期错位
const systemTz = 8 const localTime = new Date(date)
const tzOffset = systemTz * 60 * 60 * 1000 const year = localTime.getFullYear()
const localTime = new Date(date.getTime() + tzOffset) const month = String(localTime.getMonth() + 1).padStart(2, '0')
const day = String(localTime.getDate()).padStart(2, '0')
const year = localTime.getUTCFullYear() const hours = String(localTime.getHours()).padStart(2, '0')
const month = String(localTime.getUTCMonth() + 1).padStart(2, '0') const minutes = String(localTime.getMinutes()).padStart(2, '0')
const day = String(localTime.getUTCDate()).padStart(2, '0') const seconds = String(localTime.getSeconds()).padStart(2, '0')
const hours = String(localTime.getUTCHours()).padStart(2, '0')
const minutes = String(localTime.getUTCMinutes()).padStart(2, '0')
const seconds = String(localTime.getUTCSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
} }

View File

@@ -1036,6 +1036,7 @@ function createUsageTrendChart() {
type: 'linear', type: 'linear',
display: true, display: true,
position: 'left', position: 'left',
min: 0,
title: { title: {
display: true, display: true,
text: 'Token数量', text: 'Token数量',
@@ -1055,6 +1056,7 @@ function createUsageTrendChart() {
type: 'linear', type: 'linear',
display: true, display: true,
position: 'right', position: 'right',
min: 0,
title: { title: {
display: true, display: true,
text: '请求数', text: '请求数',
@@ -1073,7 +1075,8 @@ function createUsageTrendChart() {
y2: { y2: {
type: 'linear', type: 'linear',
display: false, // 隐藏费用轴在tooltip中显示 display: false, // 隐藏费用轴在tooltip中显示
position: 'right' position: 'right',
min: 0
} }
} }
} }
@@ -1253,6 +1256,7 @@ function createApiKeysUsageTrendChart() {
}, },
y: { y: {
beginAtZero: true, beginAtZero: true,
min: 0,
title: { title: {
display: true, display: true,
text: apiKeysTrendMetric.value === 'tokens' ? 'Token 数量' : '请求次数', text: apiKeysTrendMetric.value === 'tokens' ? 'Token 数量' : '请求次数',
@@ -1428,6 +1432,7 @@ function createAccountUsageTrendChart() {
}, },
y: { y: {
beginAtZero: true, beginAtZero: true,
min: 0,
title: { title: {
display: true, display: true,
text: '消耗金额 (USD)', text: '消耗金额 (USD)',