fix: 修复前端页面的一些bug
This commit is contained in:
parent
0a5a6ec4e2
commit
87ff8f44d7
@ -38,6 +38,10 @@ func main() {
|
||||
var historicalEnd = flag.String("historical_end", "", "历史数据结束日期(格式YYYY-MM-DD)")
|
||||
// 覆盖风:使用彩云实况替换导出中的风速/风向
|
||||
var useWindOverride = flag.Bool("wind", false, "使用彩云实况覆盖导出CSV中的风速/风向")
|
||||
// 历史CSV导出
|
||||
var exportRangeOnly = flag.Bool("export_range", false, "按日期范围导出10分钟CSV(含ZTD融合),并退出。日期格式支持 YYYY-MM-DD 或 YYYYMMDD")
|
||||
var exportStart = flag.String("export_start", "", "导出起始日期(含),格式 YYYY-MM-DD 或 YYYYMMDD")
|
||||
var exportEnd = flag.String("export_end", "", "导出结束日期(含),格式 YYYY-MM-DD 或 YYYYMMDD")
|
||||
flag.Parse()
|
||||
|
||||
// 设置日志
|
||||
@ -87,6 +91,32 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
// 历史CSV范围导出
|
||||
if *exportRangeOnly {
|
||||
if *exportStart == "" || *exportEnd == "" {
|
||||
log.Fatalln("export_range 需要提供 --export_start 与 --export_end 日期(YYYY-MM-DD 或 YYYYMMDD)")
|
||||
}
|
||||
var opts tools.ExporterOptions
|
||||
if *useWindOverride {
|
||||
token := os.Getenv("CAIYUN_TOKEN")
|
||||
if token == "" {
|
||||
token = config.GetConfig().Forecast.CaiyunToken
|
||||
}
|
||||
if token == "" {
|
||||
log.Println("警告: 指定了 --wind 但未提供彩云 token,忽略风覆盖")
|
||||
} else {
|
||||
opts.OverrideWindWithCaiyun = true
|
||||
opts.CaiyunToken = token
|
||||
}
|
||||
}
|
||||
exporter := tools.NewExporterWithOptions(opts)
|
||||
if err := exporter.ExportRange(context.Background(), *exportStart, *exportEnd); err != nil {
|
||||
log.Fatalf("export_range 失败: %v", err)
|
||||
}
|
||||
log.Println("export_range 完成")
|
||||
return
|
||||
}
|
||||
|
||||
// 工具:按日期抓取当天0点到当前时间+3小时(两家)
|
||||
if *forecastDay != "" {
|
||||
if err := tools.RunForecastFetchForDay(context.Background(), *forecastDay); err != nil {
|
||||
|
||||
@ -317,6 +317,56 @@ func (e *Exporter) exportBucket(ctx context.Context, bucketStart, bucketEnd time
|
||||
return nil
|
||||
}
|
||||
|
||||
// 导出一个日期范围内的全部10分钟桶(CST),start 与 end 为“日期起止(含)”
|
||||
func (e *Exporter) ExportRange(ctx context.Context, startDate, endDate string) error {
|
||||
// 解析日期,支持 YYYY-MM-DD 或 YYYYMMDD
|
||||
parse := func(s string) (time.Time, error) {
|
||||
if len(s) == 8 {
|
||||
// YYYYMMDD -> YYYY-MM-DD
|
||||
s = s[:4] + "-" + s[4:6] + "-" + s[6:8]
|
||||
}
|
||||
loc := e.loc
|
||||
if loc == nil {
|
||||
loc = time.FixedZone("CST", 8*3600)
|
||||
}
|
||||
return time.ParseInLocation("2006-01-02", s, loc)
|
||||
}
|
||||
|
||||
fromDay, err := parse(startDate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("起始日期解析失败: %v", err)
|
||||
}
|
||||
toDay, err := parse(endDate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("结束日期解析失败: %v", err)
|
||||
}
|
||||
if toDay.Before(fromDay) {
|
||||
return fmt.Errorf("结束日期早于起始日期")
|
||||
}
|
||||
|
||||
// 日期起止 -> 时间范围(含):[fromDay 00:00, toDay 23:59:59]
|
||||
from := time.Date(fromDay.Year(), fromDay.Month(), fromDay.Day(), 0, 0, 0, 0, e.loc)
|
||||
to := time.Date(toDay.Year(), toDay.Month(), toDay.Day(), 23, 59, 59, 0, e.loc)
|
||||
|
||||
firstBucket := from.Truncate(10 * time.Minute)
|
||||
lastBucket := to.Truncate(10 * time.Minute)
|
||||
|
||||
for b := firstBucket; !b.After(lastBucket); b = b.Add(10 * time.Minute) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
bucketStart := b
|
||||
bucketEnd := b.Add(10 * time.Minute)
|
||||
if err := e.exportBucket(ctx, bucketStart, bucketEnd); err != nil {
|
||||
// 不中断整个范围导出,记录错误继续
|
||||
e.logger.Printf("范围导出: 桶 %s-%s 导出失败: %v", bucketStart.Format("2006-01-02 15:04:05"), bucketEnd.Format("2006-01-02 15:04:05"), err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Exporter) lookupZTD(ctx context.Context, deviceID string, bucketEnd time.Time) string {
|
||||
if e.my == nil {
|
||||
return ""
|
||||
|
||||
@ -8,13 +8,18 @@ const WeatherTable = {
|
||||
}
|
||||
|
||||
const showPastEl = document.getElementById('showPastForecast');
|
||||
const nowTs = Date.now();
|
||||
const future3hTs = nowTs + 3 * 60 * 60 * 1000;
|
||||
const endInput = document.getElementById('endDate')?.value;
|
||||
let endTs = Date.now();
|
||||
if (endInput) {
|
||||
const d = new Date(endInput);
|
||||
if (!isNaN(d)) endTs = d.getTime();
|
||||
}
|
||||
const future3hTs = endTs + 3 * 60 * 60 * 1000;
|
||||
const showPast = !!(showPastEl && showPastEl.checked);
|
||||
const displayedForecast = forecastData.filter(item => {
|
||||
const t = new Date(item.date_time).getTime();
|
||||
const isFuture3h = t > nowTs && t <= future3hTs;
|
||||
const isPast = t <= nowTs;
|
||||
const isFuture3h = t > endTs && t <= future3hTs;
|
||||
const isPast = t <= endTs;
|
||||
return isFuture3h || (showPast && isPast);
|
||||
});
|
||||
const taggedHistory = historyData.map(item => ({ ...item, __source: '实测' }));
|
||||
|
||||
@ -533,7 +533,7 @@
|
||||
</div>
|
||||
|
||||
<div class="table-container" id="tableContainer">
|
||||
<div id="forecastToggleContainer" style="padding: 8px 12px; font-size: 12px; color: #666; display: none; display: flex; justify-content: center; align-items: center;">
|
||||
<div id="forecastToggleContainer" style="padding: 8px 12px;font-size: 12px;color: #666;display: none;display: flex;justify-content: flex-start;align-items: center;align-content: center;">
|
||||
<label style="display: flex; align-items: flex-start; gap: 5px;">
|
||||
<input type="checkbox" id="showPastForecast" style="vertical-align: middle;" x-model="showPastForecast" @change="window.WeatherTable.display(window.WeatherApp.cachedHistoryData, window.WeatherApp.cachedForecastData)">
|
||||
显示历史预报
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user