更新说明:\n1. 新增用户端与管理员端 Passkey 登录/注册/设备管理(最多3台,支持设备备注、删除设备)。\n2. 修复 Passkey 注册与登录流程中的浏览器/证书/CSRF相关问题,增强错误提示。\n3. 前台登录页改为独立入口,首屏仅加载必要资源,其他页面按需加载。\n4. 系统配置页改为静默获取金山文档状态,避免首屏阻塞,并优化状态展示为“检测中/已登录/未登录/异常”。\n5. 补充后端接口与页面渲染适配,修复多入口下样式依赖注入问题。\n6. 同步更新前后台构建产物与相关静态资源。
2 lines
9.2 KiB
JavaScript
2 lines
9.2 KiB
JavaScript
import{a as g,_ as X}from"./index-BMIn4N2u.js";import{a as u,E as D}from"./vendor-element-B5S5pUKo.js";import{r as f,o as Y,aj as y,ap as Z,n as K,q as V,t as _,L as s,E as l,I as C,F as Q,D as I}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function W(t){const{data:e}=await g.put("/admin/username",{new_username:t});return e}async function ee(t={}){const e=String(t.currentPassword||""),r=String(t.newPassword||""),{data:o}=await g.put("/admin/password",{current_password:e,new_password:r});return o}async function ae(){const{data:t}=await g.post("/logout");return t}async function te(){const{data:t}=await g.get("/admin/passkeys");return t}async function M(t={}){const{data:e}=await g.post("/admin/passkeys/register/options",t);return e}async function ne(t={}){const{data:e}=await g.post("/admin/passkeys/register/verify",t);return e}async function se(t){const{data:e}=await g.delete(`/admin/passkeys/${t}`);return e}async function re(t={}){const{data:e}=await g.post("/admin/passkeys/client-error",t);return e}function le(t){if(!t||typeof t!="object")throw new Error("Passkey参数无效");return t.publicKey&&typeof t.publicKey=="object"?t.publicKey:t}function U(t){const e=String(t||""),r="=".repeat((4-e.length%4)%4),o=(e+r).replace(/-/g,"+").replace(/_/g,"/"),m=window.atob(o),i=new Uint8Array(m.length);for(let w=0;w<m.length;w+=1)i[w]=m.charCodeAt(w);return i}function h(t){const e=t instanceof ArrayBuffer?new Uint8Array(t):new Uint8Array(t||[]);let r="";for(let o=0;o<e.length;o+=1)r+=String.fromCharCode(e[o]);return window.btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function oe(t){const e=le(t),r={...e,challenge:U(e.challenge),user:{...e.user,id:U(e.user?.id)}};return Array.isArray(e.excludeCredentials)&&(r.excludeCredentials=e.excludeCredentials.map(o=>({...o,id:U(o.id)}))),r}function ie(t){if(!t)return null;const e=t.response||{},r={id:t.id,rawId:h(t.rawId),type:t.type,authenticatorAttachment:t.authenticatorAttachment||void 0,response:{}};return e.clientDataJSON&&(r.response.clientDataJSON=h(e.clientDataJSON)),e.attestationObject&&(r.response.attestationObject=h(e.attestationObject)),e.authenticatorData&&(r.response.authenticatorData=h(e.authenticatorData)),e.signature&&(r.response.signature=h(e.signature)),e.userHandle?r.response.userHandle=h(e.userHandle):r.response.userHandle=null,typeof e.getTransports=="function"&&(r.response.transports=e.getTransports()||[]),r}function ue(){return typeof window<"u"&&window.isSecureContext&&!!window.PublicKeyCredential&&!!navigator.credentials}async function de(t){const e=oe(t),r=await navigator.credentials.create({publicKey:e});return ie(r)}const ce={class:"page-stack"},pe=24e4,fe={__name:"SettingsPage",setup(t){const e=f(""),r=f(""),o=f(""),m=f(""),i=f(!1),w=f(!1),S=f(!1),A=f(""),k=f([]),v=f(null),b=f(0);function j(n){const a=String(n||"");return a.length<8?{ok:!1,message:"密码长度至少8位"}:a.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(a)||!/\d/.test(a)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function E(){try{await ae()}catch{}finally{window.location.href="/yuyx"}}async function H(){const n=e.value.trim();if(!n){u.error("请输入新用户名");return}try{await D.confirm(`确定将管理员用户名修改为「${n}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}i.value=!0;try{await W(n),u.success("用户名修改成功,请重新登录"),e.value="",setTimeout(E,1200)}catch{}finally{i.value=!1}}async function R(){const n=r.value,a=o.value,d=m.value;if(!n){u.error("请输入当前密码");return}if(!a){u.error("请输入新密码");return}const c=j(a);if(!c.ok){u.error(c.message);return}if(a!==d){u.error("两次输入的新密码不一致");return}try{await D.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}i.value=!0;try{await ee({currentPassword:n,newPassword:a}),u.success("密码修改成功,请重新登录"),r.value="",o.value="",m.value="",setTimeout(E,1200)}catch{}finally{i.value=!1}}async function T(){w.value=!0;try{const n=await te();k.value=Array.isArray(n?.items)?n.items:[],k.value.length<3?await N():(v.value=null,b.value=0)}catch{k.value=[],v.value=null,b.value=0}finally{w.value=!1}}function $(){return!v.value||Date.now()-Number(b.value||0)>pe?null:v.value}async function N(){try{const n=await M({});v.value=n,b.value=Date.now()}catch{v.value=null,b.value=0}}async function z(){if(!ue()){u.error("当前浏览器或环境不支持Passkey(需 HTTPS)");return}if(k.value.length>=3){u.error("最多可绑定3台设备");return}S.value=!0;try{let n=$();n||(n=await M({}));const a=await de(n?.publicKey||{});await ne({credential:a,device_name:A.value.trim()}),v.value=null,b.value=0,A.value="",u.success("Passkey设备添加成功"),await T()}catch(n){try{await re({stage:"register",source:"admin-settings",name:n?.name||"",message:n?.message||"",code:n?.code||"",user_agent:navigator.userAgent||""})}catch{}v.value=null,b.value=0,await N();const a=n?.response?.data,d=n?.message?String(n.message):"",c=a?.error||(n?.name==="NotAllowedError"?`Passkey注册未完成(浏览器返回:${d||"未提供详细原因"})`:d||"Passkey添加失败");u.error(c)}finally{S.value=!1}}async function J(n){try{await D.confirm(`确定删除设备「${n?.device_name||"未命名设备"}」吗?`,"删除Passkey设备",{confirmButtonText:"删除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await se(n.id),u.success("设备已删除"),await T()}catch(a){const d=a?.response?.data;u.error(d?.error||"删除失败")}}return Y(()=>{T()}),(n,a)=>{const d=y("el-input"),c=y("el-form-item"),O=y("el-form"),x=y("el-button"),B=y("el-card"),L=y("el-alert"),F=y("el-empty"),P=y("el-table-column"),q=y("el-table"),G=Z("loading");return V(),K("div",ce,[a[13]||(a[13]=_("div",{class:"app-page-title"},[_("h2",null,"设置"),_("span",{class:"app-muted"},"管理员账号设置")],-1)),s(B,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[a[6]||(a[6]=_("h3",{class:"section-title"},"修改管理员用户名",-1)),s(O,{"label-width":"120px"},{default:l(()=>[s(c,{label:"新用户名"},{default:l(()=>[s(d,{modelValue:e.value,"onUpdate:modelValue":a[0]||(a[0]=p=>e.value=p),placeholder:"输入新用户名",disabled:i.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),s(x,{type:"primary",loading:i.value,onClick:H},{default:l(()=>[...a[5]||(a[5]=[C("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),s(B,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[a[8]||(a[8]=_("h3",{class:"section-title"},"修改管理员密码",-1)),s(O,{"label-width":"120px"},{default:l(()=>[s(c,{label:"当前密码"},{default:l(()=>[s(d,{modelValue:r.value,"onUpdate:modelValue":a[1]||(a[1]=p=>r.value=p),type:"password","show-password":"",placeholder:"输入当前密码",disabled:i.value},null,8,["modelValue","disabled"])]),_:1}),s(c,{label:"新密码"},{default:l(()=>[s(d,{modelValue:o.value,"onUpdate:modelValue":a[2]||(a[2]=p=>o.value=p),type:"password","show-password":"",placeholder:"输入新密码",disabled:i.value},null,8,["modelValue","disabled"])]),_:1}),s(c,{label:"确认新密码"},{default:l(()=>[s(d,{modelValue:m.value,"onUpdate:modelValue":a[3]||(a[3]=p=>m.value=p),type:"password","show-password":"",placeholder:"再次输入新密码",disabled:i.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),s(x,{type:"primary",loading:i.value,onClick:R},{default:l(()=>[...a[7]||(a[7]=[C("保存密码",-1)])]),_:1},8,["loading"]),a[9]||(a[9]=_("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1}),s(B,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[a[12]||(a[12]=_("h3",{class:"section-title"},"Passkey设备",-1)),s(L,{type:"info",closable:!1,title:"最多可绑定3台设备,可用于管理员无密码登录。","show-icon":"",class:"help-alert"}),s(O,{inline:""},{default:l(()=>[s(c,{label:"设备备注"},{default:l(()=>[s(d,{modelValue:A.value,"onUpdate:modelValue":a[4]||(a[4]=p=>A.value=p),placeholder:"例如:值班iPhone / 办公Mac",maxlength:"40","show-word-limit":""},null,8,["modelValue"])]),_:1}),s(c,null,{default:l(()=>[s(x,{type:"primary",loading:S.value,onClick:z},{default:l(()=>[...a[10]||(a[10]=[C("添加Passkey设备",-1)])]),_:1},8,["loading"])]),_:1})]),_:1}),Q((V(),K("div",null,[k.value.length===0?(V(),I(F,{key:0,description:"暂无Passkey设备"})):(V(),I(q,{key:1,data:k.value,size:"small",style:{width:"100%"}},{default:l(()=>[s(P,{prop:"device_name",label:"设备备注","min-width":"160"}),s(P,{prop:"credential_id_preview",label:"凭据ID","min-width":"180"}),s(P,{prop:"last_used_at",label:"最近使用","min-width":"140"}),s(P,{prop:"created_at",label:"创建时间","min-width":"140"}),s(P,{label:"操作",width:"100",fixed:"right"},{default:l(({row:p})=>[s(x,{type:"danger",text:"",onClick:ye=>J(p)},{default:l(()=>[...a[11]||(a[11]=[C("删除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"]))])),[[G,w.value]])]),_:1})])}}},_e=X(fe,[["__scopeId","data-v-bb93be75"]]);export{_e as default};
|