Files
FootPrint/sync-server/www/assets/ResumePage-CI6B-TV3.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

27 lines
31 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
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 B,d as ve,f as te,r as E,j as e,B as R,ah as Ne,af as L,J as U,S as se,ad as ce,P as q,H as we,a9 as Ce,T as ne,I as W,ag as Se,v as _e}from"./index-DuP8O2p4.js";import{C as ie,a as ae,b as re}from"./card-DR9dPxFa.js";import{R as le,P as ze,C as Re}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 Pe=[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]],de=B("ArrowDown",Pe);/**
* @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:"m5 12 7-7 7 7",key:"hav0vg"}],["path",{d:"M12 19V5",key:"x0mq9r"}]],me=B("ArrowUp",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 Te=[["circle",{cx:"9",cy:"12",r:"1",key:"1vctgf"}],["circle",{cx:"9",cy:"5",r:"1",key:"hp0tcf"}],["circle",{cx:"9",cy:"19",r:"1",key:"fkjjf6"}],["circle",{cx:"15",cy:"12",r:"1",key:"1tmaij"}],["circle",{cx:"15",cy:"5",r:"1",key:"19l28e"}],["circle",{cx:"15",cy:"19",r:"1",key:"f4zoj3"}]],Oe=B("GripVertical",Te);/**
* @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 Fe=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M15 3v18",key:"14nvp0"}],["path",{d:"m8 9 3 3-3 3",key:"12hl5m"}]],Ie=B("PanelRightClose",Fe);/**
* @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 Me=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M15 3v18",key:"14nvp0"}],["path",{d:"m10 15-3-3 3-3",key:"1pgupc"}]],Le=B("PanelRightOpen",Me),D=[{key:"info",defaultTitle:"基本信息",kind:"info",fixed:!0},{key:"education",defaultTitle:"教育经历",kind:"edu",fixed:!0},{key:"skills",defaultTitle:"核心技能",kind:"text"},{key:"projects",defaultTitle:"项目经历",kind:"list",types:["竞赛","科研","实习实践"]},{key:"certs",defaultTitle:"证书与荣誉",kind:"list",types:["荣誉惩处","证书技能"]},{key:"campus",defaultTitle:"在校经历",kind:"list",types:["学生工作","志愿公益"]},{key:"activity",defaultTitle:"文体活动",kind:"list",types:["文体活动"]},{key:"other",defaultTitle:"其他经历",kind:"list"},{key:"self",defaultTitle:"自我评价",kind:"text",fixed:!0}];function A(){return{version:2,template:"tech",accent:"#3056d3",fontScale:1,density:"normal",onePage:!1,sections:D.map(r=>({key:r.key,title:r.defaultTitle,enabled:!0,kind:r.kind,types:r.types}))}}function V(r){const h=A();if(!r||typeof r!="object")return h;const u=r,w=Array.isArray(u.sections)?u.sections:[],y=h.sections.map(l=>{const i=w.find(N=>N.key===l.key),S=D.find(N=>N.key===l.key);return{...l,...i??{},enabled:(i==null?void 0:i.enabled)??!0,title:(i==null?void 0:i.title)??(S==null?void 0:S.defaultTitle)??l.title,kind:S?l.kind:(i==null?void 0:i.kind)??l.kind,types:(S==null?void 0:S.types)??(i==null?void 0:i.types)}}),c=new Set(["awards","competition","practice","research"]);for(const l of w)c.has(l.key)||y.some(i=>i.key===l.key)||y.push({key:l.key,title:l.title||l.key,enabled:l.enabled??!0,kind:"text",text:""});return{version:2,template:u.template??"tech",accent:u.accent??"#3056d3",fontScale:Math.min(1.2,Math.max(.8,u.fontScale??1)),density:u.density??"normal",onePage:u.onePage??!1,sections:y}}function De(){return D.filter(r=>r.types).flatMap(r=>r.types)}function Ke(r){const h=[r.organizer,r.level,r.award_grade].filter(w=>w!=null&&w!=="").map(String).join(" · "),u=[r.result,r.description].filter(w=>w!=null&&w!=="").map(String).join(" — ");return{title:r.title,subtitle:h||void 0,date:r.start_date??r.created_at.slice(0,10),desc:u||void 0,fromRecord:r.id}}function xe(r,h){var c;const u=[],w=De(),y=new Set;for(const l of h.sections){if(!l.enabled)continue;const i=D.find(m=>m.key===l.key);if(l.key==="info"){u.push({key:l.key,title:l.title,kind:"info",hiddenFields:l.hiddenFields??[]});continue}if(l.key==="self"||l.kind==="text"){const m=l.text??"";if(!m.trim())continue;u.push({key:l.key,title:l.title,kind:"text",text:m});continue}const S=new Set(l.excluded??[]);let _=[];const N=i==null?void 0:i.types;N?_=r.filter(m=>N.includes(m.type_name)&&m.status!=="draft"&&!S.has(m.id)&&!y.has(m.id)):_=r.filter(m=>m.status!=="draft"&&!S.has(m.id)&&!y.has(m.id)&&!w.includes(m.type_name));const O=new Map((l.itemOrder??[]).map((m,k)=>[m,k])),j=[];for(const m of _){const k={itemKey:"r"+m.id,...Ke(m),...((c=l.overrides)==null?void 0:c[String(m.id)])??{}};j.push({...k,_ord:O.get(k.itemKey)??9999}),y.add(m.id)}for(const m of l.items??[]){const k=m.id??"c"+(l.items??[]).indexOf(m);j.push({itemKey:k,...m,_ord:O.get(k)??9999})}j.sort((m,k)=>m._ord-k._ord||(k.date??"").localeCompare(m.date??"")),j.length&&u.push({key:l.key,title:l.title,kind:"list",items:j.map(m=>{const{_ord:k,...d}=m;return d})})}return u}const pe=[{key:"tech",name:"互联网技术风",desc:"品牌色标题 + 左侧色条,紧凑现代"},{key:"minimal",name:"极简风",desc:"灰色大写字距标题,大量留白"},{key:"classic",name:"经典风",desc:"黑色衬线标题 + 通栏分隔线,正式通用"}],he=["#3056d3","#0f766e","#7c3aed","#c2410c","#0e7490","#1f2937"],z=r=>String(r??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function Ue(r,h,u,w){var F,s,x;const y=u.fontScale,c=o=>(o*y).toFixed(1)+"pt",l=u.accent,i=h,S={classic:"font-size:"+c(14)+";color:#111;border-bottom:1.5pt solid #333;padding-bottom:3pt;margin:14pt 0 7pt",tech:"font-size:"+c(14)+";color:"+l+";border-bottom:1pt solid "+l+";padding-bottom:3pt;margin:14pt 0 7pt",minimal:"font-size:"+c(11.5)+";color:#555;letter-spacing:1.5pt;text-transform:uppercase;border-bottom:0.75pt solid #e0e0e0;padding-bottom:3pt;margin:14pt 0 7pt"},_=u.template==="tech"?l:"#111",N=[i.school,i.college,i.major,i.degree].filter(o=>o!=="").map(o=>z(o)).join(" · "),O=[i.phone,i.email,i.homepage].filter(o=>o!=="").map(o=>z(o)).join(" · "),j=(((F=u.header)==null?void 0:F.showPhoto)??!0)&&w&&i.photoUrl?'<td style="width:'+(120*y).toFixed(0)+'px;text-align:right;vertical-align:top"><img src="'+z(i.photoUrl)+'" alt="证件照" width="'+Math.round(120*y)+'" height="'+Math.round(160*y)+'" style="width:'+Math.round(120*y)+"px;height:"+Math.round(160*y)+'px" /></td>':"",m='<table style="width:100%;border-collapse:collapse"><tr><td style="vertical-align:top"><div style="font-size:'+c(20)+";font-weight:bold;color:"+_+'">'+z(i.name||"个人简历")+"</div>"+((((s=u.header)==null?void 0:s.showSchool)??!0)&&N?'<div style="color:#555;font-size:'+c(11)+';margin-top:3pt">'+N+"</div>":"")+((((x=u.header)==null?void 0:x.showContact)??!0)&&O?'<div style="color:#555;font-size:'+c(11)+';margin-top:2pt">'+O+"</div>":"")+"</td>"+j+"</tr></table>",k=o=>'<table style="width:100%;border-collapse:collapse;margin-top:'+(u.density==="compact"?4:7)+'pt"><tr><td style="font-weight:bold;font-size:'+c(12)+'">'+z(o.title)+'</td><td style="text-align:right;color:#666;font-size:'+c(10.5)+';white-space:nowrap">'+z(o.date??"")+"</td></tr></table>"+(o.subtitle?'<div style="color:#666;font-size:'+c(10.5)+'">'+z(o.subtitle)+"</div>":"")+(o.desc?'<div style="color:#444;font-size:'+c(11)+';line-height:1.55;margin-top:2pt">'+z(o.desc)+"</div>":""),d=r.map(o=>{let C="";if(o.kind==="edu"){const M=[["学校",i.school??""],["专业",i.major??""],["学制",i.eduLength??""],["学历",i.degree??""],["入学",i.eduStart??""],["毕业",i.eduEnd??""]].filter(b=>b[1]!=="");C='<table style="width:100%;border-collapse:collapse;font-size:'+c(12)+'">'+M.map(b=>'<tr><td style="width:80px;padding:3px 6px;color:#555">'+z(b[0])+'</td><td style="padding:3px 6px">'+z(b[1])+"</td></tr>").join("")+"</table>"+(i.majorCourses?'<div style="margin-top:6pt;font-size:'+c(11.5)+';line-height:1.6"><b>主修课程:</b>'+z(i.majorCourses)+"</div>":"")}else if(o.kind==="info"){const M=[["name","姓名",i.name??""],["gender","性别",i.gender??""],["birth","出生年月",i.birth??""],["nation","民族",i.nation??""],["native","籍贯",i.native??""],["political","政治面貌",i.political??""],["className","班级",i.className??""],["studentId","学号",i.studentId??""],["degree","学历",i.degree??""],["phone","手机",i.phone??""],["email","邮箱",i.email??""]].filter(b=>b[2]!==""&&!(o.hiddenFields??[]).includes(b[0]));C='<table style="width:100%;border-collapse:collapse;font-size:'+c(12)+'">'+M.map((b,I)=>(I%2===0?"<tr>":"")+'<td style="width:80px;padding:3px 6px;color:#555">'+z(b[1])+'</td><td style="padding:3px 6px">'+z(b[2])+"</td>"+(I%2===1?"</tr>":"")).join("")+"</table>"}else o.kind==="list"?C="<div>"+(o.items??[]).map(k).join("")+"</div>":C='<p style="font-size:'+c(12)+';line-height:1.7;margin:0;text-align:justify">'+z(o.text??"").replace(/\n/g,"<br/>")+"</p>";return'<div style="'+S[u.template]+'">'+z(o.title)+"</div>"+C}).join("");return'<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word"><head><meta charset="utf-8"><title>'+z(i.name||"个人简历")+'</title><!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View></w:WordDocument></xml><![endif]--></head><body style="font-family:Calibri,SimSun,sans-serif;margin:36pt;color:#222">'+m+d+"</body></html>"}const Ae=Object.freeze(Object.defineProperty({__proto__:null,ACCENT_CHOICES:he,RESUME_SECTION_DEFS:D,TEMPLATES:pe,assembleSections:xe,defaultResumeConfig:A,migrateResumeConfig:V,resumeToWordHtml:Ue},Symbol.toStringTag,{value:"Module"})),Be={姓名:"name",性别:"gender",出生年月:"birth",民族:"nation",籍贯:"native",政治面貌:"political",班级:"className",学号:"studentId",手机:"phone",邮箱:"email"},He=[["name","姓名"],["gender","性别"],["birth","出生年月"],["nation","民族"],["native","籍贯"],["political","政治面貌"],["className","班级"],["studentId","学号"],["phone","手机"],["email","邮箱"]];function oe(r){const h=/^(\d{4})-(\d{1,2})/.exec(r??"");return h?h[1]+"."+Number(h[2]):r??""}function T({html:r,onCommit:h,className:u}){const w=r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");return e.jsx("span",{contentEditable:!0,suppressContentEditableWarning:!0,spellCheck:!1,className:"outline-none break-words rounded-sm transition-shadow focus:shadow-[0_0_0_1.5px_rgba(48,86,211,0.5)] cursor-text "+(u??""),dangerouslySetInnerHTML:{__html:w},onBlur:y=>{const c=(y.currentTarget.textContent??"").trim();c!==r&&h(c)}})}function $e(){var J,Q,Y,Z,X,ee;const r=ve(),h=te({queryKey:["profile"],queryFn:Se}),u=te({queryKey:["resume-records"],queryFn:()=>_e({page:1,page_size:500})}),[w,y]=E.useState(null),[c,l]=E.useState(""),[i,S]=E.useState(!0),[_,N]=E.useState(new Set),[O,j]=E.useState(1),m=E.useRef(null),k=E.useRef(null);E.useEffect(()=>{var a,n;const t=(a=h.data)==null?void 0:a.content;t&&(y(V(t.resume)),l(((n=t.info)==null?void 0:n.selfIntro)??""))},[h.data]);const d={...((Q=(J=h.data)==null?void 0:J.content)==null?void 0:Q.info)??{}},F=((Y=u.data)==null?void 0:Y.items)??[],s=w??A(),x={...d,selfIntro:c},o=[oe(d.eduStart),oe(d.eduEnd)].filter(Boolean).join("-"),C=E.useMemo(()=>xe(F,s),[F,s]),M=(t,a)=>y(n=>{if(!n)return n;const f=[...n.sections],v=t+a;return v<0||v>=f.length?n:([f[t],f[v]]=[f[v],f[t]],{...n,sections:f})}),b=t=>{y(t),L({info:{...d,selfIntro:c},resume:t}).catch(a=>r("error",a.message))},I=t=>{y(a=>{const n=a??s,f=n.sections.map(p=>{var g;if(p.key!==t.secKey)return p;if(t.field==="text")return{...p,text:t.value};if(t.fromRecord!=null){const P={...((g=p.overrides)==null?void 0:g[t.fromRecord])??{},[t.field]:t.value};return{...p,overrides:{...p.overrides??{},[t.fromRecord]:P}}}if(t.itemIdx!=null&&p.items){const P=[...p.items];return P[t.itemIdx]={...P[t.itemIdx],[t.field]:t.value},{...p,items:P}}return p}),v={...n,sections:f};return L({info:{...d,selfIntro:c},resume:v}).catch(p=>r("error",p.message)),v})},H=(t,a)=>{L({info:{...x,[t]:a}}).then(()=>h.refetch()).catch(n=>r("error",n.message))};E.useEffect(()=>{if(!s.onePage){j(1);return}const t=m.current;if(!t)return;const a=1043,n=t.scrollHeight;j(n>a?Math.max(.7,a/n):1)},[s.onePage,s.fontScale,s.density,C]);const G=s.fontScale*(s.onePage?O:1),ue=()=>{Ce(()=>Promise.resolve().then(()=>Ae),void 0).then(function(t){const a=t.resumeToWordHtml(C,x,s,d.photoUrl),n=new Blob(["\uFEFF"+a],{type:"application/msword"}),f=URL.createObjectURL(n),v=document.createElement("a");v.href=f,v.download=(d.name||"个人简历")+".doc",v.click(),setTimeout(()=>URL.revokeObjectURL(f),6e4),r("success","Word 文档已导出")})},$=t=>{const a="custom_"+Date.now();b({...s,sections:[...s.sections,t==="text"?{key:a,title:"自定义板块",enabled:!0,kind:"text",text:""}:{key:a,title:"自定义经历",enabled:!0,kind:"list",items:[]}]}),N(n=>new Set(n).add(a))},fe=t=>{var a,n;return((n=(a=D.find(f=>f.key===t))==null?void 0:a.types)==null?void 0:n.join(" / "))??"固定板块"},[ge,K]=E.useState(!1),ye=()=>{const t=A();y(t),N(new Set),L({info:{...d,selfIntro:c},resume:t}).then(()=>h.refetch()).then(()=>{K(!1),r("success","简历已初始化为默认模板")}).catch(a=>r("error",a.message))},be=()=>{const t=A();t.sections=t.sections.map(a=>({...a,text:a.kind==="text"?"":a.text,items:a.kind==="list"?[]:a.items,overrides:{},excluded:[],itemOrder:[],hiddenFields:[]})),y(t),N(new Set),L({info:{...d,selfIntro:c},resume:t}).then(()=>h.refetch()).then(()=>{K(!1),r("success","简历数据已彻底清空")}).catch(a=>r("error",a.message))},je=()=>{const t=new Blob([JSON.stringify(s,null,2)],{type:"application/json"}),a=URL.createObjectURL(t),n=document.createElement("a");n.href=a,n.download="简历配置.json",n.click(),setTimeout(()=>URL.revokeObjectURL(a),6e4),r("success","简历配置已导出")},ke=t=>{t&&t.text().then(a=>{try{const n=V(JSON.parse(a));b(n),r("success","配置已导入")}catch{r("error","配置文件格式不正确")}})};return e.jsxs("div",{className:"mx-auto max-w-[1280px] 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.jsx("h2",{className:"text-[15px] font-semibold text-ink-1",children:"我的简历"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsxs(R,{variant:"secondary",onClick:()=>S(t=>!t),children:[i?e.jsx(Ie,{size:13}):e.jsx(Le,{size:13}),i?"收起设置":"展开设置"]}),e.jsxs(R,{variant:"ghost",className:"text-danger hover:bg-danger-subtle",onClick:()=>K(!0),children:[e.jsx(le,{size:13})," 初始化简历"]}),e.jsxs(R,{variant:"secondary",onClick:ue,children:[e.jsx(Ne,{size:13})," 导出 Word"]}),e.jsxs(R,{variant:"primary",onClick:()=>window.print(),children:[e.jsx(ze,{size:13})," 打印 / PDF"]})]})]}),e.jsxs("div",{className:"grid gap-4 "+(i?"lg:grid-cols-[1fr_340px]":"grid-cols-1"),children:[e.jsxs("div",{children:[e.jsx("div",{className:"print-sheet mx-auto w-full max-w-[794px] rounded-lg border border-line bg-white shadow-card",style:{minHeight:s.onePage?1123:void 0},children:e.jsxs("div",{ref:m,className:"min-w-0 overflow-x-hidden p-5 sm:p-10 resume-tpl-"+s.template,style:{zoom:G,"--accent":s.accent},children:[e.jsxs("div",{className:"mb-6 flex items-start justify-between gap-4 sm:gap-6",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("h1",{className:"text-[26px] font-bold leading-tight "+(s.template==="tech"?"text-[var(--accent)]":"text-neutral-900"),children:d.name||"姓名"}),(((Z=s.header)==null?void 0:Z.showSchool)??!0)&&e.jsx("div",{className:"mt-1.5 text-[12.5px] text-neutral-500",children:[d.school,d.college,d.major,d.degree].filter(Boolean).join(" · ")||"完善个人信息后自动展示"}),(((X=s.header)==null?void 0:X.showContact)??!0)&&e.jsx("div",{className:"mt-1 text-[12px] text-neutral-500",children:[d.phone,d.email,d.homepage].filter(Boolean).join(" · ")})]}),(((ee=s.header)==null?void 0:ee.showPhoto)??!0)&&d.photoUrl&&e.jsx("img",{src:d.photoUrl,alt:"证件照",className:"h-[110px] w-[82px] rounded border border-neutral-200 object-cover"})]}),C.map(t=>{var a;return e.jsxs("div",{className:"mb-5 r-sec",children:[e.jsx("div",{className:"r-sec-title mb-2.5 text-[14px] font-bold text-neutral-900",children:e.jsx(T,{html:t.title,onCommit:n=>I({secKey:t.key,field:"title",value:n})})}),t.kind==="edu"?e.jsxs("div",{children:[e.jsxs("div",{className:"flex flex-wrap items-baseline justify-between gap-x-4 gap-y-0.5 text-[13px]",children:[e.jsx("span",{className:"num shrink-0 text-neutral-500",children:e.jsx(T,{html:o,onCommit:n=>{const f=/(\d{4})\.(\d{1,2})\s*[-—~至]\s*(\d{4})\.(\d{1,2})/.exec(n);if(!f){r("error","时间格式:2023.9-2027.6");return}L({info:{...x,eduStart:f[1]+"-"+f[2].padStart(2,"0"),eduEnd:f[3]+"-"+f[4].padStart(2,"0")}}).then(()=>h.refetch()).catch(v=>r("error",v.message))}})}),e.jsx("span",{className:"min-w-0 font-semibold text-neutral-900",children:e.jsx(T,{html:d.school??"",onCommit:n=>H("school",n)})}),e.jsxs("span",{className:"shrink-0 text-neutral-600",children:[e.jsx(T,{html:d.major??"",onCommit:n=>H("major",n)}),d.degree?e.jsxs("span",{children:[" | ",e.jsx(T,{html:d.degree,onCommit:n=>H("degree",n)})]}):null]})]}),d.majorCourses&&e.jsxs("p",{className:"mt-1.5 text-[12px] leading-relaxed text-justify text-neutral-700",children:[e.jsx("b",{children:"主修课程:"}),e.jsx(T,{html:d.majorCourses,onCommit:n=>H("majorCourses",n)})]})]}):t.kind==="info"?e.jsx("div",{className:"grid grid-cols-1 gap-x-8 gap-y-1 text-[12.5px] sm:grid-cols-2",children:[["姓名",d.name],["性别",d.gender],["出生年月",d.birth],["民族",d.nation],["籍贯",d.native],["政治面貌",d.political],["班级",d.className],["学号",d.studentId],["手机",d.phone],["邮箱",d.email]].filter(n=>n[1]&&!(t.hiddenFields??[]).includes(Be[n[0]])).map(n=>e.jsxs("div",{className:"flex gap-2",children:[e.jsx("span",{className:"w-16 shrink-0 text-neutral-500",children:n[0]}),e.jsx("span",{children:n[1]})]},n[0]))}):t.kind==="text"?e.jsx("p",{className:"whitespace-pre-wrap text-justify text-neutral-700 "+(s.density==="compact"?"":"leading-relaxed"),children:e.jsx(T,{html:t.text??"",onCommit:n=>I({secKey:t.key,field:"text",value:n})})}):e.jsx("div",{className:s.density==="compact"?"flex flex-col gap-1.5":"flex flex-col gap-3",children:(a=t.items)==null?void 0:a.map((n,f)=>e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-baseline justify-between gap-3",children:[e.jsx("span",{className:"text-[13px] font-semibold text-neutral-900 r-item-title",children:e.jsx(T,{html:n.title,onCommit:v=>I({secKey:t.key,field:"title",value:v,itemIdx:f,fromRecord:n.fromRecord})})}),e.jsx("span",{className:"num shrink-0 text-[11.5px] text-neutral-500",children:e.jsx(T,{html:n.date??"",onCommit:v=>I({secKey:t.key,field:"date",value:v,itemIdx:f,fromRecord:n.fromRecord})})})]}),n.subtitle&&e.jsx("div",{className:"text-[12px] text-neutral-600",children:n.subtitle}),n.desc&&e.jsx("p",{className:"text-justify text-[12px] text-neutral-500 "+(s.density==="compact"?"":"mt-0.5"),children:e.jsx(T,{html:n.desc,onCommit:v=>I({secKey:t.key,field:"desc",value:v,itemIdx:f,fromRecord:n.fromRecord})})})]},n.itemKey??(n.title??"")+f))})]},t.key)})]})}),s.onePage&&e.jsxs("div",{className:"no-print mt-2 text-center text-[11.5px] text-ink-3",children:["一页限定已启用:内容自动缩放 ",Math.round(G*100),"% 以适配单页 A4"]})]}),i&&e.jsxs("div",{className:"no-print flex flex-col gap-4 lg:sticky lg:top-4 lg:max-h-[calc(100vh-2rem)] lg:self-start lg:overflow-y-auto lg:pr-1",children:[e.jsxs(ie,{children:[e.jsx(ae,{title:"模板与风格"}),e.jsxs(re,{className:"flex flex-col gap-3",children:[e.jsx("div",{className:"grid grid-cols-1 gap-2",children:pe.map(t=>e.jsxs("button",{className:"rounded-md border px-3 py-2 text-left transition-all "+(s.template===t.key?"border-brand bg-brand-subtle":"border-line bg-surface hover:border-line-strong"),onClick:()=>b({...s,template:t.key}),children:[e.jsx("div",{className:"text-[12.5px] font-semibold "+(s.template===t.key?"text-brand-text":"text-ink-1"),children:t.name}),e.jsx("div",{className:"text-[11px] text-ink-3",children:t.desc})]},t.key))}),e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[e.jsx(U,{label:"主题色",children:e.jsx("div",{className:"flex flex-wrap gap-1.5 pt-1",children:he.map(t=>e.jsx("button",{"aria-label":"主题色 "+t,className:"size-5 rounded-full border-2 transition-transform "+(s.accent===t?"scale-110 border-ink-1":"border-transparent"),style:{backgroundColor:t},onClick:()=>b({...s,accent:t})},t))})}),e.jsx(U,{label:"字号",children:e.jsx(se,{value:String(s.fontScale),onChange:t=>b({...s,fontScale:Number(t.target.value)}),options:[{value:"0.9",label:"小"},{value:"1",label:"标准"},{value:"1.1",label:"大"}]})})]}),e.jsx(U,{label:"头部布局",children:e.jsx("div",{className:"flex flex-wrap gap-1.5 pt-1",children:[["showPhoto","证件照"],["showContact","联系行"],["showSchool","学校行"]].map(t=>{var n;const a=((n=s.header)==null?void 0:n[t[0]])??!0;return e.jsx("button",{className:"rounded-md border px-2.5 py-1 text-[11.5px] transition-colors "+(a?"border-brand bg-brand-subtle text-brand-text":"border-line bg-surface text-ink-3"),onClick:()=>b({...s,header:{...s.header,[t[0]]:!a}}),children:t[1]},t[0])})})}),e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[e.jsx(U,{label:"间距",children:e.jsx(se,{value:s.density,onChange:t=>b({...s,density:t.target.value}),options:[{value:"compact",label:"紧凑"},{value:"normal",label:"标准"},{value:"relaxed",label:"宽松"}]})}),e.jsx(U,{label:"单页限定",children:e.jsx("button",{className:"flex h-9 w-full items-center justify-center gap-1.5 rounded-md border text-[12px] transition-colors "+(s.onePage?"border-brand bg-brand-subtle text-brand-text":"border-line bg-surface text-ink-2"),onClick:()=>b({...s,onePage:!s.onePage}),children:s.onePage?"已开启":"已关闭"})})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-2 border-t border-line pt-3",children:[e.jsx(R,{size:"sm",variant:"secondary",onClick:je,children:"导出配置"}),e.jsxs("label",{className:"flex h-8 cursor-pointer items-center justify-center rounded-md border border-line bg-surface text-[12px] text-ink-2 transition-colors hover:border-line-strong",children:["导入配置",e.jsx("input",{type:"file",accept:"application/json",className:"hidden",onChange:t=>{var a;ke((a=t.target.files)==null?void 0:a[0]),t.target.value=""}})]})]})]})]}),e.jsxs(ie,{children:[e.jsx(ae,{title:"板块布局",extra:e.jsx("span",{className:"text-[11px] text-ink-3",children:"拖动排序 · 点击预览可改内容"})}),e.jsx(re,{className:"p-0",children:e.jsxs("div",{className:"flex flex-col divide-y divide-line",children:[s.sections.map((t,a)=>{var v;const n=_.has(t.key),f=t.kind==="info"?"固定":t.kind==="text"?"文本":t.kind==="edu"?"教育":"记录";return e.jsxs("div",{draggable:!0,onDragStart:()=>{k.current=t.key},onDragOver:p=>p.preventDefault(),onDrop:()=>{const p=s.sections.findIndex(g=>g.key===k.current);if(p>=0&&p!==a){const g=[...s.sections],P=g.splice(p,1)[0];g.splice(a,0,P),b({...s,sections:g})}k.current=null},className:"bg-surface "+(k.current===t.key?"opacity-40":""),children:[e.jsxs("div",{className:"flex items-center gap-1 px-3 py-2",children:[e.jsx(Oe,{size:13,className:"shrink-0 cursor-grab text-ink-3"}),e.jsx("input",{className:"min-w-0 flex-1 rounded-sm border border-transparent bg-transparent px-1 py-0.5 text-[12.5px] font-medium text-ink-1 hover:border-line focus:border-brand focus:outline-none",value:t.title,onChange:p=>b({...s,sections:s.sections.map(g=>g.key===t.key?{...g,title:p.target.value}:g)})}),e.jsx("button",{className:"rounded p-1 text-ink-3 hover:text-ink-1 disabled:opacity-30","aria-label":"上移",disabled:a===0,onClick:()=>M(a,-1),children:e.jsx(me,{size:12})}),e.jsx("button",{className:"rounded p-1 text-ink-3 hover:text-ink-1 disabled:opacity-30","aria-label":"下移",disabled:a===s.sections.length-1,onClick:()=>M(a,1),children:e.jsx(de,{size:12})}),e.jsx("button",{className:"ml-0.5 rounded-sm border px-1.5 py-0.5 text-[11px] transition-colors "+(t.enabled?"border-brand/40 bg-brand-subtle text-brand-text":"border-line bg-surface-2 text-ink-3"),onClick:()=>b({...s,sections:s.sections.map(p=>p.key===t.key?{...p,enabled:!p.enabled}:p)}),children:t.enabled?"显示":"隐藏"}),e.jsx("button",{className:"rounded p-1 text-ink-3 hover:text-ink-1","aria-label":"展开编辑",onClick:()=>N(p=>{const g=new Set(p);return g.has(t.key)?g.delete(t.key):g.add(t.key),g}),children:e.jsx(Re,{size:13,className:"transition-transform "+(n?"rotate-90":"")})})]}),n&&e.jsx("div",{className:"border-t border-line bg-surface-2 px-3 py-2.5",children:t.kind==="text"?e.jsx(ce,{rows:4,value:t.text??"",placeholder:"输入该板块内容…",onChange:p=>b({...s,sections:s.sections.map(g=>g.key===t.key?{...g,text:p.target.value}:g)})}):t.kind==="info"?e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("div",{className:"mb-0.5 text-[11px] text-ink-3",children:"勾选 = 在基本信息中显示"}),He.map(p=>e.jsxs("label",{className:"flex cursor-pointer items-center gap-2 text-[12px]",children:[e.jsx("input",{type:"checkbox",className:"size-3.5",checked:!(t.hiddenFields??[]).includes(p[0]),onChange:()=>b({...s,sections:s.sections.map(g=>{if(g.key!==t.key)return g;const P=new Set(g.hiddenFields??[]);return P.has(p[0])?P.delete(p[0]):P.add(p[0]),{...g,hiddenFields:[...P]}})})}),p[1]]},p[0]))]}):t.kind==="list"?e.jsx(We,{cfg:s,section:t,records:F,persist:b,sectionTypes:fe(t.key)}):e.jsx("p",{className:"text-[11.5px] text-ink-3",children:"该板块内容来自「个人信息」页与记录数据。"})}),e.jsxs("div",{className:"px-3 pb-1.5 text-[10.5px] text-ink-3",children:[f,(v=t.types)!=null&&v.length?" · "+t.types.join("/"):""]})]},t.key)}),e.jsxs("div",{className:"grid grid-cols-2 gap-2 p-3",children:[e.jsxs(R,{size:"sm",variant:"secondary",onClick:()=>$("text"),children:[e.jsx(q,{size:13})," 文本板块"]}),e.jsxs(R,{size:"sm",variant:"secondary",onClick:()=>$("list"),children:[e.jsx(q,{size:13})," 列表板块"]})]})]})})]})]})]}),e.jsx(we,{open:ge,onClose:()=>K(!1),width:440,title:"初始化简历",footer:e.jsxs(e.Fragment,{children:[e.jsx(R,{variant:"ghost",onClick:()=>K(!1),children:"取消"}),e.jsx(R,{variant:"secondary",onClick:be,children:"彻底清空"}),e.jsxs(R,{variant:"primary",onClick:ye,children:[e.jsx(le,{size:13})," 恢复默认模板"]})]}),children:e.jsxs("div",{className:"text-[12.5px] leading-relaxed text-ink-2",children:[e.jsx("b",{children:"恢复默认模板"}),":清除个性化修改,恢复默认板块与示例内容。",e.jsxs("div",{className:"mt-1",children:[e.jsx("b",{children:"彻底清空"}),":保留板块结构,内容全部置空,从零开始。"]}),e.jsx("div",{className:"mt-2 text-ink-3",children:"两种操作都不影响记录数据与个人信息。"}),e.jsx("div",{className:"mt-2 text-ink-3",children:"记录数据与个人信息不受影响,随时可重新修改。"})]})})]})}function We({cfg:r,section:h,records:u,persist:w,sectionTypes:y}){const[c,l]=E.useState(null),i=new Set(h.excluded??[]),S=u.filter(s=>(h.types??[]).includes(s.type_name)&&s.status!=="draft").sort((s,x)=>(x.start_date??x.created_at).localeCompare(s.start_date??s.created_at)),_=h.items??[],N=s=>w({...r,sections:r.sections.map(x=>x.key===h.key?{...x,...s}:x)}),O=s=>{const x=new Set(h.excluded??[]);x.has(s)?x.delete(s):x.add(s),N({excluded:[...x]})},j=[...h.itemOrder??[]],m=new Set(j),k=new Map;S.forEach(s=>{const x="r"+s.id;k.set(x,{type:"record",r:s}),m.has(x)||(j.push(x),m.add(x))}),_.forEach((s,x)=>{const o=s.id??"c"+x;k.set(o,{type:"custom",c:s,idx:x}),m.has(o)||(j.push(o),m.add(o))});const d=(s,x)=>{const o=j.indexOf(s),C=o+x;o<0||C<0||C>=j.length||([j[o],j[C]]=[j[C],j[o]],N({itemOrder:j}))},F=({k:s})=>e.jsxs("span",{className:"flex shrink-0 flex-col",children:[e.jsx("button",{className:"text-ink-3 hover:text-ink-1","aria-label":"上移",onClick:()=>d(s,-1),children:e.jsx(me,{size:11})}),e.jsx("button",{className:"text-ink-3 hover:text-ink-1","aria-label":"下移",onClick:()=>d(s,1),children:e.jsx(de,{size:11})})]});return e.jsxs("div",{className:"flex flex-col gap-2.5",children:[e.jsx("div",{className:"text-[11px] text-ink-3",children:"条目排序(↑↓)· 记录勾选显隐 · 自定义条目可增删"}),j.map(s=>{const x=k.get(s);if(!x)return null;if(x.type==="record"){const o=x.r;return e.jsxs("div",{className:"flex items-center gap-2 text-[12px]",children:[e.jsx(F,{k:s}),e.jsxs("label",{className:"flex min-w-0 flex-1 cursor-pointer items-center gap-2",children:[e.jsx("input",{type:"checkbox",className:"size-3.5",checked:!i.has(o.id),onChange:()=>O(o.id)}),e.jsx("span",{className:"min-w-0 flex-1 truncate text-ink-1",children:o.title})]}),e.jsx("span",{className:"num shrink-0 text-[11px] text-ink-3",children:(o.start_date??"").slice(0,7)})]},s)}return e.jsxs("div",{className:"flex items-center gap-2 rounded-md border border-line bg-surface p-1.5 text-[12px]",children:[e.jsx(F,{k:s}),e.jsxs("span",{className:"min-w-0 flex-1 truncate text-ink-1",children:[x.c.title,e.jsxs("span",{className:"text-ink-3",children:[" · ",x.c.date??""]})]}),e.jsx("button",{className:"text-ink-3 hover:text-danger","aria-label":"删除条目",onClick:()=>N({items:_.filter((o,C)=>C!==x.idx)}),children:e.jsx(ne,{size:12})})]},s)}),e.jsxs("div",{className:"mt-1 border-t border-line pt-2",children:[e.jsxs("div",{className:"mb-1.5 flex items-center justify-between",children:[e.jsxs("span",{className:"text-[11.5px] font-medium text-ink-2",children:["自定义条目(",_.length,")"]}),e.jsxs(R,{size:"sm",variant:"ghost",onClick:()=>l({title:""}),children:[e.jsx(q,{size:12})," 添加"]})]}),c&&e.jsxs("div",{className:"mb-2 rounded-md border border-line bg-surface p-2",children:[e.jsx(W,{className:"mb-1.5",placeholder:"标题",value:c.title,onChange:s=>l({...c,title:s.target.value})}),e.jsxs("div",{className:"mb-1.5 grid grid-cols-2 gap-1.5",children:[e.jsx(W,{placeholder:"副标题(单位/角色)",value:c.subtitle??"",onChange:s=>l({...c,subtitle:s.target.value})}),e.jsx(W,{placeholder:"日期(2025.9)",value:c.date??"",onChange:s=>l({...c,date:s.target.value})})]}),e.jsx(ce,{rows:2,placeholder:"描述:做了什么、负责什么、学会什么",value:c.desc??"",onChange:s=>l({...c,desc:s.target.value})}),e.jsxs("div",{className:"mt-1.5 flex gap-1.5",children:[e.jsx(R,{size:"sm",variant:"primary",disabled:!c.title.trim(),onClick:()=>{c.title.trim()&&(N({items:[..._,{...c,id:"c"+Date.now()}]}),l(null))},children:"添加"}),e.jsx(R,{size:"sm",variant:"ghost",onClick:()=>l(null),children:"取消"})]})]}),_.map((s,x)=>e.jsxs("div",{className:"flex items-center gap-2 rounded-md border border-line bg-surface p-1.5 text-[12px]",children:[e.jsxs("span",{className:"min-w-0 flex-1 truncate text-ink-1",children:[s.title,e.jsxs("span",{className:"text-ink-3",children:[" · ",s.date??""]})]}),e.jsx("button",{className:"text-ink-3 hover:text-danger","aria-label":"删除条目",onClick:()=>N({items:_.filter((o,C)=>C!==x)}),children:e.jsx(ne,{size:12})})]},x)),!_.length&&!c&&e.jsx("p",{className:"text-[11.5px] text-ink-3",children:"暂无自定义条目"})]}),e.jsxs("p",{className:"text-[11px] text-ink-3",children:["「",y,"」相关条目来自你的记录数据,此处可隐藏不需要展示的条目;描述可在预览中点击直接修改。"]})]})}export{$e as ResumePage};