From 84ce4013703d0dab2f07253c19a1ea35a2ef556a Mon Sep 17 00:00:00 2001 From: tbphp Date: Sat, 19 Jul 2025 18:36:19 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E6=8A=98=E7=BA=BF=E5=9B=BEdb?= =?UTF-8?q?=E6=97=B6=E5=8C=BA=E5=85=BC=E5=AE=B9=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/handler/dashboard_handler.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/handler/dashboard_handler.go b/internal/handler/dashboard_handler.go index 79df614..d4fdd61 100644 --- a/internal/handler/dashboard_handler.go +++ b/internal/handler/dashboard_handler.go @@ -107,10 +107,12 @@ func (s *Server) Stats(c *gin.Context) { func (s *Server) Chart(c *gin.Context) { groupID := c.Query("groupId") - twentyFourHoursAgo := time.Now().Add(-24 * time.Hour) + now := time.Now() + endHour := now.Truncate(time.Hour) + startHour := endHour.Add(-23 * time.Hour) var hourlyStats []models.GroupHourlyStat - query := s.DB.Where("time >= ? ", twentyFourHoursAgo) + query := s.DB.Where("time >= ? AND time < ?", startHour, endHour.Add(time.Hour)) if groupID != "" { query = query.Where("group_id = ?", groupID) } @@ -121,7 +123,7 @@ func (s *Server) Chart(c *gin.Context) { statsByHour := make(map[time.Time]map[string]int64) for _, stat := range hourlyStats { - hour := stat.Time.Truncate(time.Hour) + hour := stat.Time.Local().Truncate(time.Hour) if _, ok := statsByHour[hour]; !ok { statsByHour[hour] = make(map[string]int64) } @@ -133,7 +135,7 @@ func (s *Server) Chart(c *gin.Context) { var successData, failureData []int64 for i := range 24 { - hour := twentyFourHoursAgo.Add(time.Duration(i+1) * time.Hour).Truncate(time.Hour) + hour := startHour.Add(time.Duration(i) * time.Hour) labels = append(labels, hour.Format(time.RFC3339)) if data, ok := statsByHour[hour]; ok { From a7f61e6bb747a68b759a04f432ec56a524d4928a Mon Sep 17 00:00:00 2001 From: tbphp Date: Sat, 19 Jul 2025 18:45:09 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=8A=98=E7=BA=BF?= =?UTF-8?q?=E5=88=BB=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/LineChart.vue | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/web/src/components/LineChart.vue b/web/src/components/LineChart.vue index 89f52f5..e9401b8 100644 --- a/web/src/components/LineChart.vue +++ b/web/src/components/LineChart.vue @@ -94,7 +94,7 @@ const visibleLabels = computed(() => { return labels .map((label, index) => ({ text: formatTimeLabel(label), index })) - .filter((_, i) => i % step === 0); + .filter((_, i) => i % step === 1); }); // 位置计算函数 @@ -502,18 +502,6 @@ onMounted(() => { }" :style="{ opacity: isErrorDataset(dataset.label) ? 0.8 : 1 }" /> - - From 972d6764a9ea4a2f4c8f0435e16f11edff98ddac Mon Sep 17 00:00:00 2001 From: tbphp Date: Sat, 19 Jul 2025 18:50:53 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E6=8A=98=E7=BA=BF=E5=9B=BE0?= =?UTF-8?q?=E5=80=BC=E8=BF=9E=E7=BA=BF=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/LineChart.vue | 48 +++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/web/src/components/LineChart.vue b/web/src/components/LineChart.vue index e9401b8..96ef0d5 100644 --- a/web/src/components/LineChart.vue +++ b/web/src/components/LineChart.vue @@ -115,7 +115,7 @@ const getYPosition = (value: number) => { return padding.top + (1 - ratio) * plotHeight; }; -// Helper to find segments of non-zero data +// Helper to find segments of non-zero data (用于填充区域) const getSegments = (data: number[]) => { const segments: Array> = []; let currentSegment: Array<{ value: number; index: number }> = []; @@ -138,25 +138,41 @@ const getSegments = (data: number[]) => { return segments; }; -// 生成线条路径(处理零值点) +// 生成线条路径(连续线条,包括0值点) const generateLinePath = (data: number[]) => { - const segments = getSegments(data); - const pathParts: string[] = []; + if (data.length === 0) { + return ""; + } - segments.forEach(segment => { - if (segment.length > 1) { - const segmentPath = segment - .map((point, pointIndex) => { - const x = getXPosition(point.index); - const y = getYPosition(point.value); - return `${pointIndex === 0 ? "M" : "L"} ${x},${y}`; - }) - .join(" "); - pathParts.push(segmentPath); + // 找到第一个和最后一个非0值的位置 + let firstNonZeroIndex = -1; + let lastNonZeroIndex = -1; + + for (let i = 0; i < data.length; i++) { + if (data[i] > 0) { + if (firstNonZeroIndex === -1) { + firstNonZeroIndex = i; + } + lastNonZeroIndex = i; } - }); + } - return pathParts.join(" "); + // 如果没有非0值,返回空路径 + if (firstNonZeroIndex === -1) { + return ""; + } + + // 生成连续的路径,从第一个非0值到最后一个非0值 + const pathCommands: string[] = []; + + for (let i = firstNonZeroIndex; i <= lastNonZeroIndex; i++) { + const x = getXPosition(i); + const y = getYPosition(data[i]); + const command = i === firstNonZeroIndex ? "M" : "L"; + pathCommands.push(`${command} ${x},${y}`); + } + + return pathCommands.join(" "); }; // 生成填充区域路径(只为有数据的区域填充)