@@ -63,6 +63,8 @@ const api = {
6363 removeChannel : ( id ) => call ( "/api/notifications/channels/" + id , { method : "DELETE" } ) ,
6464 testChannel : ( b ) => call ( "/api/notifications/test" , { body : b } ) ,
6565 saveRules : ( b ) => call ( "/api/notifications/rules" , { method : "PUT" , body : b } ) ,
66+ reportPreview : ( ) => call ( "/api/report/preview" , { method : "POST" , body : { } } ) ,
67+ reportSend : ( ) => call ( "/api/report/send" , { method : "POST" , body : { } } ) ,
6668 login : ( b ) => call ( "/api/auth/login" , { body : b } ) ,
6769 logout : ( ) => call ( "/api/auth/logout" , { method : "POST" , body : { } } ) ,
6870 changePassword : ( b ) => call ( "/api/auth/password" , { body : b } ) ,
@@ -609,7 +611,33 @@ function renderNotify() {
609611 <div><div class="set-title">保存规则</div><div class="set-desc">应用阈值与间隔修改</div></div>
610612 <button class="btn btn-primary" id="rulesSave">保存</button>
611613 </div>
614+ </div>
615+ <div class="section-head" style="margin-top:20px"><h2>每日日报</h2>
616+ <span class="muted">定时汇总昨日「我的站点」经营情况并推送(时间按服务器时区)</span></div>
617+ <div class="panel settings-list">
618+ <div class="set-row">
619+ <div><div class="set-title">启用日报</div><div class="set-desc">每天在设定时间生成并发送昨日报告</div></div>
620+ <button class="toggle ${ state . settings . dailyReport ?. enabled ? "on" : "" } " id="dr-enabled"></button>
621+ </div>
622+ <div class="set-row">
623+ <div><div class="set-title">发送时间</div><div class="set-desc">服务器时区的每日时刻</div></div>
624+ <div class="field-inline"><input type="time" class="input small" style="width:112px;text-align:center" id="dr-time" value="${ esc ( state . settings . dailyReport ?. time || "09:00" ) } "></div>
625+ </div>
626+ <div class="set-row">
627+ <div><div class="set-title">发送渠道</div><div class="set-desc">不勾选 = 所有启用的渠道;日报较长,建议勾选邮件渠道</div></div>
628+ <div class="dr-channels">${ state . channels . map ( ( c ) =>
629+ `<label class="chk-line"><input type="checkbox" data-drch="${ c . id } "${ ( state . settings . dailyReport ?. channelIds || [ ] ) . includes ( c . id ) ? " checked" : "" } > ${ esc ( c . name ) } </label>` ) . join ( "" ) || '<span class="muted">先添加通知渠道</span>' } </div>
630+ </div>
631+ <div class="set-row">
632+ <div><div class="set-title">保存与测试</div><div class="set-desc">预览按当前数据生成的报告,或立即发送一次</div></div>
633+ <div class="field-inline">
634+ <button class="btn btn-ghost" id="dr-preview">预览</button>
635+ <button class="btn btn-ghost" id="dr-send">立即发送</button>
636+ <button class="btn btn-primary" id="dr-save">保存</button>
637+ </div>
638+ </div>
612639 </div>` ;
640+ $ ( "#dr-enabled" ) . onclick = ( ) => $ ( "#dr-enabled" ) . classList . toggle ( "on" ) ;
613641
614642 // 切换单位时把输入值换算过去(两个单位间必然是互换)
615643 $ ( "#rule-etaUnit" ) . onchange = ( ) => {
@@ -1588,6 +1616,8 @@ async function openTrend(station) {
15881616}
15891617$ ( "#trendClose" ) . onclick = ( ) => $ ( "#trendModal" ) . classList . remove ( "open" ) ;
15901618backdropClose ( "trendModal" , ( ) => $ ( "#trendModal" ) . classList . remove ( "open" ) ) ;
1619+ $ ( "#reportClose" ) . onclick = ( ) => $ ( "#reportModal" ) . classList . remove ( "open" ) ;
1620+ backdropClose ( "reportModal" , ( ) => $ ( "#reportModal" ) . classList . remove ( "open" ) ) ;
15911621
15921622function niceStep ( rough ) {
15931623 const pow = Math . pow ( 10 , Math . floor ( Math . log10 ( rough || 1 ) ) ) ;
@@ -1776,6 +1806,45 @@ $(".main").addEventListener("click", async (e) => {
17761806 return ;
17771807 }
17781808
1809+ // 日报:保存 / 预览 / 立即发送
1810+ if ( e . target . closest ( "#dr-save" ) ) {
1811+ try {
1812+ const channelIds = [ ...document . querySelectorAll ( "[data-drch]:checked" ) ] . map ( ( el ) => el . dataset . drch ) ;
1813+ const r = await api . saveSettings ( {
1814+ dailyReport : {
1815+ enabled : $ ( "#dr-enabled" ) . classList . contains ( "on" ) ,
1816+ time : $ ( "#dr-time" ) . value || "09:00" ,
1817+ channelIds,
1818+ } ,
1819+ } ) ;
1820+ state . settings = r . settings ;
1821+ toast ( "日报设置已保存" ) ;
1822+ } catch ( err ) { toast ( err . message , "err" ) ; }
1823+ return ;
1824+ }
1825+ const drPreview = e . target . closest ( "#dr-preview" ) ;
1826+ if ( drPreview ) {
1827+ drPreview . disabled = true ;
1828+ try {
1829+ const r = await api . reportPreview ( ) ;
1830+ $ ( "#reportText" ) . textContent = r . text ;
1831+ $ ( "#reportModal" ) . classList . add ( "open" ) ;
1832+ } catch ( err ) { toast ( err . message , "err" ) ; }
1833+ finally { drPreview . disabled = false ; }
1834+ return ;
1835+ }
1836+ const drSend = e . target . closest ( "#dr-send" ) ;
1837+ if ( drSend ) {
1838+ drSend . disabled = true ;
1839+ try {
1840+ const r = await api . reportSend ( ) ;
1841+ const ok = r . results . filter ( ( x ) => x . ok ) . length ;
1842+ toast ( `日报已发送:${ ok } /${ r . results . length } 个渠道成功` , ok ? "ok" : "err" ) ;
1843+ } catch ( err ) { toast ( err . message , "err" ) ; }
1844+ finally { drSend . disabled = false ; }
1845+ return ;
1846+ }
1847+
17791848 // 渠道操作
17801849 const chBtn = e . target . closest ( "[data-chact]" ) ;
17811850 if ( chBtn ) {
0 commit comments