Files
FootPrint/sync-server/www/assets/AssessmentPage-cLSGAKx0.js
T
root 17ab5f0390 security(review): fix invite bypass via smuggled seed flag, login rate limit, server-side logout, attachment preview XSS hardening
- /auth/register: strip client-supplied seed flag (PoC confirmed it bypassed
  invite code, MAX_USERS quota AND first-user auto-admin, granting admin
  without any credential); demo-rebuild internal path unaffected
- /auth/login: per-IP+username of 5 consecutive failures gets a 10-minute
  cooldown against in-memory counter (brute-force guard)
- /auth/logout: new endpoint revokes the current Bearer token server-side;
  frontend calls it (best-effort) before local clearToken on sidebar logout
- attachment docx/xlsx preview: sanitize generated HTML (strip scripts,
  event handlers, dangerous protocols) before dangerouslySetInnerHTML
  render, preventing token theft via malicious previewed documents

PoC suite: seed smuggling rejected, 429 on 6th login attempt, token dead
after logout, per-user isolation intact (403/401/404 as before);
integration 61/61 green
2026-10-17 21:18:55 +08:00

28 lines
35 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import{c as ve,d as Ce,f as pe,r as S,ai as ke,aj as Se,j as e,S as _e,B as A,ak as xe,ah as ze,m as me,H as fe,af as Oe,ag as Ie,v as Me,M as Re}from"./index-DuP8O2p4.js";import{R as he,P as Fe,C as Ke}from"./rotate-ccw-xltE5Sb1.js";/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Ee=[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]],Te=ve("ChevronLeft",Ee);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Le=[["path",{d:"M18 22H4a2 2 0 0 1-2-2V6",key:"pblm9e"}],["path",{d:"m22 13-1.296-1.296a2.41 2.41 0 0 0-3.408 0L11 18",key:"nf6bnh"}],["circle",{cx:"12",cy:"8",r:"2",key:"1822b1"}],["rect",{width:"16",height:"16",x:"6",y:"2",rx:"2",key:"12espp"}]],De=ve("Images",Le),be={politics:{中共党员:3,中共预备党员:2.5,入党积极分子:2,共青团员:1.5,群众:1},conductDefault:8,integrityDefault:2,sportsFactor:.025,gpaFactor:10,table2:{国家级:{一等奖:3,二等奖:2,三等奖:1,优秀奖:.5},省市级:{一等奖:2,二等奖:1,三等奖:.5,优秀奖:.2},区县级:{一等奖:1,二等奖:.5,三等奖:.2,优秀奖:.1},校级:{一等奖:.5,二等奖:.2,三等奖:.1,优秀奖:.05},二级学院:{一等奖:.2,二等奖:.1,三等奖:.05,优秀奖:0}},table4:{校级:{干部:4,干事:2,社团干部:1,学工助理:2},院级:{干部:3,干事:1.5,支部委员:1.5,支部组长:1,班长:1,团支书:1,班委:.5,社团干部:1,学工助理:2}},publicGood:{无偿献血:.5,捐献骨髓:3,国家级:1.5,省市级:1,区县级:.5,校级:.2,二级学院:.1},rewards:{荣誉称号:{院级:.3,校级:.5,区级:1,市级:1.5,国家级:2},英语等级:{三级:.5,四级:1,六级:2},计算机等级:{一级:.5,二级:1,三级:2},职业资格:{从业资格:.5,初级:1,中级:2,高级:3}},penalties:{留校察看:5,记过:4,严重警告:3,警告:2,通报批评:1}};function ie(i){const l=/^(\d{4})-(\d{4})/.exec(String(i??"").trim());return l?l[1]+"-"+l[2]:null}function we(i){const l=/^(\d{4})-(\d{4})$/.exec(i);return l?[l[1]+"-09-01",l[2]+"-09-01"]:null}function ue(i,l){const m=ie(i)??i,b=/^(\d{4})/.exec(m),y=/^(\d{4})/.exec(l??"");if(!b||!y)return m+" 学年";const a=Number(b[1])-Number(y[1])+1,h=["大一","大二","大三","大四","大五","大六","大七"];return a<1?m+" 学年":(h[a-1]??a+"年级")+"("+m+" 学年)"}function We(i,l){if(!l)return!0;const m=(i??"").slice(0,10);return m>=l[0]&&m<l[1]}function ne(i){const l=(i??"").trim();return l?l.includes("国家")?"国家级":l.includes("省")||l.includes("市")?"省市级":l.includes("区")?"区县级":l.includes("院")||l.includes("二级")?"二级学院":l.includes("校")?"校级":null:null}function ge(i){const l=(i??"").trim();return l?/特等|一等|金(色)?奖|冠军|第1名|第一名/.test(l)?"一等奖":/二等|银(色)?奖|亚军|第2名|第3名|第二名|第三名/.test(l)?"二等奖":/三等|铜(色)?奖|第[4-6]名|第四名|第五名|第六名/.test(l)?"三等奖":/优秀|优胜|第[7-9]名|第七名/.test(l)?"优秀奖":null:null}const K=i=>Math.round(i*100)/100;function ye(i,l,m){var y;return!l||!m?null:((y=i.table2[l])==null?void 0:y[m])??null}function Ge(i){var x,I,G,ee;const{year:l,records:m,semesters:b,bands:y,weights:a,profile:h,rules:o,ov:w}=i,C=l==="all"?null:we(l),f=m.filter(t=>t.status!=="draft"&&We(t.start_date??t.created_at,C)),j=t=>{var n;return(n=w.itemOverrides)==null?void 0:n[t]},v=(t,n,d,p,N,U)=>{const F=j(t),Ne=(F==null?void 0:F.score)??p??0;return{itemKey:t,title:n,source:d,score:Ne,overridden:(F==null?void 0:F.score)!=null,note:N,fromRecord:U}},R=(t,n,d,p)=>({key:t,label:n,cap:d,items:p,subtotal:K(p.reduce((N,U)=>N+U.score,0))}),_=[],te=h.political??"",J=Object.keys(o.politics).find(t=>te.includes(t)),u=J?o.politics[J]:1,E=w.conduct??o.conductDefault,z=w.integrity??o.integrityDefault;_.push(R("conduct","品行素质",13,[{itemKey:"politics",title:"思想政治基础分("+(te||"群众")+")",source:"个人信息 · 政治面貌",score:u},{itemKey:"conduct",title:"操行考评成绩",source:"辅导员考评(可修改)",score:E,overridden:w.conduct!=null},{itemKey:"integrity",title:"诚信分",source:"默认满分(可修改)",score:z,overridden:w.integrity!=null}]));const O=t=>{if(t.grade_point!=null)return t.grade_point;if(y&&t.score!=null){const n=y.find(d=>t.score>=d.min&&(t.score<d.max||d.max>=100&&t.score<=d.max));if(n&&n.gp!=null){if(n.gpTop!=null){const d=n.max>=100?n.max:n.max-1,p=(n.gpTop-n.gp)/Math.max(1,d-n.min);return K(n.gp+(t.score-n.min)*p)}return n.gp}}return null},Q=t=>a?a[t.category??"其他"]??a.其他??1:1,P=ie(l),H=b.filter(t=>!P||ie(t.term)===P),q=[],D=[];for(const t of H){if(q.push(...t.courses??[]),t.gpa!=null){D.push({term:t.term,gpa:t.gpa,fallback:!1});continue}const n=(t.courses??[]).map(p=>({...p,grade_point:O(p)})).filter(p=>p.grade_point!=null&&p.credit>0);if(!n.length)continue;const d=n.reduce((p,N)=>p+N.credit,0);D.push({term:t.term,gpa:K(n.reduce((p,N)=>p+N.grade_point*Q(N)*N.credit,0)/d),fallback:!0})}const T=D.length?K(D.reduce((t,n)=>t+n.gpa,0)/D.length):null,ae=T==null?"该学年暂无学期绩点数据(先在「成绩」页录入学期或课程)":D.some(t=>t.fallback)?"部分学期绩点未录入,按课程推算":void 0,ce=T!=null?K(T*o.gpaFactor):0;_.push(R("academic","学业成绩",60,[{itemKey:"gpa",title:"学年平均学分绩点 "+(T!=null?T.toFixed(2):"—")+" × "+o.gpaFactor,source:"成绩模块 · "+H.length+" 学期"+(D.length?"(绩点均值)":""),score:ce,note:ae||(T==null?void 0:q.length+" 门课程")}]));const W=q.filter(t=>/体育/.test(t.course_name)),se=K(W.reduce((t,n)=>t+(n.score??0),0)*o.sportsFactor),re=[];W.length&&re.push({itemKey:"sports",title:"体育成绩("+W.length+" 学期体育课)",source:"体育课成绩之和 × "+o.sportsFactor,score:Math.min(se,5)});const k=[];for(const t of f.filter(n=>n.type_name==="文体活动")){const n=ye(o,ne(t.level),ge(t.award_grade));n==null&&!((x=j("w"+t.id))!=null&&x.score)||k.push(v("w"+t.id,t.title,t.type_name+" · "+(t.level??"")+" "+(t.award_grade??""),n,void 0,t.id))}const de=K(Math.min(k.reduce((t,n)=>t+n.score,0),5));_.push(R("sports","文体素质",10,[...re,{itemKey:"wcap",title:"文体类加分(上限 5 分)",source:k.length+" 条记录",score:de,note:k.map(t=>t.title+" "+t.score+"分").join(";")||"无匹配记录",recordIds:k.map(t=>t.fromRecord).filter(Boolean)}]));const Y=[];for(const t of f.filter(n=>n.type_name==="竞赛"||n.type_name==="科研")){const n=ye(o,ne(t.level),ge(t.award_grade));n==null&&!((I=j("c"+t.id))!=null&&I.score)||Y.push(v("c"+t.id,t.title,t.type_name+" · "+(t.level??"")+" "+(t.award_grade??""),n,void 0,t.id))}const $=K(Math.min(Y.reduce((t,n)=>t+n.score,0),10)),Z=[];for(const t of f.filter(n=>n.type_name==="学生工作")){const n=ne(t.level)??"院级",d=(t.role??"")+" "+t.title,p=o.table4[n==="国家级"||n==="省市级"||n==="区县级"?"校级":n]??o.table4.院级;let N=null;for(const[U,F]of Object.entries(p))if(d.includes(U)){N=F;break}N==null&&!((G=j("k"+t.id))!=null&&G.score)||Z.push(v("k"+t.id,t.title,"学生工作 · "+(t.level??"院级")+" "+(t.role??""),N,N==null?"请手动填写分值":void 0,t.id))}const X=K(Math.min(Z.reduce((t,n)=>t+n.score,0),4)),V=[];for(const t of f.filter(n=>n.type_name==="志愿公益")){const n=ne(t.level),d=/献血/.test(t.title);let p=d?o.publicGood.无偿献血:n?o.publicGood[n]??null:null;p==null&&!((ee=j("g"+t.id))!=null&&ee.score)||V.push(v("g"+t.id,t.title,"公益 · "+(d?"无偿献血":t.level??"校级"),p,void 0,t.id))}const oe=K(Math.min(V.reduce((t,n)=>t+n.score,0),3));_.push(R("expand","拓展性素质",17,[{itemKey:"ccap",title:"竞赛获奖(上限 10 分)",source:Y.length+" 条记录",score:$,note:Y.map(t=>t.title+" "+t.score+"分").join(";")||"无匹配记录",recordIds:Y.map(t=>t.fromRecord).filter(Boolean)},{itemKey:"kcap",title:"担任学生工作(上限 4 分)",source:Z.length+" 条记录",score:X,note:Z.map(t=>t.title+" "+t.score+"分").join(";")||"无匹配记录",recordIds:Z.map(t=>t.fromRecord).filter(Boolean)},{itemKey:"gcap",title:"公益活动(上限 3 分)",source:V.length+" 条记录",score:oe,note:V.map(t=>t.title+" "+t.score+"分").join(";")||"无匹配记录",recordIds:V.map(t=>t.fromRecord).filter(Boolean)}]));const s=[];for(const t of f.filter(n=>n.type_name==="荣誉惩处"||n.type_name==="证书技能")){const n=j("h"+t.id),d=t.title+" "+(t.award_grade??"");if(/综合测评奖学金/.test(d))continue;let p=null,N=t.type_name;if(/留校察看/.test(d))p=-o.penalties.留校察看;else if(/记过/.test(d))p=-o.penalties.记过;else if(/严重警告/.test(d))p=-o.penalties.严重警告;else if(/警告/.test(d))p=-o.penalties.警告;else if(/通报批评/.test(d))p=-o.penalties.通报批评;else if(/六级|专八/.test(d))p=o.rewards.英语等级.六级;else if(/四级|专四/.test(d))p=o.rewards.英语等级.四级;else if(/三级/.test(d)&&/英语/.test(d))p=o.rewards.英语等级.三级;else if(/计算机/.test(d)&&/三级/.test(d))p=o.rewards.计算机等级.三级;else if(/计算机/.test(d)&&/二级/.test(d))p=o.rewards.计算机等级.二级;else if(/计算机/.test(d)&&/一级/.test(d))p=o.rewards.计算机等级.一级;else if(/高级/.test(d))p=o.rewards.职业资格.高级;else if(/中级/.test(d))p=o.rewards.职业资格.中级;else if(/初级/.test(d))p=o.rewards.职业资格.初级;else if(/从业|资格证|教师资格|普通话|驾驶证|软考/.test(d))p=o.rewards.职业资格.从业资格;else{const U=ne(t.level),F=U==="省市级"?"市级":U==="区县级"?"区级":U==="二级学院"?"院级":U;F&&o.rewards.荣誉称号[F]!=null&&(p=o.rewards.荣誉称号[F],N="荣誉 · "+F)}p==null&&!(n!=null&&n.score)||s.push(v("h"+t.id,t.title,N,p,void 0,t.id))}const r=K(Math.max(Math.min(s.reduce((t,n)=>t+n.score,0),5),-5));_.push(R("rewards","奖惩分",5,[{itemKey:"hcap",title:"奖惩合计(±5 分)",source:s.length+" 条记录",score:r,note:s.map(t=>t.title+" "+t.score+"分").join(";")||"无匹配记录",recordIds:s.map(t=>t.fromRecord).filter(Boolean)}]));for(const t of w.manual??[]){const n=_.find(d=>d.label===t.section)??_[_.length-1];n.items.push({itemKey:"m"+t.title+t.score,title:t.title,source:"手动添加",score:t.score}),n.subtotal=K(n.items.reduce((d,p)=>d+p.score,0))}const c=K(_.reduce((t,n)=>{const d=n.key==="rewards"?n.subtotal:Math.min(n.subtotal,n.cap);return t+d},0));return{sections:_,total:c,gpa:T}}function M(i){return String(i??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Ue(i,l){var y;const m="----=_fp_zongce",b=[];b.push(`MIME-Version: 1.0\r
Content-Type: multipart/related; type="text/html"; boundary="`+m+`"\r
\r
--`+m+`\r
Content-Location: document.html\r
Content-Type: text/html; charset="utf-8"\r
Content-Transfer-Encoding: 8bit\r
\r
`+i+`\r
`);for(const a of l){const h=a.dataUri.indexOf(","),o=a.dataUri.slice(0,h),w=a.dataUri.slice(h+1),C=((y=/data:([^;]+)/.exec(o))==null?void 0:y[1])??"image/png";b.push("--"+m+`\r
Content-Location: `+a.name+`\r
Content-Type: `+C+`\r
Content-Transfer-Encoding: base64\r
\r
`+w+`\r
`)}return b.push("--"+m+`--\r
`),new Blob([b.join("")],{type:'multipart/related; type="text/html"; boundary="'+m+'"'})}const g="border:0.75pt solid #3a3a3a;padding:5pt 8pt;font-size:10.5pt;vertical-align:middle",L='border:0.75pt solid #3a3a3a;padding:5pt 8pt;font-size:10.5pt;font-family:"黑体",SimHei;letter-spacing:1pt;vertical-align:middle;background:#eef1f6',Be='border:0.75pt solid #3a3a3a;padding:5pt 8pt;font-size:11pt;font-family:"黑体",SimHei;letter-spacing:1.5pt;background:#f5f6f9;text-align:left',je=(i,l)=>'<!--[if supportFields]><span style="mso-element:field-begin"></span>'+i+'<span style="mso-element:field-separator"></span><![endif]-->'+l+'<!--[if supportFields]><span style="mso-element:field-end"></span><![endif]-->';function He(i){const l='<table style="border-collapse:collapse;width:100%;margin:4pt 0" border="1"><tr><td style="'+L+';width:30%">基础项</td><td style="'+L+'">分值 / 规则</td></tr><tr><td style="'+g+'">政治面貌分</td><td style="'+g+'">'+M(Object.entries(i.politics).map(([f,j])=>f+" "+j+" 分").join(";"))+'</td></tr><tr><td style="'+g+'">操行考评 / 诚信分默认</td><td style="'+g+'">'+M(i.conductDefault)+" 分 / "+M(i.integrityDefault)+' 分</td></tr><tr><td style="'+g+'">学业成绩</td><td style="'+g+'">学年平均学分绩点 × '+M(i.gpaFactor)+'(上限 60 分)</td></tr><tr><td style="'+g+'">体育加分</td><td style="'+g+'">体育课成绩之和 × '+M(i.sportsFactor)+'(上限 5 分)</td></tr><tr><td style="'+g+'">公益服务</td><td style="'+g+'">'+M(Object.entries(i.publicGood).map(([f,j])=>f+" "+j).join(";"))+"</td></tr></table>",m=i.table2,b=i.table4,y=i.rewards,a=Object.keys(m),h=["一等奖","二等奖","三等奖","优秀奖"],o='<table style="border-collapse:collapse;width:100%;margin:4pt 0" border="1"><tr><td style="'+L+';width:20%">获奖级别</td>'+h.map(f=>'<td style="'+L+'">'+f+"</td>").join("")+"</tr>"+a.map(f=>'<tr><td style="'+g+'">'+M(f)+"</td>"+h.map(j=>{var v;return'<td style="'+g+';text-align:right" class="num">'+M(((v=m[f])==null?void 0:v[j])??"—")+"</td>"}).join("")+"</tr>").join("")+"</table>",w='<table style="border-collapse:collapse;width:100%;margin:4pt 0" border="1"><tr><td style="'+L+';width:20%">学生工作级别</td><td style="'+L+'">岗位分值</td></tr>'+Object.keys(b).map(f=>'<tr><td style="'+g+'">'+M(f)+'</td><td style="'+g+'">'+M(Object.entries(b[f]).map(([j,v])=>j+" "+v).join(";"))+"</td></tr>").join("")+"</table>",C='<table style="border-collapse:collapse;width:100%;margin:4pt 0" border="1"><tr><td style="'+L+';width:20%">类别</td><td style="'+L+'">分值</td></tr>'+["荣誉称号","英语等级","计算机等级","职业资格"].map(f=>'<tr><td style="'+g+'">'+M(f)+'</td><td style="'+g+'">'+M(Object.entries(y[f]??{}).map(([j,v])=>j+" "+v).join(";"))+"</td></tr>").join("")+'<tr><td style="'+g+'">违纪扣分</td><td style="'+g+'">'+M(Object.entries(i.penalties).map(([f,j])=>f+" -"+j).join(";"))+"</td></tr></table>";return'<div style="font-family:SimHei;font-size:12pt;margin:14pt 0 4pt">附 2:评分细则参考(默认值,可自行调整)</div>'+l+o+w+C}function qe(i,l,m,b,y){const a=u=>M(u),h=m.school?'<div style="text-align:center;font-size:10.5pt;letter-spacing:2pt;color:#222">'+a(m.school)+"</div>":"",o=(u,E)=>'<td style="'+L+';width:16%;text-align:center">'+E+'</td><td style="'+g+';text-align:center">'+a(u||"")+"</td>",w=["一","二","三","四","五","六","七","八"],C=[];l.sections.forEach((u,E)=>{const z=u.key==="rewards"?u.subtotal:Math.min(u.subtotal,u.cap);C.push('<tr><td colspan="3" style="'+Be+'">'+a(w[E]??E+1)+"、"+a(u.label)+"(上限 "+a(u.cap)+" 分)</td></tr>");for(const O of u.items)C.push('<tr><td style="'+g+'">'+a(O.title)+(O.overridden?'<span style="color:#8a6d3b">(已手动修正)</span>':"")+'</td><td style="'+g+';color:#444;font-size:9.5pt;border-left-width:0.75pt">'+a(O.note||O.source)+'</td><td style="'+g+';text-align:right;width:64pt" class="num">'+a(O.score)+"</td></tr>");C.push('<tr><td colspan="2" style="'+g+';text-align:right;background:#fafbfc">'+a(u.label)+'小计</td><td style="'+g+';text-align:right;background:#fafbfc" class="num"><b>'+a(z)+"</b></td></tr>")}),C.push('<tr><td colspan="2" style="'+g+';text-align:center;font-family:"黑体",SimHei;font-size:12pt;letter-spacing:2pt;background:#eef1f6">综合素质测评总分</td><td style="'+g+';text-align:right;background:#eef1f6" class="num"><b style="font-size:12pt">'+a(l.total)+"</b></td></tr>");const f='<table style="border-collapse:collapse;width:100%;margin-top:10pt" border="1"><tr><td style="'+L+';width:34%;text-align:center">考评项目</td><td style="'+L+';text-align:center">评分依据</td><td style="'+L+';width:64pt;text-align:center">分值</td></tr>'+C.join("")+"</table>",j=[];let v=0;const R=b.map(u=>{const E=u.images.map(z=>{v++;const O="ev"+u.recordId+"_"+j.length;return j.push({name:O,dataUri:z}),'<div style="text-align:center;margin:3pt 0"><img src="'+O+'" style="width:200pt;border:0.75pt solid #5a5a5a" alt="附图"><br><span style="font-size:9pt;color:#333;font-family:"宋体",SimSun">图 '+v+" "+a(u.title)+"</span></div>"}).join("");return'<tr><td style="'+g+';width:26%;vertical-align:top;background:#fafafa"><b style="font-size:10pt">'+a(u.title)+'</b><br><span style="font-size:9pt;color:#555">'+a(u.meta)+'</span></td><td style="'+g+'">'+E+"</td></tr>"}).join(""),_=R?'<div style="font-family:"黑体",SimHei;font-size:12pt;margin:16pt 0 4pt;letter-spacing:1pt">附 1:加分证明材料</div><div style="font-size:9.5pt;color:#555;margin:0 0 4pt">('+a(b.length)+" 项记录 · "+a(v)+' 张材料图)</div><table style="border-collapse:collapse;width:100%" border="1">'+R+"</table>":"",te='<table style="border-collapse:collapse;width:100%;margin-top:12pt" border="1"><tr>'+o(m.studentId,"学 号")+o(m.name,"姓 名")+o(m.gender,"性 别")+"</tr><tr>"+o(m.college,"学 院")+o(m.major,"专 业")+o(m.className,"班 级")+"</tr><tr>"+o(m.political,"政治面貌")+o(i,"统计周期")+o(m.phone,"电话号码")+"</tr></table>",J='<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word"><head><meta charset="utf-8"><title>学生综合素质测评表</title><!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:Zoom>100</w:Zoom><w:TrackChanges>0</w:TrackChanges></w:WordDocument></xml><![endif]--><style>@page WordSection1 { size:595.3pt 841.9pt; margin:72pt 78pt 66pt; mso-footer: f1; }div.WordSection1 { page:WordSection1; }body { font-family:"宋体",SimSun,serif; font-size:10.5pt; color:#1a1a1a; line-height:1.5; }h1.title { font-family:"黑体",SimHei; font-size:18pt; text-align:center; letter-spacing:4pt; margin:2pt 0 6pt; font-weight:bold; }p.sub { text-align:center; font-size:10pt; color:#333; margin:0 0 2pt; }table { border-collapse:collapse; }.num { font-family:"Times New Roman",SimSun; }</style></head><body><div class="WordSection1">'+h+'<h1 class="title">学生综合素质测评表</h1><p class="sub">测评学年:'+a(i)+"</p>"+te+f+_+He(y)+'<table style="width:100%;border-collapse:collapse;margin-top:26pt;font-size:10.5pt"><tr><td style="width:25%">学生签名:_____</td><td style="width:25%">班长签名:_____</td><td style="width:25%">辅导员签名:_____</td><td>日期:______</td></tr></table><p style="font-size:9pt;color:#555;margin-top:12pt">说明:本表依据《学生综合素质测评评分细则》自动生成(细则默认值见附 2,可自行调整);带「已手动修正」标记的分值为手动核定结果。导出前请核对加分记录与证明材料一致。</p><div style="mso-element:footer" id="f1"><p style="text-align:center;font-size:9pt;font-family:"宋体",SimSun;margin:0">第 '+je("PAGE","1")+" 页(共 "+je("NUMPAGES","1")+" 页)</p></div></div></body></html>";return Ue(J,j)}const B=({value:i,onChange:l,wide:m})=>e.jsx("input",{type:"number",step:"0.05",className:"num rounded-md border border-neutral-200 bg-white px-1 py-0.5 text-right text-[11.5px] focus:border-brand focus:outline-none "+(m?"w-[76px]":"w-[58px]"),value:i,onChange:b=>l(b.target.value===""?0:Number(b.target.value))}),le=({head:i,children:l})=>e.jsxs("table",{className:"w-full border border-neutral-200 text-[11.5px]",children:[e.jsx("thead",{children:e.jsx("tr",{children:i.map((m,b)=>e.jsx("th",{className:"border-b border-neutral-200 bg-neutral-50 px-2 py-1 text-left font-medium text-neutral-500",children:m},b))})}),e.jsx("tbody",{children:l})]});function Ze(){var V,oe;const i=Ce(),l=pe({queryKey:["profile"],queryFn:Ie}),m=pe({queryKey:["zc-records"],queryFn:()=>Me({page:1,page_size:500})}),b=pe({queryKey:["grades"],queryFn:Re}),y=(V=l.data)==null?void 0:V.content,a={...(y==null?void 0:y.info)??{}},[h,o]=S.useState(""),[w,C]=S.useState({}),[f,j]=S.useState([]),[v,R]=S.useState(!1),[_,te]=S.useState(!1),J=S.useRef(null),[u,E]=S.useState(be),[z,O]=S.useState(null);S.useEffect(()=>{var s,r;y&&(C(((s=y.zongce)==null?void 0:s.overrides)??{}),(r=y.zongce)!=null&&r.rules&&E(y.zongce.rules))},[y]);const Q=((oe=m.data)==null?void 0:oe.items)??[],P=S.useMemo(()=>{var s;return Array.from(new Set((((s=b.data)==null?void 0:s.semesters)??[]).map(r=>ie(r.term)).filter(Boolean))).sort()},[b.data]);S.useEffect(()=>{var s;y&&(C(((s=y.zongce)==null?void 0:s.overrides)??{}),!h&&P.length&&o(P[0]))},[y,b.data]);const H=w[h]??{},q=S.useMemo(()=>{var s,r,c;return Ge({year:h||"all",records:Q,semesters:((s=b.data)==null?void 0:s.semesters)??[],bands:(r=b.data)==null?void 0:r.bands,weights:(c=b.data)==null?void 0:c.weights,profile:a,rules:u,ov:H})},[Q,b.data,a,u,H,h]),D=a.eduStart||(P.length?P[0]+"-09":""),T=h==="all"||!h?"全部学年":ue(h,D),ae=S.useMemo(()=>{const s=h&&h!=="all"?we(h):null;return Q.filter(r=>{if(r.status==="draft")return!1;if(!s)return!0;const c=(r.start_date??r.created_at??"").slice(0,10);return c>=s[0]&&c<s[1]}).map(r=>r.id)},[Q,h]),ce=S.useMemo(()=>{const s=new Set;return q.sections.forEach(r=>r.items.forEach(c=>{c.fromRecord&&s.add(c.fromRecord);for(const x of c.recordIds??[])s.add(x)})),s},[q]);S.useEffect(()=>{let s=!0;return(async()=>{const r=[];for(const c of ae){const x=Q.find(I=>I.id===c);try{const I=await ke(c),G=[];for(const ee of I.items.filter(t=>t.id&&(t.mime??"").startsWith("image/"))){const t=await Se(ee.id);t.data_b64&&G.push("data:"+(ee.mime==="image/jpeg"?"image/jpeg":"image/webp")+";base64,"+t.data_b64)}if(G.length){const ee=[x==null?void 0:x.type_name,x==null?void 0:x.level,x==null?void 0:x.award_grade].filter(Boolean).join(" · ");r.push({recordId:c,title:(x==null?void 0:x.title)??"记录 "+c,meta:ee,images:G})}}catch{}}s&&j(r)})(),()=>{s=!1}},[ae.join(",")]);const W=S.useMemo(()=>{const s=[];for(const r of f)for(const c of r.images)s.push({src:c,title:r.title});return s},[f]),se=s=>void Oe({info:a,zongce:{rules:s.rules??u,overrides:s.overrides??w}}).catch(r=>i("error",r.message)),re=s=>{C(r=>({...r,[h]:s})),se({overrides:{...w,[h]:s}})},k=s=>{const r=JSON.parse(JSON.stringify(u));s(r),E(r),se({rules:r})},de=()=>{const s=JSON.parse(JSON.stringify(be));E(s),se({rules:s}),i("success","评分细则已恢复默认")},Y=()=>{const s=qe(T,q,a,f,u),r=URL.createObjectURL(s),c=document.createElement("a");c.href=r,c.download="综合素质测评_"+T+".doc",c.click(),setTimeout(()=>URL.revokeObjectURL(r),6e4),i("success","综测表已导出(.doc,附证明材料图 "+W.length+" 张 + 细则参考)")},$=z?W.findIndex(s=>s.src===z.src):-1,Z=s=>{const r=$+s;r>=0&&r<W.length&&O({src:W[r].src,title:W[r].title,index:r})},X=W.length;return e.jsxs("div",{className:"mx-auto max-w-[1080px] px-3 py-4 sm:px-6 sm:py-5",children:[e.jsxs("div",{className:"no-print mb-4 flex flex-wrap items-center justify-between gap-2",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-[15px] font-semibold text-ink-1",children:"综合测评"}),e.jsx("p",{className:"mt-0.5 text-[11.5px] text-ink-3",children:"按学年依据评分细则自动计算,可手动修正分值,支持导出 Word 测评表"})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx(_e,{className:"w-[190px]",value:h,onChange:s=>o(s.target.value),options:[...P.map(s=>({value:s,label:ue(s,D)})),{value:"all",label:"全部学年"}]}),e.jsxs(A,{variant:"secondary",onClick:()=>{const s=!_;te(s),s&&setTimeout(()=>{var r;return(r=J.current)==null?void 0:r.scrollIntoView({behavior:"smooth",block:"start"})},80)},children:[e.jsx(xe,{size:13})," 评分细则参考"]}),e.jsxs(A,{variant:"secondary",onClick:Y,children:[e.jsx(ze,{size:13})," 导出 Word"]}),e.jsxs(A,{variant:"secondary",onClick:()=>R(!0),children:[e.jsx(he,{size:13})," 初始化"]}),e.jsxs(A,{variant:"primary",onClick:()=>window.print(),children:[e.jsx(Fe,{size:13})," 打印"]})]})]}),_&&e.jsxs("div",{ref:J,className:"no-print mb-4 scroll-mt-16 rounded-xl border border-line bg-white p-4 shadow-card",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(xe,{size:15,className:"text-neutral-500"}),e.jsx("span",{className:"text-[13.5px] font-bold text-neutral-900",children:"评分细则参考"}),e.jsx("span",{className:"text-[11.5px] text-neutral-400",children:"默认值可自行调整,调整后即时生效并随档案保存"})]}),e.jsxs(A,{variant:"ghost",onClick:de,children:[e.jsx(he,{size:13})," 恢复默认"]})]}),e.jsxs("div",{className:"mt-3 grid gap-4 md:grid-cols-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"mb-1.5 text-[12px] font-semibold text-neutral-600",children:"基础分值"}),e.jsxs(le,{head:["项目","分值"],children:[Object.entries(u.politics).map(([s,r])=>e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:s}),e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:e.jsx(B,{value:r,onChange:c=>k(x=>{x.politics[s]=c})})})]},s)),e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:"操行考评默认分"}),e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:e.jsx(B,{value:u.conductDefault,onChange:s=>k(r=>{r.conductDefault=s})})})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:"诚信分默认"}),e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:e.jsx(B,{value:u.integrityDefault,onChange:s=>k(r=>{r.integrityDefault=s})})})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:"学业成绩(绩点 ×)"}),e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:e.jsx(B,{value:u.gpaFactor,onChange:s=>k(r=>{r.gpaFactor=s})})})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:"体育加分(成绩 ×)"}),e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:e.jsx(B,{value:u.sportsFactor,onChange:s=>k(r=>{r.sportsFactor=s})})})]})]}),e.jsx("div",{className:"mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600",children:"公益服务"}),e.jsx(le,{head:["项目","分值"],children:Object.entries(u.publicGood).map(([s,r])=>e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:s}),e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:e.jsx(B,{value:r,onChange:c=>k(x=>{x.publicGood[s]=c})})})]},s))}),e.jsx("div",{className:"mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600",children:"违纪扣分"}),e.jsx(le,{head:["项目","扣分"],children:Object.entries(u.penalties).map(([s,r])=>e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:s}),e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:e.jsx(B,{value:r,onChange:c=>k(x=>{x.penalties[s]=c})})})]},s))})]}),e.jsxs("div",{children:[e.jsx("div",{className:"mb-1.5 text-[12px] font-semibold text-neutral-600",children:"竞赛 / 文体奖项加分(级别 × 奖项)"}),e.jsxs("table",{className:"w-full border border-neutral-200 text-[11.5px]",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-neutral-200 bg-neutral-50 px-2 py-1 text-left font-medium text-neutral-500",children:"级别"}),["一等奖","二等奖","三等奖","优秀奖"].map(s=>e.jsx("th",{className:"border-b border-neutral-200 bg-neutral-50 px-2 py-1 text-left font-medium text-neutral-500",children:s},s))]})}),e.jsx("tbody",{children:Object.entries(u.table2).map(([s,r])=>e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1 font-medium text-neutral-700",children:s}),["一等奖","二等奖","三等奖","优秀奖"].map(c=>e.jsx("td",{className:"border-b border-neutral-100 px-1 py-1",children:e.jsx(B,{value:r[c]??0,onChange:x=>k(I=>{I.table2[s][c]=x})})},c))]},s))})]}),e.jsx("div",{className:"mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600",children:"学生工作"}),e.jsx(le,{head:["级别 / 岗位","分值"],children:Object.entries(u.table4).flatMap(([s,r])=>Object.entries(r).map(([c,x])=>e.jsxs("tr",{children:[e.jsxs("td",{className:"border-b border-neutral-100 px-2 py-1",children:[s," · ",c]}),e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:e.jsx(B,{value:x,onChange:I=>k(G=>{G.table4[s][c]=I})})})]},s+c)))}),e.jsx("div",{className:"mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600",children:"荣誉与证照加分"}),e.jsx(le,{head:["类别 / 等级","分值"],children:Object.entries(u.rewards).flatMap(([s,r])=>Object.entries(r).map(([c,x])=>e.jsxs("tr",{children:[e.jsxs("td",{className:"border-b border-neutral-100 px-2 py-1",children:[s," · ",c]}),e.jsx("td",{className:"border-b border-neutral-100 px-2 py-1",children:e.jsx(B,{value:x,onChange:I=>k(G=>{G.rewards[s][c]=I})})})]},s+c)))})]})]})]}),e.jsxs("div",{className:"print-sheet mx-auto w-full max-w-[820px] rounded-xl border border-line bg-white shadow-card sm:p-8",children:[a.school&&e.jsx("div",{className:"text-center text-[11px] tracking-widest text-neutral-500",children:a.school}),e.jsx("h1",{className:"mt-0.5 text-center text-[19px] font-bold tracking-wide text-neutral-900",children:"学生综合素质测评表"}),e.jsxs("div",{className:"mt-1 text-center text-[12px] text-neutral-500",children:["测评学年:",T]}),e.jsx("div",{className:"mt-4 grid grid-cols-2 gap-px overflow-hidden rounded-lg border border-neutral-300 bg-neutral-300 text-[12.5px] sm:grid-cols-3",children:[["学 号",a.studentId],["姓 名",a.name],["性 别",a.gender],["学 院",a.college],["专 业",a.major],["班 级",a.className],["政治面貌",a.political],["统计周期",T],["电话号码",a.phone]].map(([s,r])=>e.jsxs("div",{className:"flex bg-white",children:[e.jsx("span",{className:"w-[62px] shrink-0 bg-neutral-50 px-2 py-1.5 font-medium text-neutral-500",children:s}),e.jsx("span",{className:"min-w-0 flex-1 truncate px-2 py-1.5 text-neutral-900",children:r||"—"})]},s))}),e.jsxs("div",{className:"mt-4 flex items-center justify-center rounded-lg bg-gradient-to-r from-neutral-900 to-neutral-700 px-4 py-2.5 text-white",children:[e.jsx("span",{className:"text-[12px] opacity-80",children:"综合素质测评总分"}),e.jsx("span",{className:"num ml-3 text-[26px] font-bold leading-none",children:q.total}),e.jsx("span",{className:"ml-1.5 text-[12px] opacity-80",children:"/ 100 分"})]}),q.sections.map(s=>e.jsxs("div",{className:"mt-6",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"h-4 w-1 rounded-full bg-brand"}),e.jsx("span",{className:"text-[13.5px] font-bold text-neutral-900",children:s.label}),e.jsxs(me,{className:"no-print",color:"var(--text-3)",children:["上限 ",s.cap," 分"]})]}),e.jsxs("span",{className:"num rounded-md bg-neutral-100 px-2 py-0.5 text-[12px] font-semibold text-neutral-700",children:["小计 ",s.key==="rewards"?s.subtotal:Math.min(s.subtotal,s.cap)]})]}),e.jsxs("div",{className:"mt-2 flex flex-col gap-1.5 rounded-lg border border-neutral-200 bg-neutral-50/60 p-3",children:[s.items.map(r=>e.jsxs("div",{className:"flex items-center gap-2 text-[12.5px]",children:[e.jsxs("span",{className:"min-w-0 flex-1",children:[e.jsx("span",{className:"text-neutral-900",children:r.title}),e.jsx("span",{className:"ml-2 text-[11px] text-neutral-400",children:r.source}),r.note&&e.jsx("div",{className:"text-[11px] leading-relaxed text-neutral-400",children:r.note})]}),e.jsx("input",{type:"number",step:"0.1",className:"num w-[72px] rounded-md border border-neutral-200 bg-white px-1.5 py-0.5 text-right text-[12px] focus:border-brand focus:outline-none",value:r.score,onChange:c=>{var x;if(r.itemKey==="conduct"||r.itemKey==="integrity")re({...H,[r.itemKey]:Number(c.target.value)});else{const I={...H.itemOverrides??{},[r.itemKey]:{...((x=H.itemOverrides)==null?void 0:x[r.itemKey])??{},score:Number(c.target.value)}};re({...H,itemOverrides:I})}}})]},r.itemKey)),!s.items.length&&e.jsx("div",{className:"text-[12px] text-neutral-400",children:"暂无数据"})]})]},s.key)),(f.length>0||X>0)&&e.jsxs("div",{className:"mt-7",children:[e.jsx("div",{className:"flex items-center justify-between border-t border-neutral-200 pt-4",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(De,{size:15,className:"text-neutral-500"}),e.jsx("span",{className:"text-[13.5px] font-bold text-neutral-900",children:"附:加分证明材料"}),e.jsxs("span",{className:"text-[11.5px] text-neutral-400",children:["共 ",f.length," 项记录 · ",X," 张图片(点击可放大)"]})]})}),e.jsx("div",{className:"mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2",children:f.map(s=>e.jsxs("div",{className:"rounded-lg border border-neutral-200 bg-white p-2.5 transition-shadow hover:shadow-md",children:[e.jsxs("div",{className:"flex items-baseline justify-between gap-2",children:[e.jsxs("div",{className:"flex min-w-0 items-center gap-1.5",children:[ce.has(s.recordId)?e.jsx(me,{color:"var(--brand-text)",children:"计分"}):e.jsx(me,{className:"no-print",color:"var(--text-3)",children:"未计分"}),e.jsx("div",{className:"min-w-0 truncate text-[12.5px] font-semibold text-neutral-800",children:s.title})]}),e.jsx("div",{className:"shrink-0 text-[10.5px] text-neutral-400",children:s.meta})]}),e.jsx("div",{className:"mt-2 gap-2 "+(s.images.length>1?"grid grid-cols-2":"flex"),children:s.images.map((r,c)=>e.jsx("button",{onClick:()=>O({src:r,title:s.title,index:W.findIndex(x=>x.src===r)}),className:"group overflow-hidden rounded-md border border-neutral-200 bg-neutral-50",children:e.jsx("img",{src:r,alt:"证明 "+(c+1),className:"aspect-[4/3] w-full object-cover transition-transform group-hover:scale-[1.03]"})},c))})]},s.recordId))})]}),e.jsx("div",{className:"mt-6 border-t border-neutral-200 pt-3 text-[11px] leading-relaxed text-neutral-400",children:"计分依据:《学生综合素质测评评分细则》(默认值可在上方「评分细则参考」查看与修改)。 自动匹配条目均可手动修正分值;「初始化」清除本学年全部手动修正。导出 Word / 打印前请核对加分记录与证明材料一致。"})]}),e.jsx(fe,{open:!!z,onClose:()=>O(null),width:920,title:z?z.title+"("+($+1)+"/"+X+")":"",footer:e.jsxs("div",{className:"flex w-full items-center justify-between",children:[e.jsxs(A,{variant:"secondary",disabled:$<=0,onClick:()=>Z(-1),children:[e.jsx(Te,{size:13})," 上一张"]}),e.jsx("span",{className:"num text-[12px] text-ink-3",children:z?$+1+" / "+X:""}),e.jsxs(A,{variant:"primary",disabled:$>=X-1,onClick:()=>Z(1),children:["下一张 ",e.jsx(Ke,{size:13})]})]}),children:z&&e.jsx("img",{src:z.src,alt:z.title,className:"mx-auto max-h-[74vh] rounded-md"})}),e.jsx(fe,{open:v,onClose:()=>R(!1),width:440,title:"初始化综测",footer:e.jsxs(e.Fragment,{children:[e.jsx(A,{variant:"ghost",onClick:()=>R(!1),children:"取消"}),e.jsx(A,{variant:"primary",onClick:()=>{const s={...w};delete s[h],C(s),se({overrides:s}),R(!1),i("success","已恢复自动计算")},children:"恢复自动计算"})]}),children:e.jsxs("div",{className:"text-[12.5px] leading-relaxed text-ink-2",children:["将清除 ",e.jsx("b",{children:h==="all"||!h?"全部学年":ue(h,D)})," 的全部手动修正 (分值覆盖、操行/诚信分修改),恢复为按细则规则的自动计算结果。",e.jsx("div",{className:"mt-2 text-ink-3",children:"记录数据、个人信息与评分细则不受影响。"})]})})]})}export{Ze as AssessmentPage};