364364 max-width : 540px ;
365365 }
366366
367+ /* 管理员验证 */
368+ .app-summary {
369+ text-align : center;
370+ padding : 30px 20px ;
371+ background : # f8f9fa ;
372+ border-radius : 15px ;
373+ color : # 888 ;
374+ line-height : 1.8 ;
375+ }
376+
377+ .app-summary .lock-icon {
378+ font-size : 2.5em ;
379+ margin-bottom : 10px ;
380+ }
381+
382+ .pin-modal {
383+ position : fixed;
384+ top : 0 ; left : 0 ; width : 100% ; height : 100% ;
385+ background : rgba (0 , 0 , 0 , 0.5 );
386+ display : flex;
387+ align-items : center;
388+ justify-content : center;
389+ z-index : 2000 ;
390+ }
391+
392+ .pin-modal .hidden { display : none; }
393+
394+ .pin-box {
395+ background : white;
396+ border-radius : 16px ;
397+ padding : 30px ;
398+ width : 90% ;
399+ max-width : 360px ;
400+ text-align : center;
401+ }
402+
403+ .pin-box h3 { margin : 0 0 8px ; font-size : 1.2em ; }
404+
405+ .pin-box .pin-subtitle { color : # 999 ; font-size : 0.85em ; margin-bottom : 20px ; }
406+
407+ .pin-input-row { display : flex; gap : 8px ; justify-content : center; margin-bottom : 15px ; }
408+
409+ .pin-digit {
410+ width : 44px ; height : 52px ;
411+ border : 2px solid # ddd ;
412+ border-radius : 10px ;
413+ text-align : center;
414+ font-size : 1.3em ;
415+ font-weight : 700 ;
416+ outline : none;
417+ }
418+
419+ .pin-digit : focus { border-color : # 667eea ; box-shadow : 0 0 0 2px rgba (102 , 126 , 234 , 0.2 ); }
420+
421+ .pin-error { color : # d63031 ; font-size : 0.82em ; margin-bottom : 10px ; min-height : 20px ; }
422+
423+ .admin-badge {
424+ display : inline-block;
425+ padding : 3px 10px ;
426+ border-radius : 12px ;
427+ font-size : 0.75em ;
428+ font-weight : 600 ;
429+ background : # d4edda ;
430+ color : # 155724 ;
431+ }
432+
367433 margin-bottom : 30px ;
368434 flex-wrap : wrap;
369435 }
@@ -1164,14 +1230,26 @@ <h3 style="margin: 0 0 16px; color: #333; font-size: 1.15em;">🐱 可领养猫
11641230 < p > 目前没有可领养的猫咪,请先添加</ p >
11651231 </ div >
11661232
1233+ <!-- 领养申请记录(管理员验证后可见详情) -->
11671234 < div style ="margin: 30px 0 16px; display:flex; align-items:center; justify-content:space-between; ">
11681235 < h3 style ="color: #333; font-size: 1.15em; "> 📋 领养申请记录</ h3 >
1169- < button class ="btn btn-primary " onclick ="openAdoptModal() " style ="padding: 10px 22px; font-size: 0.9em; "> ➕ 新增申请</ button >
1236+ < div style ="display:flex; gap:8px; align-items:center; ">
1237+ < span id ="admin-status " style ="font-size:0.8em; color:#999; "> </ span >
1238+ < button id ="admin-toggle-btn " class ="btn btn-secondary " onclick ="toggleAdminMode() " style ="padding: 8px 16px; font-size: 0.85em; "> 🔒 管理员验证</ button >
1239+ </ div >
11701240 </ div >
1171- < div id ="adopt-app-list " class ="app-list "> </ div >
1172- < div id ="adopt-app-empty " class ="app-empty " style ="display:none; ">
1173- < div class ="icon "> 📋</ div >
1174- < p > 暂无领养申请记录</ p >
1241+ <!-- 未验证:只显示汇总 -->
1242+ < div id ="adopt-app-public " class ="app-summary "> </ div >
1243+ <!-- 已验证:显示完整详情 -->
1244+ < div id ="adopt-app-admin " style ="display:none; ">
1245+ < div style ="display:flex; justify-content:flex-end; margin-bottom:10px; ">
1246+ < button class ="btn btn-primary " onclick ="openAdoptModal() " style ="padding: 8px 18px; font-size: 0.85em; "> ➕ 新增申请</ button >
1247+ </ div >
1248+ < div id ="adopt-app-list " class ="app-list "> </ div >
1249+ < div id ="adopt-app-empty " class ="app-empty " style ="display:none; ">
1250+ < div class ="icon "> 📋</ div >
1251+ < p > 暂无领养申请记录</ p >
1252+ </ div >
11751253 </ div >
11761254 </ section >
11771255 </ div >
@@ -1250,6 +1328,22 @@ <h3 style="color: #333; font-size: 1.15em;">📋 领养申请记录</h3>
12501328 </ div >
12511329 </ div >
12521330
1331+ <!-- 管理员 PIN 验证弹窗 -->
1332+ < div id ="pin-modal " class ="pin-modal hidden ">
1333+ < div class ="pin-box ">
1334+ < h3 > 🔒 管理员验证</ h3 >
1335+ < p class ="pin-subtitle "> 请输入4位管理密码以查看领养申请详情</ p >
1336+ < div class ="pin-input-row ">
1337+ < input type ="password " class ="pin-digit " maxlength ="1 " id ="p1 " oninput ="pinNext(this, 'p2') " autocomplete ="off ">
1338+ < input type ="password " class ="pin-digit " maxlength ="1 " id ="p2 " oninput ="pinNext(this, 'p3') " autocomplete ="off ">
1339+ < input type ="password " class ="pin-digit " maxlength ="1 " id ="p3 " oninput ="pinNext(this, 'p4') " autocomplete ="off ">
1340+ < input type ="password " class ="pin-digit " maxlength ="1 " id ="p4 " oninput ="verifyPin() " autocomplete ="off ">
1341+ </ div >
1342+ < div class ="pin-error " id ="pin-error "> </ div >
1343+ < button class ="btn btn-secondary " onclick ="closePinModal() " style ="width:100%; padding:10px; "> 取消</ button >
1344+ </ div >
1345+ </ div >
1346+
12531347 <!-- Toast 提示 -->
12541348 < div id ="toast " class ="toast "> </ div >
12551349
@@ -1272,13 +1366,14 @@ <h3 style="color: #333; font-size: 1.15em;">📋 领养申请记录</h3>
12721366
12731367 const CAT_EMOJIS = [ '🐱' , '😺' , '😸' , '🙀' , '😻' ] ;
12741368
1369+ // ⚠️ 安全:位置仅保留大区域,不暴露具体活动地点(防虐猫)
12751370 const DEFAULT_CATS = [
1276- { id : 1 , name : '大黄' , neutered : true , personality : '亲人可摸' , location : '图书馆后门 ' , color : '橘色/黄色' , isDefault : true } ,
1277- { id : 2 , name : '小白' , neutered : false , personality : '胆小怕人' , location : '食堂门口 ' , color : '白色' , isDefault : true } ,
1278- { id : 3 , name : '花花' , neutered : true , personality : '可摸不可抱' , location : '教学楼A座 ' , color : '三花' , isDefault : true } ,
1279- { id : 4 , name : '黑豹' , neutered : null , personality : '不亲人' , location : '操场看台 ' , color : '黑色' , isDefault : true } ,
1371+ { id : 1 , name : '大黄' , neutered : true , personality : '亲人可摸' , location : '校园东区 ' , color : '橘色/黄色' , isDefault : true } ,
1372+ { id : 2 , name : '小白' , neutered : false , personality : '胆小怕人' , location : '校园西区 ' , color : '白色' , isDefault : true } ,
1373+ { id : 3 , name : '花花' , neutered : true , personality : '可摸不可抱' , location : '教学区 ' , color : '三花' , isDefault : true } ,
1374+ { id : 4 , name : '黑豹' , neutered : null , personality : '不亲人' , location : '运动区 ' , color : '黑色' , isDefault : true } ,
12801375 { id : 5 , name : '咪咪' , neutered : true , personality : '非常亲人' , location : '宿舍区' , color : '狸花(条纹)' , isDefault : true } ,
1281- { id : 6 , name : '蓝蓝' , neutered : false , personality : '亲人' , location : '实验楼后 ' , color : '蓝色' , isDefault : true }
1376+ { id : 6 , name : '蓝蓝' , neutered : false , personality : '亲人' , location : '教学区 ' , color : '蓝色' , isDefault : true }
12821377 ] ;
12831378
12841379 const DEFAULT_VOLUNTEERS = [
@@ -1347,6 +1442,123 @@ <h3 style="color: #333; font-size: 1.15em;">📋 领养申请记录</h3>
13471442 localStorage . setItem ( 'campuscat_adopt_apps' , JSON . stringify ( list ) ) ;
13481443 }
13491444
1445+ // ====================================================
1446+ // 领养对接 — 管理员 PIN 验证
1447+ // ⚠️ 安全:领养申请记录仅管理员可见,防止偷猫风险
1448+ // ====================================================
1449+ const ADMIN_PIN = '0426' ;
1450+
1451+ function isAdminVerified ( ) {
1452+ return sessionStorage . getItem ( 'campuscat_admin' ) === '1' ;
1453+ }
1454+
1455+ function setAdminVerified ( val ) {
1456+ if ( val ) {
1457+ sessionStorage . setItem ( 'campuscat_admin' , '1' ) ;
1458+ } else {
1459+ sessionStorage . removeItem ( 'campuscat_admin' ) ;
1460+ }
1461+ }
1462+
1463+ function renderAdoptAppsPublicView ( ) {
1464+ const apps = loadAdoptApps ( ) ;
1465+ const div = document . getElementById ( 'adopt-app-public' ) ;
1466+ if ( ! div ) return ;
1467+
1468+ const pending = apps . filter ( a => a . status === 'pending' ) . length ;
1469+ const approved = apps . filter ( a => a . status === 'approved' ) . length ;
1470+ const adopted = apps . filter ( a => a . status === 'adopted' ) . length ;
1471+
1472+ div . innerHTML = `<div class="lock-icon">🔒</div>
1473+ <p>共有 <b>${ apps . length } </b> 条领养申请记录</p>
1474+ <p style="font-size:0.85em;">待审核 <b>${ pending } </b> · 已通过 <b>${ approved } </b> · 已领养 <b>${ adopted } </b></p>
1475+ <p style="font-size:0.82em; color:#bbb; margin-top:8px;">申请人信息仅管理员可见</p>` ;
1476+ }
1477+
1478+ function updateAdminUI ( ) {
1479+ const verified = isAdminVerified ( ) ;
1480+ document . getElementById ( 'adopt-app-public' ) . style . display = verified ? 'none' : 'block' ;
1481+ document . getElementById ( 'adopt-app-admin' ) . style . display = verified ? 'block' : 'none' ;
1482+ const statusEl = document . getElementById ( 'admin-status' ) ;
1483+ const toggleBtn = document . getElementById ( 'admin-toggle-btn' ) ;
1484+ if ( verified ) {
1485+ statusEl . innerHTML = '<span class="admin-badge">✅ 已验证</span>' ;
1486+ toggleBtn . textContent = '🔓 退出管理' ;
1487+ toggleBtn . className = 'btn btn-secondary' ;
1488+ } else {
1489+ statusEl . innerHTML = '' ;
1490+ toggleBtn . textContent = '🔒 管理员验证' ;
1491+ toggleBtn . className = 'btn btn-secondary' ;
1492+ }
1493+ }
1494+
1495+ function toggleAdminMode ( ) {
1496+ if ( isAdminVerified ( ) ) {
1497+ setAdminVerified ( false ) ;
1498+ updateAdminUI ( ) ;
1499+ renderAdoptAppsPublicView ( ) ;
1500+ showToast ( '已退出管理员模式' ) ;
1501+ } else {
1502+ openPinModal ( ) ;
1503+ }
1504+ }
1505+
1506+ function openPinModal ( ) {
1507+ document . getElementById ( 'pin-modal' ) . classList . remove ( 'hidden' ) ;
1508+ document . getElementById ( 'pin-error' ) . textContent = '' ;
1509+ document . getElementById ( 'p1' ) . value = '' ;
1510+ document . getElementById ( 'p2' ) . value = '' ;
1511+ document . getElementById ( 'p3' ) . value = '' ;
1512+ document . getElementById ( 'p4' ) . value = '' ;
1513+ setTimeout ( ( ) => document . getElementById ( 'p1' ) . focus ( ) , 100 ) ;
1514+ }
1515+
1516+ function closePinModal ( ) {
1517+ document . getElementById ( 'pin-modal' ) . classList . add ( 'hidden' ) ;
1518+ }
1519+
1520+ function pinNext ( current , nextId ) {
1521+ if ( current . value . length === 1 ) {
1522+ const next = document . getElementById ( nextId ) ;
1523+ if ( next ) next . focus ( ) ;
1524+ }
1525+ }
1526+
1527+ function verifyPin ( ) {
1528+ const p1 = document . getElementById ( 'p1' ) . value ;
1529+ const p2 = document . getElementById ( 'p2' ) . value ;
1530+ const p3 = document . getElementById ( 'p3' ) . value ;
1531+ const p4 = document . getElementById ( 'p4' ) . value ;
1532+ const pin = p1 + p2 + p3 + p4 ;
1533+
1534+ if ( pin . length < 4 ) return ;
1535+
1536+ if ( pin === ADMIN_PIN ) {
1537+ setAdminVerified ( true ) ;
1538+ closePinModal ( ) ;
1539+ updateAdminUI ( ) ;
1540+ renderAdoptApps ( ) ;
1541+ showToast ( '✅ 验证成功,管理员模式已开启' ) ;
1542+ } else {
1543+ document . getElementById ( 'pin-error' ) . textContent = '❌ 密码错误,请重试' ;
1544+ document . getElementById ( 'p1' ) . value = '' ;
1545+ document . getElementById ( 'p2' ) . value = '' ;
1546+ document . getElementById ( 'p3' ) . value = '' ;
1547+ document . getElementById ( 'p4' ) . value = '' ;
1548+ setTimeout ( ( ) => document . getElementById ( 'p1' ) . focus ( ) , 100 ) ;
1549+ }
1550+ }
1551+
1552+ // 点击遮罩关闭 PIN 弹窗
1553+ document . addEventListener ( 'DOMContentLoaded' , function ( ) {
1554+ const pinModal = document . getElementById ( 'pin-modal' ) ;
1555+ if ( pinModal ) {
1556+ pinModal . addEventListener ( 'click' , function ( e ) {
1557+ if ( e . target === pinModal ) closePinModal ( ) ;
1558+ } ) ;
1559+ }
1560+ } ) ;
1561+
13501562 // ====================================================
13511563 // 领养对接渲染
13521564 // ====================================================
@@ -1451,15 +1663,17 @@ <h3 style="color: #333; font-size: 1.15em;">📋 领养申请记录</h3>
14511663 }
14521664 }
14531665
1454- // 如果审核通过,发动态
1666+ // 如果审核通过,发动态(不暴露申请人姓名)
14551667 if ( newStatus === 'approved' && oldStatus === 'pending' ) {
1456- addActivity ( `🏠 领养申请通过:「${ app . applicantName } 」申请领养「${ app . catName } 」已通过审核` ) ;
1668+ const maskedName = app . applicantName ? app . applicantName . charAt ( 0 ) + '**' : '申请人' ;
1669+ addActivity ( `🏠 领养申请通过:${ maskedName } 申请领养「${ app . catName } 」已通过审核` ) ;
14571670 } else if ( newStatus === 'adopted' && oldStatus !== 'adopted' ) {
1458- addActivity ( `🎉 领养成功!「${ app . catName } 」已找到新家(申请人: ${ app . applicantName } ) ` ) ;
1671+ addActivity ( `🎉 领养成功!「${ app . catName } 」已找到新家` ) ;
14591672 }
14601673
14611674 renderAdoptStats ( ) ;
14621675 renderAdoptCats ( ) ;
1676+ renderAdoptAppsPublicView ( ) ;
14631677 renderAdoptApps ( ) ;
14641678 showToast ( `✅ 状态已更新为「${ ADOPT_STATUS_MAP [ newStatus ] . label } 」` ) ;
14651679 }
@@ -1514,10 +1728,11 @@ <h3 style="color: #333; font-size: 1.15em;">📋 领养申请记录</h3>
15141728 apps . push ( app ) ;
15151729 saveAdoptApps ( apps ) ;
15161730
1517- addActivity ( `🏠 ${ name } 提交了领养 「${ catName } 」的申请,等待审核` ) ;
1731+ addActivity ( `🏠 有新同学提交了领养 「${ catName } 」的申请,等待审核` ) ;
15181732
15191733 closeAdoptModal ( ) ;
15201734 renderAdoptStats ( ) ;
1735+ renderAdoptAppsPublicView ( ) ;
15211736 renderAdoptApps ( ) ;
15221737 showToast ( `✅ 领养申请已提交!审核结果将通知 ${ contact } ` , 'success' ) ;
15231738 }
@@ -2005,7 +2220,7 @@ <h3 style="margin-bottom: 20px; color: #333;">识别结果</h3>
20052220 if ( sectionId === 'cats' ) renderCats ( ) ;
20062221 if ( sectionId === 'volunteers' ) renderVolunteers ( ) ;
20072222 if ( sectionId === 'finance' ) { renderFinanceSummary ( ) ; renderFinanceChart ( ) ; renderFinanceRecords ( ) ; }
2008- if ( sectionId === 'adoption' ) { renderAdoptStats ( ) ; renderAdoptCats ( ) ; renderAdoptApps ( ) ; }
2223+ if ( sectionId === 'adoption' ) { renderAdoptStats ( ) ; renderAdoptCats ( ) ; renderAdoptAppsPublicView ( ) ; renderAdoptApps ( ) ; updateAdminUI ( ) ; }
20092224 }
20102225
20112226 // ====================================================
@@ -2250,7 +2465,9 @@ <h3 style="margin-bottom: 20px; color: #333;">识别结果</h3>
22502465 renderVolunteers ( ) ;
22512466 renderAdoptStats ( ) ;
22522467 renderAdoptCats ( ) ;
2468+ renderAdoptAppsPublicView ( ) ;
22532469 renderAdoptApps ( ) ;
2470+ updateAdminUI ( ) ;
22542471 renderFinanceSummary ( ) ;
22552472 renderFinanceChart ( ) ;
22562473 renderFinanceRecords ( ) ;
0 commit comments