@@ -108,13 +108,15 @@ function fmtClock(ts) {
108108 const p = ( n ) => String ( n ) . padStart ( 2 , "0" ) ;
109109 return `${ d . getMonth ( ) + 1 } /${ d . getDate ( ) } ${ p ( d . getHours ( ) ) } :${ p ( d . getMinutes ( ) ) } ` ;
110110}
111+ function fmtEtaText ( days ) {
112+ return days >= 1 ? `${ days } 天` : `${ Math . max ( 1 , Math . round ( days * 24 ) ) } 小时` ;
113+ }
111114function etaText ( p ) {
112115 if ( ! p ) return null ;
113116 if ( p . burnPerDay === 0 ) return { text : "近期无消耗" , cls : "" } ;
114117 if ( p . etaDays == null ) return null ;
115118 const cls = p . etaDays <= ( state . rules . etaDays ?? 3 ) ? "danger" : p . etaDays <= 7 ? "warn" : "" ;
116- const t = p . etaDays >= 1 ? `${ p . etaDays } 天` : `${ Math . max ( 1 , Math . round ( p . etaDays * 24 ) ) } 小时` ;
117- return { text : `≈ ${ usd ( p . burnPerDay ) } /天 · 预计 ${ t } 后耗尽` , cls } ;
119+ return { text : `≈ ${ usd ( p . burnPerDay ) } /天 · 预计 ${ fmtEtaText ( p . etaDays ) } 后耗尽` , cls } ;
118120}
119121
120122// ---- 登录 -------------------------------------------------------------------
@@ -175,7 +177,13 @@ function stationRow(s) {
175177 ? `<div class="st-bar"><div class="progress"><i class="${ cls } " style="width:${ usedPct } %"></i></div><span class="st-usage">已用 ${ usd ( b . used ) } / ${ usd ( b . total ) } </span></div>`
176178 : "" ;
177179 const eta = etaText ( s . prediction ) ;
178- const predict = eta ? `<div class="st-predict"><span class="${ eta . cls } ">${ eta . text } </span><span>· 点击查看趋势</span></div>` : "" ;
180+ const pieces = [ ] ;
181+ if ( b && b . ok && s . todayUsed != null ) {
182+ pieces . push ( `<span>今日消耗 ${ s . todayIsEstimate ? "≈" : "" } ${ usd ( s . todayUsed ) } </span>` ) ;
183+ }
184+ if ( eta ) pieces . push ( `<span class="${ eta . cls } ">${ eta . text } </span>` ) ;
185+ if ( pieces . length ) pieces . push ( "<span>点击查看趋势</span>" ) ;
186+ const predict = pieces . length ? `<div class="st-predict">${ pieces . join ( "<span>·</span>" ) } </div>` : "" ;
179187 return `
180188 <div class="st-row" data-id="${ s . id } ">
181189 <div class="st-plate">${ PLATE [ s . type ] || "?" } </div>
@@ -207,14 +215,18 @@ function renderDashboard() {
207215 const totalRemaining = okList . reduce ( ( a , s ) => a + s . balance . remaining , 0 ) ;
208216 const totalUsed = okList . reduce ( ( a , s ) => a + s . balance . used , 0 ) ;
209217 const totalBurn = list . reduce ( ( a , s ) => a + ( s . prediction ?. burnPerDay || 0 ) , 0 ) ;
218+ const todayTotal = list . reduce ( ( a , s ) => a + ( s . todayUsed || 0 ) , 0 ) ;
219+ // 任一站点的今日消耗是历史推算值时,合计也只能算约数
220+ const todayApprox = list . some ( ( s ) => ( s . todayUsed || 0 ) > 0 && s . todayIsEstimate ) ;
210221 const lowCount = list . filter ( ( s ) => [ "warn" , "danger" ] . includes ( statusOf ( s ) ) ) . length ;
211222 const errCount = list . filter ( ( s ) => statusOf ( s ) === "error" ) . length ;
212223
213224 $ ( "#headerActions" ) . innerHTML = HDR_BTNS ;
214225 const stats = `
215- <div class="stats">
226+ <div class="stats stats-5 ">
216227 <div class="stat-card"><div class="label">总剩余余额</div><div class="value">${ usd ( totalRemaining ) } </div></div>
217- <div class="stat-card"><div class="label">日均总消耗</div><div class="value">${ totalBurn > 0 ? usd ( totalBurn ) : "—" } </div></div>
228+ <div class="stat-card"><div class="label">今日总消耗</div><div class="value">${ todayApprox ? "≈ " : "" } ${ usd ( todayTotal ) } </div></div>
229+ <div class="stat-card"><div class="label">日均消耗(估算)</div><div class="value">${ totalBurn > 0 ? usd ( totalBurn ) : "—" } </div></div>
218230 <div class="stat-card"><div class="label">低余额 / 耗尽</div><div class="value ${ lowCount ? "warn" : "" } ">${ lowCount } <small>个</small></div></div>
219231 <div class="stat-card"><div class="label">查询异常</div><div class="value ${ errCount ? "danger" : "" } ">${ errCount } <small>个</small></div></div>
220232 </div>` ;
@@ -484,8 +496,14 @@ function renderNotify() {
484496 ${ tg ( "onRecover" , "恢复正常" , "从异常状态恢复后通知" ) }
485497 ${ tg ( "onEta" , "耗尽预警" , "按消耗速度预计即将耗尽时通知" ) }
486498 <div class="set-row">
487- <div><div class="set-title">耗尽预警阈值</div><div class="set-desc">预计 N 天内耗尽则触发「耗尽预警」</div></div>
488- <div class="field-inline"><input class="input small" id="rule-etaDays" value="${ r . etaDays ?? 3 } "><span class="set-desc">天</span></div>
499+ <div><div class="set-title">耗尽预警阈值</div><div class="set-desc">预计在该时间内耗尽则触发「耗尽预警」,可按天或小时设置</div></div>
500+ <div class="field-inline">
501+ <input class="input small" id="rule-etaVal" value="${ etaRuleDisplay ( r ) } ">
502+ <select class="select small" id="rule-etaUnit">
503+ <option value="days"${ r . etaUnit === "hours" ? "" : " selected" } >天</option>
504+ <option value="hours"${ r . etaUnit === "hours" ? " selected" : "" } >小时</option>
505+ </select>
506+ </div>
489507 </div>
490508 <div class="set-row">
491509 <div><div class="set-title">重复提醒间隔</div><div class="set-desc">同一异常持续存在时,每隔 N 小时再次提醒(0 = 只提醒一次)</div></div>
@@ -496,6 +514,20 @@ function renderNotify() {
496514 <button class="btn btn-primary" id="rulesSave">保存</button>
497515 </div>
498516 </div>` ;
517+
518+ // 切换单位时把输入值换算过去(两个单位间必然是互换)
519+ $ ( "#rule-etaUnit" ) . onchange = ( ) => {
520+ const inp = $ ( "#rule-etaVal" ) ;
521+ const v = Number ( inp . value ) ;
522+ if ( ! Number . isFinite ( v ) || v <= 0 ) return ;
523+ inp . value = $ ( "#rule-etaUnit" ) . value === "hours" ? + ( v * 24 ) . toFixed ( 2 ) : + ( v / 24 ) . toFixed ( 2 ) ;
524+ } ;
525+ }
526+
527+ // 阈值内部按天存储;界面按所选单位展示
528+ function etaRuleDisplay ( r ) {
529+ const days = Number ( r . etaDays ?? 3 ) ;
530+ return r . etaUnit === "hours" ? + ( days * 24 ) . toFixed ( 2 ) : + days . toFixed ( 2 ) ;
499531}
500532
501533// ---- 设置页 -----------------------------------------------------------------
@@ -736,8 +768,9 @@ async function openTrend(station) {
736768 const eta = etaText ( prediction ) ;
737769 $ ( "#trendStats" ) . innerHTML = `
738770 <div class="stat-card"><div class="label">当前余额</div><div class="value">${ b ?. ok ? usd ( b . remaining ) : "—" } </div></div>
739- <div class="stat-card"><div class="label">日均消耗</div><div class="value">${ prediction ?. burnPerDay > 0 ? usd ( prediction . burnPerDay ) : "—" } </div></div>
740- <div class="stat-card"><div class="label">预计耗尽</div><div class="value ${ eta ?. cls || "" } ">${ prediction ?. etaDays != null ? prediction . etaDays + " 天" : "—" } </div></div>` ;
771+ <div class="stat-card"><div class="label">今日消耗</div><div class="value">${ station . todayUsed != null ? ( station . todayIsEstimate ? "≈ " : "" ) + usd ( station . todayUsed ) : "—" } </div></div>
772+ <div class="stat-card"><div class="label">日均消耗(估算)</div><div class="value">${ prediction ?. burnPerDay > 0 ? usd ( prediction . burnPerDay ) : "—" } </div></div>
773+ <div class="stat-card"><div class="label">预计耗尽</div><div class="value ${ eta ?. cls || "" } ">${ prediction ?. etaDays != null ? fmtEtaText ( prediction . etaDays ) : "—" } </div></div>` ;
741774 drawChart ( $ ( "#trendChart" ) , points , prediction ) ;
742775 } catch ( e ) {
743776 if ( seq !== trendSeq ) return ;
@@ -884,13 +917,16 @@ $(".main").addEventListener("click", async (e) => {
884917 }
885918 if ( e . target . closest ( "#rulesSave" ) ) {
886919 try {
920+ const unit = $ ( "#rule-etaUnit" ) . value ;
921+ const val = Number ( $ ( "#rule-etaVal" ) . value ) ;
887922 const r = await api . saveRules ( {
888- etaDays : Number ( $ ( "#rule-etaDays" ) . value ) ,
923+ etaDays : unit === "hours" ? val / 24 : val , // 内部统一按天
924+ etaUnit : unit ,
889925 renotifyHours : Number ( $ ( "#rule-renotify" ) . value ) ,
890926 } ) ;
891927 state . rules = r . rules ;
892- // 回显服务端钳制后的值(比如 0 天会被修正为 3 ),不然界面显示的是没生效的输入
893- $ ( "#rule-etaDays " ) . value = state . rules . etaDays ;
928+ // 回显服务端钳制后的值(如非法输入被忽略、下限 1 小时 ),不然界面显示的是没生效的输入
929+ $ ( "#rule-etaVal " ) . value = etaRuleDisplay ( state . rules ) ;
894930 $ ( "#rule-renotify" ) . value = state . rules . renotifyHours ;
895931 toast ( "规则已保存" ) ;
896932 } catch ( err ) { toast ( err . message , "err" ) ; }
0 commit comments