Merge pull request #32 from tbphp/fix-line-chart

fix: 折线图时区及展示问题
This commit is contained in:
tbphp
2025-07-19 18:52:38 +08:00
committed by GitHub
2 changed files with 39 additions and 33 deletions

View File

@@ -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 {

View File

@@ -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);
});
// 位置计算函数
@@ -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<Array<{ value: number; index: number }>> = [];
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[] = [];
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);
if (data.length === 0) {
return "";
}
});
return pathParts.join(" ");
// 找到第一个和最后一个非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;
}
}
// 如果没有非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(" ");
};
// 生成填充区域路径(只为有数据的区域填充)
@@ -502,18 +518,6 @@ onMounted(() => {
}"
:style="{ opacity: isErrorDataset(dataset.label) ? 0.8 : 1 }"
/>
<!-- 零值点用灰色小点表示 -->
<circle
v-else
:cx="getXPosition(pointIndex)"
:cy="getYPosition(value)"
r="1.5"
fill="#d1d5db"
stroke="#d1d5db"
stroke-width="1"
class="data-point-zero"
opacity="0.6"
/>
</g>
</g>