mirror of
https://github.com/wess09/AzurLaneAutoScript.git
synced 2026-05-14 04:18:26 +08:00
fix(webui): align coin chart data with AP timeline
This commit is contained in:
parent
a1f5bdb334
commit
86ecf38ebe
@ -503,6 +503,16 @@ class Cl1Database:
|
||||
}
|
||||
|
||||
snapshots = data.get('coins_snapshots', [])
|
||||
if snapshots:
|
||||
last = snapshots[-1]
|
||||
try:
|
||||
same_yellow = int(last.get('yellow_coins', -1)) == snapshot['yellow_coins']
|
||||
same_purple = int(last.get('purple_coins', -1)) == snapshot['purple_coins']
|
||||
if same_yellow and same_purple and last.get('source') == source:
|
||||
return
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
snapshots.append(snapshot)
|
||||
# 保留最近 500 条记录,避免数据过大
|
||||
if len(snapshots) > 500:
|
||||
|
||||
@ -470,6 +470,7 @@ class AlasGUI(Frame):
|
||||
counts = []
|
||||
ap_list = []
|
||||
detail_sources = []
|
||||
chart_points = []
|
||||
is_detail_mode = False
|
||||
|
||||
today = _dt.now().date()
|
||||
@ -486,11 +487,13 @@ class AlasGUI(Frame):
|
||||
labels.append(p['dt'].strftime('%H:%M'))
|
||||
ap_list.append(p['ap'])
|
||||
detail_sources.append(p.get('source', '-'))
|
||||
chart_points.append(p)
|
||||
view_title = t("Gui.Stat.DetailChartTitle")
|
||||
else:
|
||||
for p in raw_points:
|
||||
labels.append(p['dt'].strftime('%m-%d %H:%M'))
|
||||
ap_list.append(p['ap'])
|
||||
chart_points.append(p)
|
||||
view_title = t("Gui.Stat.ViewTitleLine")
|
||||
is_detail_mode = False
|
||||
current_view = 'line'
|
||||
@ -498,6 +501,7 @@ class AlasGUI(Frame):
|
||||
for p in raw_points:
|
||||
labels.append(p['dt'].strftime('%m-%d %H:%M'))
|
||||
ap_list.append(p['ap'])
|
||||
chart_points.append(p)
|
||||
view_title = t("Gui.Stat.ViewTitleLine")
|
||||
else:
|
||||
from collections import OrderedDict
|
||||
@ -561,8 +565,7 @@ class AlasGUI(Frame):
|
||||
coins_stats_html = ''
|
||||
coins_legend_html = ''
|
||||
|
||||
if coins_timeline and current_view in ('line', 'detail'):
|
||||
show_coins = True
|
||||
if coins_timeline and chart_points and current_view in ('line', 'detail'):
|
||||
coins_raw_points = []
|
||||
for pt in coins_timeline:
|
||||
ts_raw = pt.get('ts', '')
|
||||
@ -579,29 +582,42 @@ class AlasGUI(Frame):
|
||||
|
||||
if coins_raw_points:
|
||||
coins_raw_points.sort(key=lambda p: p['dt'])
|
||||
for p in coins_raw_points:
|
||||
yellow_coins_list.append(p['yellow_coins'])
|
||||
purple_coins_list.append(p['purple_coins'])
|
||||
coins_sources_list.append(p.get('source', '-'))
|
||||
coins_idx = 0
|
||||
coins_last = len(coins_raw_points) - 1
|
||||
for p in chart_points:
|
||||
while coins_idx < coins_last:
|
||||
cur_delta = abs((coins_raw_points[coins_idx]['dt'] - p['dt']).total_seconds())
|
||||
next_delta = abs((coins_raw_points[coins_idx + 1]['dt'] - p['dt']).total_seconds())
|
||||
if next_delta > cur_delta:
|
||||
break
|
||||
coins_idx += 1
|
||||
coins_point = coins_raw_points[coins_idx]
|
||||
yellow_coins_list.append(coins_point['yellow_coins'])
|
||||
purple_coins_list.append(coins_point['purple_coins'])
|
||||
coins_sources_list.append(coins_point.get('source', '-'))
|
||||
|
||||
if yellow_coins_list:
|
||||
yc_cur = yellow_coins_list[-1]
|
||||
yc_change = yellow_coins_list[-1] - yellow_coins_list[0] if len(yellow_coins_list) >= 2 else 0
|
||||
valid_yellow_coins = [v for v in yellow_coins_list if v is not None]
|
||||
valid_purple_coins = [v for v in purple_coins_list if v is not None]
|
||||
show_coins = bool(valid_yellow_coins or valid_purple_coins)
|
||||
|
||||
if valid_yellow_coins:
|
||||
yc_cur = valid_yellow_coins[-1]
|
||||
yc_change = valid_yellow_coins[-1] - valid_yellow_coins[0] if len(valid_yellow_coins) >= 2 else 0
|
||||
yc_change_color = '#ef5350' if yc_change >= 0 else '#26a69a'
|
||||
yc_change_sign = '+' if yc_change >= 0 else ''
|
||||
yc_max = max(yellow_coins_list)
|
||||
yc_min = min(yellow_coins_list)
|
||||
yc_max = max(valid_yellow_coins)
|
||||
yc_min = min(valid_yellow_coins)
|
||||
|
||||
coins_stats_html += f'<div style="display:flex; flex-wrap:wrap; gap:12px; margin-bottom:4px; font-size:12px; color:#aaa;"><span>黄币: <b style="color:#ffd54f">{yc_cur}</b></span><span>变化: <b style="color:{yc_change_color}">{yc_change_sign}{yc_change}</b></span><span>最高: <b style="color:#ef5350">{yc_max}</b></span><span>最低: <b style="color:#26a69a">{yc_min}</b></span></div>'
|
||||
coins_legend_html += '<span style="display:flex; align-items:center; gap:4px;"><span style="width:12px; height:2px; background:#ffd54f; border-radius:1px; border-top:1px dashed #ffd54f;"></span>黄币</span>'
|
||||
|
||||
if purple_coins_list:
|
||||
pc_cur = purple_coins_list[-1]
|
||||
pc_change = purple_coins_list[-1] - purple_coins_list[0] if len(purple_coins_list) >= 2 else 0
|
||||
if valid_purple_coins:
|
||||
pc_cur = valid_purple_coins[-1]
|
||||
pc_change = valid_purple_coins[-1] - valid_purple_coins[0] if len(valid_purple_coins) >= 2 else 0
|
||||
pc_change_color = '#ef5350' if pc_change >= 0 else '#26a69a'
|
||||
pc_change_sign = '+' if pc_change >= 0 else ''
|
||||
pc_max = max(purple_coins_list)
|
||||
pc_min = min(purple_coins_list)
|
||||
pc_max = max(valid_purple_coins)
|
||||
pc_min = min(valid_purple_coins)
|
||||
|
||||
coins_stats_html += f'<div style="display:flex; flex-wrap:wrap; gap:12px; margin-bottom:4px; font-size:12px; color:#aaa;"><span>紫币: <b style="color:#ce93d8">{pc_cur}</b></span><span>变化: <b style="color:{pc_change_color}">{pc_change_sign}{pc_change}</b></span><span>最高: <b style="color:#ef5350">{pc_max}</b></span><span>最低: <b style="color:#26a69a">{pc_min}</b></span></div>'
|
||||
coins_legend_html += '<span style="display:flex; align-items:center; gap:4px;"><span style="width:12px; height:2px; background:#ce93d8; border-radius:1px; border-top:1px dashed #ce93d8;"></span>紫币</span>'
|
||||
|
||||
@ -96,12 +96,15 @@
|
||||
var coinsMin = Infinity, coinsMax = -Infinity;
|
||||
var yellowCoinsLen = yellowCoins ? yellowCoins.length : 0;
|
||||
var purpleCoinsLen = purpleCoins ? purpleCoins.length : 0;
|
||||
var hasCoins = showCoins && chartType === 'line' && (yellowCoinsLen > 0 || purpleCoinsLen > 0);
|
||||
if (showCoins && chartType === 'line') {
|
||||
for (var i = 0; i < yellowCoinsLen; i++) {
|
||||
if (yellowCoins[i] === null || yellowCoins[i] === undefined) continue;
|
||||
if (yellowCoins[i] < coinsMin) coinsMin = yellowCoins[i];
|
||||
if (yellowCoins[i] > coinsMax) coinsMax = yellowCoins[i];
|
||||
}
|
||||
for (var i = 0; i < purpleCoinsLen; i++) {
|
||||
if (purpleCoins[i] === null || purpleCoins[i] === undefined) continue;
|
||||
if (purpleCoins[i] < coinsMin) coinsMin = purpleCoins[i];
|
||||
if (purpleCoins[i] > coinsMax) coinsMax = purpleCoins[i];
|
||||
}
|
||||
@ -115,6 +118,47 @@
|
||||
function xOfLine(i) { return pad.l + (i / Math.max(nn - 1, 1)) * gW; }
|
||||
function yOf(v) { return pad.t + gH - (v - allMin) / (allMax - allMin) * gH; }
|
||||
function yOfCoins(v) { return pad.t + gH - (v - coinsMin) / (coinsMax - coinsMin) * gH; }
|
||||
function drawCoinsLine(xOf, start, end) {
|
||||
if (!hasCoins) return;
|
||||
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.lineJoin = "round";
|
||||
ctx.setLineDash([4, 2]);
|
||||
|
||||
if (yellowCoinsLen > 0) {
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "#ffd54f";
|
||||
var startedYellowCoins = false;
|
||||
for (var i = start; i < end && i < yellowCoinsLen; i++) {
|
||||
if (yellowCoins[i] === null || yellowCoins[i] === undefined) {
|
||||
startedYellowCoins = false;
|
||||
continue;
|
||||
}
|
||||
var x = xOf(i), y = yOfCoins(yellowCoins[i]);
|
||||
if (!startedYellowCoins) { ctx.moveTo(x, y); startedYellowCoins = true; }
|
||||
else ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
if (purpleCoinsLen > 0) {
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "#ce93d8";
|
||||
var startedPurpleCoins = false;
|
||||
for (var i = start; i < end && i < purpleCoinsLen; i++) {
|
||||
if (purpleCoins[i] === null || purpleCoins[i] === undefined) {
|
||||
startedPurpleCoins = false;
|
||||
continue;
|
||||
}
|
||||
var x = xOf(i), y = yOfCoins(purpleCoins[i]);
|
||||
if (!startedPurpleCoins) { ctx.moveTo(x, y); startedPurpleCoins = true; }
|
||||
else ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
|
||||
var candleSpace = gW / nn;
|
||||
var candleW = Math.max(3, Math.min(candleSpace * 0.6, 30));
|
||||
@ -136,8 +180,8 @@
|
||||
ctx.fillText(Math.round(v), pad.l - 8, y);
|
||||
}
|
||||
|
||||
if (showCoins && chartType === 'line' && yellowCoinsLen > 0) {
|
||||
ctx.fillStyle = "#ffd54f";
|
||||
if (hasCoins) {
|
||||
ctx.fillStyle = "#999";
|
||||
ctx.textAlign = "left";
|
||||
for (var i = 0; i <= 5; i++) {
|
||||
var v = coinsMin + (coinsMax - coinsMin) * (i / 5);
|
||||
@ -278,35 +322,7 @@
|
||||
drawMA(10, "#e91e63");
|
||||
}
|
||||
|
||||
if (showCoins && chartType === 'line' && yellowCoinsLen > 0) {
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.lineJoin = "round";
|
||||
ctx.setLineDash([4, 2]);
|
||||
|
||||
if (yellowCoinsLen > 0) {
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "#ffd54f";
|
||||
for (var i = 0; i < yellowCoinsLen; i++) {
|
||||
var x = xOfLine(i), y = yOfCoins(yellowCoins[i]);
|
||||
if (i === 0) ctx.moveTo(x, y);
|
||||
else ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
if (purpleCoinsLen > 0) {
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "#ce93d8";
|
||||
for (var i = 0; i < purpleCoinsLen; i++) {
|
||||
var x = xOfLine(i), y = yOfCoins(purpleCoins[i]);
|
||||
if (i === 0) ctx.moveTo(x, y);
|
||||
else ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
drawCoinsLine(xOfLine, 0, nn);
|
||||
|
||||
cv.addEventListener("mousemove", function(e) {
|
||||
var rect = cv.getBoundingClientRect();
|
||||
@ -375,17 +391,17 @@
|
||||
{ parts: [{ type: 'text', value: "来源: " }, { type: 'bold', value: source, style: { color: sourceColor } }] }
|
||||
];
|
||||
|
||||
if (showCoins && yellowCoinsLen > 0 && idx < yellowCoinsLen) {
|
||||
if (showCoins && yellowCoinsLen > 0 && idx < yellowCoinsLen && yellowCoins[idx] !== null && yellowCoins[idx] !== undefined) {
|
||||
var yc = yellowCoins[idx];
|
||||
var ycDiff = idx > 0 && idx < yellowCoinsLen ? (yc - yellowCoins[idx - 1]) : 0;
|
||||
var ycDiff = idx > 0 && yellowCoins[idx - 1] !== null && yellowCoins[idx - 1] !== undefined ? (yc - yellowCoins[idx - 1]) : 0;
|
||||
var ycColor = ycDiff >= 0 ? "#ef5350" : "#26a69a";
|
||||
var ycDiffStr = (ycDiff >= 0 ? "+" : "") + ycDiff;
|
||||
tooltipRows.push({ parts: [{ type: 'text', value: "黄币: " }, { type: 'bold', value: String(yc), style: { color: "#ffd54f" } }, { type: 'text', value: " (" + ycDiffStr + ")", style: { color: ycColor } }] });
|
||||
}
|
||||
|
||||
if (showCoins && purpleCoinsLen > 0 && idx < purpleCoinsLen) {
|
||||
if (showCoins && purpleCoinsLen > 0 && idx < purpleCoinsLen && purpleCoins[idx] !== null && purpleCoins[idx] !== undefined) {
|
||||
var pc = purpleCoins[idx];
|
||||
var pcDiff = idx > 0 && idx < purpleCoinsLen ? (pc - purpleCoins[idx - 1]) : 0;
|
||||
var pcDiff = idx > 0 && purpleCoins[idx - 1] !== null && purpleCoins[idx - 1] !== undefined ? (pc - purpleCoins[idx - 1]) : 0;
|
||||
var pcColor = pcDiff >= 0 ? "#ef5350" : "#26a69a";
|
||||
var pcDiffStr = (pcDiff >= 0 ? "+" : "") + pcDiff;
|
||||
tooltipRows.push({ parts: [{ type: 'text', value: "紫币: " }, { type: 'bold', value: String(pc), style: { color: "#ce93d8" } }, { type: 'text', value: " (" + pcDiffStr + ")", style: { color: pcColor } }] });
|
||||
@ -423,17 +439,17 @@
|
||||
{ parts: [{ type: 'text', value: "单次变化: " }, { type: 'bold', value: ds, style: { color: dc } }] }
|
||||
];
|
||||
|
||||
if (showCoins && yellowCoinsLen > 0 && idx < yellowCoinsLen) {
|
||||
if (showCoins && yellowCoinsLen > 0 && idx < yellowCoinsLen && yellowCoins[idx] !== null && yellowCoins[idx] !== undefined) {
|
||||
var yc = yellowCoins[idx];
|
||||
var ycDiff = idx > 0 && idx < yellowCoinsLen ? (yc - yellowCoins[idx - 1]) : 0;
|
||||
var ycDiff = idx > 0 && yellowCoins[idx - 1] !== null && yellowCoins[idx - 1] !== undefined ? (yc - yellowCoins[idx - 1]) : 0;
|
||||
var ycColor = ycDiff >= 0 ? "#ef5350" : "#26a69a";
|
||||
var ycDiffStr = (ycDiff >= 0 ? "+" : "") + ycDiff;
|
||||
tooltipRows.push({ parts: [{ type: 'text', value: "黄币: " }, { type: 'bold', value: String(yc), style: { color: "#ffd54f" } }, { type: 'text', value: " (" + ycDiffStr + ")", style: { color: ycColor } }] });
|
||||
}
|
||||
|
||||
if (showCoins && purpleCoinsLen > 0 && idx < purpleCoinsLen) {
|
||||
if (showCoins && purpleCoinsLen > 0 && idx < purpleCoinsLen && purpleCoins[idx] !== null && purpleCoins[idx] !== undefined) {
|
||||
var pc = purpleCoins[idx];
|
||||
var pcDiff = idx > 0 && idx < purpleCoinsLen ? (pc - purpleCoins[idx - 1]) : 0;
|
||||
var pcDiff = idx > 0 && purpleCoins[idx - 1] !== null && purpleCoins[idx - 1] !== undefined ? (pc - purpleCoins[idx - 1]) : 0;
|
||||
var pcColor = pcDiff >= 0 ? "#ef5350" : "#26a69a";
|
||||
var pcDiffStr = (pcDiff >= 0 ? "+" : "") + pcDiff;
|
||||
tooltipRows.push({ parts: [{ type: 'text', value: "紫币: " }, { type: 'bold', value: String(pc), style: { color: "#ce93d8" } }, { type: 'text', value: " (" + pcDiffStr + ")", style: { color: pcColor } }] });
|
||||
@ -552,6 +568,16 @@
|
||||
ctx.fillText(Math.round(v), pad.l - 8, y);
|
||||
}
|
||||
|
||||
if (hasCoins) {
|
||||
ctx.fillStyle = "#999";
|
||||
ctx.textAlign = "left";
|
||||
for (var i = 0; i <= 5; i++) {
|
||||
var v = coinsMin + (coinsMax - coinsMin) * (i / 5);
|
||||
var y = yOfCoins(v);
|
||||
ctx.fillText(Math.round(v), W - pad.r + 8, y);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fillStyle = "#666";
|
||||
ctx.font = "10px -apple-system, sans-serif";
|
||||
ctx.textAlign = "center";
|
||||
@ -594,6 +620,8 @@
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
drawCoinsLine(dxOf, visibleStart, visibleEnd);
|
||||
|
||||
var labelInterval = Math.max(1, Math.floor(visibleNn / 8));
|
||||
for (var i = visibleStart; i < visibleEnd; i += labelInterval) {
|
||||
var lx = dxOf(i);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user