Files
FootPrint/sync-server/www/assets/AdminPage-07xjBzip.js
T
root a5e976fee6 polish(ui): design language uplift - press feedback, brand gradient, header glass, route icon chips, empty states
- button: unified press scale (active:scale-[0.97]), primary gets vertical
  brand gradient + tighter shadow, secondary gains subtle hover shadow
- input/select/textarea: consistent focus border + 1.5px brand ring
- empty states: icon in circular tinted badge with soft ring, larger rhythm
- header: translucent frosted-glass surface (color-mix + backdrop blur),
  per-route lucide icon chip beside page title (desktop), subtitle hidden
  on phones to declutter
- sidebar: active nav icon tinted brand via .nav-active > svg; user card
  avatar gets brand gradient + ring
- dark theme: text-3 contrast raised (#68707e -> #747c8a)
- verified live (headless): h1/chips/glass render on dashboard/timeline/
  assessment/resume; tsc + vite build green; temp instrumentation reverted
2026-10-17 22:33:39 +08:00

34 lines
14 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.
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-DH5SEuAI.js","assets/index-TvH70H3i.css"])))=>i.map(i=>d[i]);
import{c as u,u as me,d as ue,$ as he,f as I,a0 as q,r as i,g as x,j as e,E as T,B as t,a1 as W,m,k as Z,a2 as pe,T as J,P as ve,I as b,l as je,H as Y,a3 as ge,a4 as ye,a5 as be,a6 as fe,a7 as ke,a8 as Ne,a9 as Ce,aa as _e,ab as we}from"./index-DH5SEuAI.js";import{C as ee,a as se,b as ae}from"./card-BI024Nvz.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 ze=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],te=u("Check",ze);/**
* @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 Se=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],Me=u("Copy",Se);/**
* @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:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],Fe=u("RefreshCw",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 Ie=[["path",{d:"M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z",key:"qn84l0"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],qe=u("TicketCheck",Ie);/**
* @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=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]],ne=u("TriangleAlert",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 De=[["path",{d:"M18 21a8 8 0 0 0-16 0",key:"3ypg7q"}],["circle",{cx:"10",cy:"8",r:"5",key:"o932ke"}],["path",{d:"M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3",key:"10s06x"}]],ie=u("UsersRound",De);function Pe({i:r}){return r.used_by&&r.holder_gone?e.jsx(m,{color:"var(--danger)",children:"已销户"}):r.used_by?e.jsx(m,{color:"var(--text-3)",children:"已使用"}):r.valid?e.jsx(m,{color:"var(--success)",children:"可用"}):e.jsx(m,{color:"var(--warning)",children:"已停用"})}function Ke(){var V,G,X,U;const r=me(),D=ue(),l=he(),f=I({queryKey:["me-admin"],queryFn:async()=>(await Ce(()=>import("./index-DH5SEuAI.js").then(a=>a.an),__vite__mapDeps([0,1]))).serverMe(l),enabled:!!q()}),k=I({queryKey:["admin-users"],queryFn:()=>_e(l),enabled:!!q()}),N=I({queryKey:["admin-invites"],queryFn:()=>we(l),enabled:!!q()}),[P,d]=i.useState(""),[re,R]=i.useState(null),[le,C]=i.useState(!1),[v,_]=i.useState(""),[Q,w]=i.useState(""),[de,h]=i.useState(null),[z,S]=i.useState(""),[c,ce]=i.useState("all"),[n,j]=i.useState(null),[K,M]=i.useState(""),[oe,g]=i.useState(!1),y=()=>{r.invalidateQueries({queryKey:["admin-invites"]})},o=s=>d(s.message||"操作失败"),O=x({mutationFn:()=>Ne(l),onSuccess:s=>{var a,p;g(!1),d(""),D("success","演示账号已重建:"+(((a=s.seeded)==null?void 0:a.footprints)??0)+" 条记录 + "+(((p=s.seeded)==null?void 0:p.semesters)??0)+" 学期"),r.invalidateQueries({queryKey:["admin-users"]})},onError:o}),A=x({mutationFn:s=>ke(s,l),onSuccess:s=>{r.invalidateQueries({queryKey:["admin-users"]}),j(null),M(""),d(""),D("success","用户「"+s.username+"」及其全部数据已删除")},onError:o}),L=x({mutationFn:()=>ge(5,l),onSuccess:s=>{y(),d(""),navigator.clipboard.writeText(s.codes.join(`
`)).catch(()=>{})},onError:o}),E=x({mutationFn:()=>ye(v.trim(),Q.trim(),l),onSuccess:()=>{y(),C(!1),_(""),w(""),d("")},onError:o}),F=x({mutationFn:s=>be(s.id,s.patch,l),onSuccess:()=>{y(),h(null),d("")},onError:o}),xe=x({mutationFn:s=>fe(s,l),onSuccess:()=>{y(),d("")},onError:o}),$=i.useMemo(()=>{var s;return((s=N.data)==null?void 0:s.invites)??[]},[N.data]),B=i.useMemo(()=>$.filter(s=>c==="valid"?s.valid&&!s.used_by:c==="used"?!!s.used_by&&!s.holder_gone:c==="gone"?!!s.used_by&&!!s.holder_gone:c==="disabled"?!s.valid&&!s.used_by:!0),[$,c]);if(f.data&&!f.data.is_admin)return e.jsx(T,{title:"需要管理员账号",description:"当前登录账号不是管理员。"});const H=s=>{navigator.clipboard.writeText(s.code),R(s.id),setTimeout(()=>R(a=>a===s.id?null:a),1500)};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:"mb-4 flex items-center justify-between",children:[e.jsx("h2",{className:"text-[15px] font-semibold text-ink-1",children:"管理"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(t,{variant:"secondary",onClick:()=>g(!0),children:[e.jsx(ie,{size:13})," 重建演示账号"]}),e.jsxs(t,{variant:"secondary",onClick:()=>{r.invalidateQueries()},children:[e.jsx(Fe,{size:13})," 刷新"]})]})]}),P&&e.jsxs("div",{className:"mb-3 flex items-center justify-between rounded-md border border-danger/30 bg-danger-subtle px-3 py-2 text-[12px] text-danger",children:[e.jsx("span",{children:P}),e.jsx("button",{className:"rounded p-0.5 hover:bg-surface-3",onClick:()=>d(""),"aria-label":"关闭",children:e.jsx(W,{size:13})})]}),e.jsxs(ee,{children:[e.jsx(se,{title:"用户管理("+(((V=k.data)==null?void 0:V.total)??0)+")",extra:e.jsx(ie,{size:14,className:"text-ink-3"})}),e.jsx(ae,{className:"p-0",children:e.jsxs("div",{className:"divide-y divide-line",children:[(((G=k.data)==null?void 0:G.users)??[]).map(s=>{var p;const a=String(s.id)===String(((p=f.data)==null?void 0:p.user_id)??"");return e.jsxs("div",{className:"flex items-center gap-3 px-4 py-2.5",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"truncate text-[13px] font-medium text-ink-1",children:s.username}),s.is_admin&&e.jsx(m,{color:"var(--brand-text)",children:"管理员"}),a&&e.jsx(m,{color:"var(--text-3)",children:"当前登录"})]}),e.jsxs("div",{className:"num mt-0.5 text-[11px] text-ink-3",children:[Z(s.created_at)," · 记录 ",s.records," · 附件 ",s.attachments,"(",pe(s.attachment_bytes||0),")"]})]}),!a&&e.jsxs(t,{size:"sm",variant:"ghost",className:"text-danger hover:bg-danger-subtle",onClick:()=>{j({id:s.id,username:s.username}),M("")},children:[e.jsx(J,{size:13})," 删除"]})]},s.id)}),!(((X=k.data)==null?void 0:X.users)??[]).length&&e.jsx(T,{title:"暂无用户"})]})})]}),e.jsxs(ee,{children:[e.jsx(se,{title:"邀请码("+(((U=N.data)==null?void 0:U.total)??0)+")",extra:e.jsxs("div",{className:"flex gap-1.5",children:[e.jsxs(t,{size:"sm",variant:"secondary",onClick:()=>{C(s=>!s),d("")},children:[e.jsx(ve,{size:13})," 新建"]}),e.jsx(t,{size:"sm",variant:"primary",disabled:L.isPending,onClick:()=>L.mutate(),children:"批量生成 5 个"})]})}),e.jsxs(ae,{className:"p-0",children:[le&&e.jsxs("div",{className:"flex flex-wrap items-end gap-2 border-b border-line bg-surface-2 px-4 py-3",children:[e.jsxs("label",{className:"flex flex-col gap-1",children:[e.jsx("span",{className:"text-[11px] font-medium text-ink-3",children:"邀请码(4-32 位字母数字-_)"}),e.jsx(b,{className:"w-[210px]",value:v,placeholder:"如 XIONG-2025",onChange:s=>_(s.target.value)})]}),e.jsxs("label",{className:"flex flex-col gap-1",children:[e.jsx("span",{className:"text-[11px] font-medium text-ink-3",children:"备注(可选)"}),e.jsx(b,{className:"w-[180px]",value:Q,placeholder:"发给谁 / 用途",onChange:s=>w(s.target.value),onKeyDown:s=>{s.key==="Enter"&&v.trim()&&E.mutate()}})]}),e.jsxs("div",{className:"flex gap-1.5 pb-0.5",children:[e.jsx(t,{size:"sm",variant:"primary",disabled:!v.trim()||E.isPending,onClick:()=>E.mutate(),children:"创建"}),e.jsx(t,{size:"sm",variant:"ghost",onClick:()=>{C(!1),_(""),w("")},children:"取消"})]})]}),e.jsx("div",{className:"flex gap-1 border-b border-line px-4 py-2 text-[12px]",children:[["all","全部"],["valid","可用"],["used","已使用"],["gone","已销户"],["disabled","已停用"]].map(([s,a])=>e.jsx("button",{className:"rounded-md px-2.5 py-1 transition-colors "+(c===s?"bg-brand-subtle font-medium text-brand-text":"text-ink-3 hover:bg-surface-3"),onClick:()=>ce(s),children:a},s))}),B.length?e.jsx("div",{className:"divide-y divide-line max-h-[430px] overflow-y-auto",children:B.map(s=>e.jsxs("div",{className:"flex flex-wrap items-center gap-2 px-4 py-2.5 text-[12.5px]",children:[e.jsx("button",{className:"num shrink-0 cursor-pointer rounded-sm bg-surface-3 px-1.5 py-0.5 font-mono text-[12px] text-ink-1 transition-colors hover:bg-brand-subtle hover:text-brand-text",title:"点击复制",onClick:()=>H(s),children:re===s.id?e.jsxs("span",{className:"inline-flex items-center gap-1 text-success",children:[e.jsx(te,{size:11}),"已复制"]}):s.code}),e.jsx(Pe,{i:s}),de===s.id?e.jsxs("span",{className:"ml-1 flex min-w-0 flex-1 items-center gap-1",children:[e.jsx(b,{className:"h-7 flex-1 text-[12px]",value:z,autoFocus:!0,placeholder:"备注(64 字以内)",onChange:a=>S(a.target.value),onKeyDown:a=>{a.key==="Enter"&&F.mutate({id:s.id,patch:{remark:z}}),a.key==="Escape"&&h(null)}}),e.jsx(t,{size:"icon-sm",variant:"ghost","aria-label":"保存备注",className:"text-success",onClick:()=>F.mutate({id:s.id,patch:{remark:z}}),children:e.jsx(te,{size:13})}),e.jsx(t,{size:"icon-sm",variant:"ghost","aria-label":"取消",className:"text-ink-3",onClick:()=>h(null),children:e.jsx(W,{size:13})})]}):e.jsx("button",{className:"min-w-0 flex-1 truncate text-left text-[12px] text-ink-3 hover:text-ink-2",title:s.remark?"点击编辑备注":s.created_by_name?"由 "+s.created_by_name+" 创建 · 点击添加备注":"点击添加备注",onClick:()=>{h(s.id),S(s.remark??"")},children:s.remark||(s.created_by_name?"由 "+s.created_by_name+" 创建":e.jsx("span",{className:"opacity-60",children:"添加备注…"}))}),s.used_by_name&&e.jsxs("span",{className:"shrink-0 text-ink-3",children:["被 ",s.used_by_name," 使用于 ",Z(s.used_at)]}),e.jsxs("span",{className:"ml-auto flex shrink-0 items-center gap-0.5",children:[e.jsx(t,{size:"icon-sm",variant:"ghost","aria-label":"复制",className:"text-ink-3",onClick:()=>H(s),children:e.jsx(Me,{size:13})}),!s.used_by&&e.jsxs(e.Fragment,{children:[e.jsx(t,{size:"icon-sm",variant:"ghost","aria-label":"编辑备注",className:"text-ink-3",onClick:()=>{h(s.id),S(s.remark??"")},children:e.jsx(je,{size:13})}),e.jsx(t,{size:"sm",variant:"ghost",className:s.valid?"text-ink-3 hover:text-warning":"text-ink-3 hover:text-success",onClick:()=>F.mutate({id:s.id,patch:{valid:!s.valid}}),children:s.valid?"停用":"启用"})]}),e.jsx(t,{size:"icon-sm",variant:"ghost","aria-label":"删除",className:"text-ink-3 hover:text-danger",onClick:()=>{window.confirm("删除邀请码 "+s.code+"?此操作不可恢复。")&&xe.mutate(s.id)},children:e.jsx(J,{size:13})})]})]},s.id))}):e.jsx(T,{icon:e.jsx(qe,{size:32,strokeWidth:1.5}),title:"暂无符合条件的邀请码",description:"新建自定义邀请码,或批量生成后发给需要注册的用户。"})]})]}),e.jsx(Y,{open:!!n,onClose:()=>j(null),width:460,title:"删除用户(高危操作)",footer:e.jsxs(e.Fragment,{children:[e.jsx(t,{variant:"ghost",onClick:()=>j(null),children:"取消"}),e.jsxs(t,{variant:"primary",className:"!bg-danger !text-white",disabled:K!==(n==null?void 0:n.username)||A.isPending,onClick:()=>n&&A.mutate(n.id),children:[e.jsx(ne,{size:13})," 永久删除"]})]}),children:e.jsxs("div",{className:"flex flex-col gap-3 text-[12.5px]",children:[e.jsxs("div",{className:"flex items-start gap-2 rounded-md border border-danger/30 bg-danger-subtle px-3 py-2.5 text-danger",children:[e.jsx(ne,{size:15,className:"mt-0.5 shrink-0"}),e.jsxs("div",{children:[e.jsx("b",{children:"此操作不可撤销!"}),"将永久删除用户「",n==null?void 0:n.username,"」的全部数据:",e.jsxs("ul",{className:"mt-1 list-disc pl-4 text-[12px] leading-relaxed",children:[e.jsx("li",{children:"账号与登录会话(所有设备将被登出)"}),e.jsx("li",{children:"全部记录、成绩、维度与统计数据"}),e.jsx("li",{children:"全部附件文件(服务器本地与图床 COS 上的副本)"})]})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"mb-1.5 text-ink-2",children:["请输入用户名 ",e.jsx("b",{className:"text-danger",children:n==null?void 0:n.username})," 以确认:"]}),e.jsx(b,{value:K,onChange:s=>M(s.target.value),placeholder:(n==null?void 0:n.username)??"",autoFocus:!0})]})]})}),e.jsx(Y,{open:oe,onClose:()=>g(!1),width:440,title:"重建演示账号",footer:e.jsxs(e.Fragment,{children:[e.jsx(t,{variant:"ghost",onClick:()=>g(!1),children:"取消"}),e.jsx(t,{variant:"primary",disabled:O.isPending,onClick:()=>O.mutate(),children:"重建并重新播种"})]}),children:e.jsxs("div",{className:"text-[12.5px] leading-relaxed text-ink-2",children:["将",e.jsx("b",{children:"删除现有 demo 账号及其全部数据"}),",然后重新创建并播种全套虚拟演示数据 (个人信息档案、25 条足迹(含 10 项竞赛获奖与证书附件)、4 学期学科成绩、简历内容)。",e.jsx("div",{className:"mt-2 text-ink-3",children:"demo / demo1234 · 仅影响演示账号,其他用户不受影响。"})]})})]})}export{Ke as AdminPage};