diff --git a/frontend/analysis.html b/frontend/analysis.html index d6f4762..63d9af4 100644 --- a/frontend/analysis.html +++ b/frontend/analysis.html @@ -19,13 +19,43 @@ body{font-family:-apple-system,BlinkMacSystemFont,"SF Pro Display","PingFang SC" .nav-tab{padding:7px 20px;border-radius:6px;font-size:13px;font-weight:500;color:var(--text2);text-decoration:none;transition:all .2s;cursor:pointer;white-space:nowrap} .nav-tab:hover{color:var(--text);background:rgba(255,255,255,.5)} .nav-tab.active{background:#fff;color:var(--text);box-shadow:0 1px 4px rgba(0,0,0,.08)} -.header-right{display:flex;align-items:center;gap:6px;flex-shrink:0} -.header-right a{color:var(--text2);text-decoration:none;font-size:12px;padding:6px 14px;border-radius:var(--radius-xs);transition:all .2s;white-space:nowrap} -.header-right a:hover{background:rgba(0,0,0,.04);color:var(--text)} +.header-right a,.hdr-btn{color:var(--text2);text-decoration:none;font-size:12px;padding:6px 14px;border-radius:var(--radius-xs);transition:all .2s;background:transparent;border:none;cursor:pointer;font-family:inherit} +.header-right a:hover,.hdr-btn:hover{background:rgba(0,0,0,.04);color:var(--text)} .header-right .admin-link{color:var(--blue);font-weight:500} .header-right .logout-link{color:var(--red)} .user-info{color:var(--text);font-size:13px;font-weight:500;cursor:pointer;padding:6px 14px;border-radius:var(--radius-xs);transition:all .2s;background:rgba(0,0,0,.02)} .user-info:hover{background:rgba(0,0,0,.06)} +.notif-wrap{position:relative} +.notif-bell{cursor:pointer;padding:6px 8px;border-radius:var(--radius-xs);transition:all .2s;color:var(--text2)} +.notif-bell:hover{background:rgba(0,0,0,.04);color:var(--text)} +.notif-badge{position:absolute;top:2px;right:2px;min-width:16px;height:16px;background:var(--red);color:#fff;border-radius:8px;font-size:10px;font-weight:600;display:flex;align-items:center;justify-content:center;padding:0 4px} +.notif-dropdown{display:none;position:absolute;right:0;top:calc(100% + 8px);background:rgba(255,255,255,.96);backdrop-filter:blur(24px);border:1px solid var(--border);border-radius:var(--radius);box-shadow:0 12px 48px rgba(0,0,0,.15);width:360px;max-height:480px;overflow:hidden;z-index:60} +.notif-dropdown.show{display:block} +.notif-header{padding:14px 16px;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center} +.notif-header h4{font-size:14px;font-weight:600;color:var(--text)} +.notif-header .mark-all{font-size:12px;color:var(--blue);cursor:pointer;background:none;border:none;font-family:inherit} +.notif-header .mark-all:hover{text-decoration:underline} +.notif-list{overflow-y:auto;max-height:380px} +.notif-item{padding:12px 16px;border-bottom:1px solid var(--border);cursor:pointer;transition:background .15s;display:flex;gap:12px;align-items:flex-start} +.notif-item:hover{background:rgba(0,0,0,.02)} +.notif-item.unread{background:rgba(0,113,227,.03)} +.notif-item .notif-icon{width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0} +.notif-icon.repayment_due{background:rgba(0,113,227,.08)} +.notif-icon.repayment_overdue{background:rgba(255,59,48,.08)} +.notif-icon.system{background:rgba(175,82,222,.08)} +.notif-icon.account{background:rgba(52,199,89,.08)} +.notif-item .notif-content{flex:1;min-width:0} +.notif-item .notif-title{font-size:13px;font-weight:500;color:var(--text);margin-bottom:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.notif-item .notif-msg{font-size:12px;color:var(--text3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.notif-item .notif-time{font-size:11px;color:var(--text3);white-space:nowrap} +.notif-empty{text-align:center;padding:32px;color:var(--text3);font-size:13px} +.export-wrap{position:relative} +.export-menu{display:none;position:absolute;right:0;top:calc(100% + 4px);background:rgba(255,255,255,.95);backdrop-filter:blur(20px);border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:0 8px 32px rgba(0,0,0,.12);min-width:160px;z-index:50;overflow:hidden} +.export-menu.show{display:block} +.export-menu a{display:block;padding:10px 18px;font-size:13px;color:var(--text);text-decoration:none;transition:background .15s} +.export-menu a:hover{background:rgba(0,0,0,.04)} +.user-info{color:var(--text);font-size:13px;font-weight:500;cursor:pointer;padding:6px 14px;border-radius:var(--radius-xs);transition:all .2s;background:rgba(0,0,0,.02)} +.user-info:hover{background:rgba(0,0,0,.06)} .container{max-width:1120px;margin:0 auto;padding:24px} .stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:24px} .stat-card{background:var(--card);border-radius:var(--radius-sm);padding:20px;text-align:center;border:1px solid var(--border)} @@ -53,6 +83,32 @@ body{font-family:-apple-system,BlinkMacSystemFont,"SF Pro Display","PingFang SC" 分析
@@ -166,6 +222,28 @@ function renderMonth(){ new Chart($('chartMonth'),{type:'bar',data:{labels:months.map(m=>m.replace('-','年')+'月'),datasets:[{label:'应还',data:months.map(m=>map[m].total),backgroundColor:'rgba(0,113,227,.3)',borderColor:'#0071e3',borderWidth:1},{label:'已还',data:months.map(m=>map[m].paid),backgroundColor:'rgba(52,199,89,.5)',borderColor:'#34c759',borderWidth:1}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{padding:12,font:{size:12}}}},scales:{y:{beginAtZero:true,ticks:{callback:v=>'¥'+v.toLocaleString()}},x:{ticks:{font:{size:10}}}}}}); } + +function escapeHtml(s){if(!s)return'';const d=document.createElement('div');d.textContent=s;return d.innerHTML;} +function notifTypeIcon(type){const map={repayment_due:'📅',repayment_overdue:'⚠️',system:'📢',account:'👤'};return map[type]||'📩';} +function timeAgo(dt){const diff=(Date.now()-new Date(dt).getTime())/1000;if(diff<60)return '刚刚';if(diff<3600)return Math.floor(diff/60)+'分钟前';if(diff<86400)return Math.floor(diff/3600)+'小时前';return Math.floor(diff/86400)+'天前';} + +function toggleNotifDropdown(e){e.stopPropagation();const dd=$('notifDropdown');dd.classList.toggle('show');if(dd.classList.contains('show'))loadNotifications();} +document.addEventListener('click',function(){$('notifDropdown')?.classList.remove('show');$('exportMenu')?.classList.remove('show');$('importMenu')?.classList.remove('show');}); + +async function loadNotifications(){const res=await apiFetch('/notifications?size=20');if(!res||!res.ok)return;const data=await res.json();renderNotifBadge(data.unread);renderNotifList(data.items);} +function renderNotifBadge(unread){const badge=$('notifBadge');if(unread>0){badge.style.display='';badge.textContent=unread>99?'99+':unread;}else{badge.style.display='none';}} +function renderNotifList(items){const el=$('notifList');if(!items.length){el.innerHTML='| 借款人 | 金额 | 利率 | 期数 |
|---|---|---|---|
| ${escapeHtml(l.name)} | ¥${l.amount.toLocaleString()} | ${l.rate}% | ${l.paid.length}/${l.periods} |