+
@@ -710,6 +802,30 @@ onUnmounted(() => {
word-break: break-word;
}
+.module-extra-actions {
+ margin-top: 10px;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.request-dialog-summary {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px 16px;
+ font-size: 12px;
+ margin-bottom: 12px;
+}
+
+.request-dialog-block + .request-dialog-block {
+ margin-top: 14px;
+}
+
+.request-dialog-title {
+ font-size: 13px;
+ font-weight: 800;
+ margin-bottom: 8px;
+}
+
.hero-overview-grid {
display: none;
}
diff --git a/static/admin/.vite/manifest.json b/static/admin/.vite/manifest.json
index cf5afbf..c96b0f7 100644
--- a/static/admin/.vite/manifest.json
+++ b/static/admin/.vite/manifest.json
@@ -1,6 +1,6 @@
{
- "_MetricGrid-CPdC57ab.js": {
- "file": "assets/MetricGrid-CPdC57ab.js",
+ "_MetricGrid-DBgW22G4.js": {
+ "file": "assets/MetricGrid-DBgW22G4.js",
"name": "MetricGrid",
"imports": [
"index.html",
@@ -14,29 +14,29 @@
"file": "assets/MetricGrid-yP_dkP6X.css",
"src": "_MetricGrid-yP_dkP6X.css"
},
- "_email-DgztAWKJ.js": {
- "file": "assets/email-DgztAWKJ.js",
+ "_email-D8pS1Pbx.js": {
+ "file": "assets/email-D8pS1Pbx.js",
"name": "email",
"imports": [
"index.html"
]
},
- "_system-1p1K-j-4.js": {
- "file": "assets/system-1p1K-j-4.js",
+ "_system-DO-Wv0rM.js": {
+ "file": "assets/system-DO-Wv0rM.js",
"name": "system",
"imports": [
"index.html"
]
},
- "_tasks-Y3hOqi3L.js": {
- "file": "assets/tasks-Y3hOqi3L.js",
+ "_tasks-BrCJEcYb.js": {
+ "file": "assets/tasks-BrCJEcYb.js",
"name": "tasks",
"imports": [
"index.html"
]
},
- "_users-kt1Evatk.js": {
- "file": "assets/users-kt1Evatk.js",
+ "_users-Czwk10aS.js": {
+ "file": "assets/users-Czwk10aS.js",
"name": "users",
"imports": [
"index.html"
@@ -74,7 +74,7 @@
]
},
"index.html": {
- "file": "assets/index-bXG7gEdM.js",
+ "file": "assets/index-ZQnpYe51.js",
"name": "index",
"src": "index.html",
"isEntry": true,
@@ -100,7 +100,7 @@
]
},
"src/pages/AnnouncementsPage.vue": {
- "file": "assets/AnnouncementsPage-fRPrWBwG.js",
+ "file": "assets/AnnouncementsPage-BZKzU8qv.js",
"name": "AnnouncementsPage",
"src": "src/pages/AnnouncementsPage.vue",
"isDynamicEntry": true,
@@ -116,14 +116,14 @@
]
},
"src/pages/EmailPage.vue": {
- "file": "assets/EmailPage-CFpw2E0J.js",
+ "file": "assets/EmailPage-BW-iqQTt.js",
"name": "EmailPage",
"src": "src/pages/EmailPage.vue",
"isDynamicEntry": true,
"imports": [
- "_email-DgztAWKJ.js",
+ "_email-D8pS1Pbx.js",
"index.html",
- "_MetricGrid-CPdC57ab.js",
+ "_MetricGrid-DBgW22G4.js",
"_vendor-element-CJoVtPsD.js",
"_vendor-sLgkZK1v.js",
"_vendor-vue-CWkOjFoA.js",
@@ -134,13 +134,13 @@
]
},
"src/pages/FeedbacksPage.vue": {
- "file": "assets/FeedbacksPage-BXNEKJpt.js",
+ "file": "assets/FeedbacksPage-C-dXtI0j.js",
"name": "FeedbacksPage",
"src": "src/pages/FeedbacksPage.vue",
"isDynamicEntry": true,
"imports": [
"index.html",
- "_MetricGrid-CPdC57ab.js",
+ "_MetricGrid-DBgW22G4.js",
"_vendor-element-CJoVtPsD.js",
"_vendor-sLgkZK1v.js",
"_vendor-vue-CWkOjFoA.js",
@@ -151,13 +151,13 @@
]
},
"src/pages/LogsPage.vue": {
- "file": "assets/LogsPage-xSURzHTt.js",
+ "file": "assets/LogsPage-BzvZ_jRl.js",
"name": "LogsPage",
"src": "src/pages/LogsPage.vue",
"isDynamicEntry": true,
"imports": [
- "_users-kt1Evatk.js",
- "_tasks-Y3hOqi3L.js",
+ "_users-Czwk10aS.js",
+ "_tasks-BrCJEcYb.js",
"index.html",
"_vendor-element-CJoVtPsD.js",
"_vendor-sLgkZK1v.js",
@@ -169,33 +169,33 @@
]
},
"src/pages/ReportPage.vue": {
- "file": "assets/ReportPage-BITaX3hh.js",
+ "file": "assets/ReportPage-JOjMl_cz.js",
"name": "ReportPage",
"src": "src/pages/ReportPage.vue",
"isDynamicEntry": true,
"imports": [
"_vendor-element-CJoVtPsD.js",
"index.html",
- "_email-DgztAWKJ.js",
- "_tasks-Y3hOqi3L.js",
- "_system-1p1K-j-4.js",
- "_MetricGrid-CPdC57ab.js",
+ "_email-D8pS1Pbx.js",
+ "_tasks-BrCJEcYb.js",
+ "_system-DO-Wv0rM.js",
+ "_MetricGrid-DBgW22G4.js",
"_vendor-sLgkZK1v.js",
"_vendor-vue-CWkOjFoA.js",
"_vendor-axios-B9ygI19o.js"
],
"css": [
- "assets/ReportPage-21i0jfUn.css"
+ "assets/ReportPage-DlF2eaTa.css"
]
},
"src/pages/SecurityPage.vue": {
- "file": "assets/SecurityPage-CQuWzZ_p.js",
+ "file": "assets/SecurityPage-DlbTJ4QF.js",
"name": "SecurityPage",
"src": "src/pages/SecurityPage.vue",
"isDynamicEntry": true,
"imports": [
"index.html",
- "_MetricGrid-CPdC57ab.js",
+ "_MetricGrid-DBgW22G4.js",
"_vendor-element-CJoVtPsD.js",
"_vendor-sLgkZK1v.js",
"_vendor-vue-CWkOjFoA.js",
@@ -206,7 +206,7 @@
]
},
"src/pages/SettingsPage.vue": {
- "file": "assets/SettingsPage-Ct3Jh1K3.js",
+ "file": "assets/SettingsPage-cWvS8Esv.js",
"name": "SettingsPage",
"src": "src/pages/SettingsPage.vue",
"isDynamicEntry": true,
@@ -222,12 +222,12 @@
]
},
"src/pages/SystemPage.vue": {
- "file": "assets/SystemPage-DE9Z1xG-.js",
+ "file": "assets/SystemPage-BdmQOog3.js",
"name": "SystemPage",
"src": "src/pages/SystemPage.vue",
"isDynamicEntry": true,
"imports": [
- "_system-1p1K-j-4.js",
+ "_system-DO-Wv0rM.js",
"index.html",
"_vendor-element-CJoVtPsD.js",
"_vendor-sLgkZK1v.js",
@@ -239,12 +239,12 @@
]
},
"src/pages/UsersPage.vue": {
- "file": "assets/UsersPage-BqAhpZky.js",
+ "file": "assets/UsersPage-CZk3ZLBk.js",
"name": "UsersPage",
"src": "src/pages/UsersPage.vue",
"isDynamicEntry": true,
"imports": [
- "_users-kt1Evatk.js",
+ "_users-Czwk10aS.js",
"index.html",
"_vendor-element-CJoVtPsD.js",
"_vendor-sLgkZK1v.js",
diff --git a/static/admin/assets/AnnouncementsPage-fRPrWBwG.js b/static/admin/assets/AnnouncementsPage-BZKzU8qv.js
similarity index 99%
rename from static/admin/assets/AnnouncementsPage-fRPrWBwG.js
rename to static/admin/assets/AnnouncementsPage-BZKzU8qv.js
index 50afa2d..40194cc 100644
--- a/static/admin/assets/AnnouncementsPage-fRPrWBwG.js
+++ b/static/admin/assets/AnnouncementsPage-BZKzU8qv.js
@@ -1 +1 @@
-import{B as j,f as r,E as C}from"./vendor-element-CJoVtPsD.js";import{a as w,_ as q}from"./index-bXG7gEdM.js";import{r as v,v as G,aW as p,b7 as J,z as B,A as f,B as u,S as n,L as t,N as I,K as x,u as O,P as c,Q as $,M as X,as as D}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function Y(){const{data:o}=await w.get("/announcements");return o}async function Z(o){const{data:s}=await w.post("/announcements",o);return s}async function ee(o){const s=new FormData;s.append("file",o);const{data:_}=await w.post("/announcements/upload_image",s);return _}async function te(o){const{data:s}=await w.post(`/announcements/${o}/activate`);return s}async function ne(o){const{data:s}=await w.post(`/announcements/${o}/deactivate`);return s}async function ae(o){const{data:s}=await w.delete(`/announcements/${o}`);return s}const le={class:"page-stack"},se={class:"image-upload-row"},oe={key:1,class:"image-url"},ie={key:0,class:"image-preview"},re=["src"],ue={class:"actions"},ce={class:"table-wrap"},de=["title"],me={key:1,class:"app-muted"},fe={class:"actions"},pe={__name:"AnnouncementsPage",setup(o){const s=v(""),_=v(""),m=v(""),g=v(null),h=v(!1),A=v(!1),T=v([]);async function k(){A.value=!0;try{T.value=await Y()}catch{T.value=[]}finally{A.value=!1}}function z(){s.value="",_.value="",m.value="",g.value&&(g.value.value="")}function P(){g.value?.click()}function U(){m.value="",g.value&&(g.value.value="")}async function F(l){const e=l.target?.files?.[0];if(e){if(e.type&&!e.type.startsWith("image/")){r.error("请选择图片文件"),l.target.value="";return}h.value=!0;try{const d=await ee(e);if(!d?.success||!d?.url){r.error(d?.error||"上传失败");return}m.value=d.url,r.success("上传成功")}catch{}finally{h.value=!1,l.target.value=""}}}async function M(l){const e=s.value.trim(),d=_.value.trim(),b=m.value.trim();if(!e||!d){r.error("标题和内容不能为空");return}try{const i=await Z({title:e,content:d,image_url:b,is_active:!!l});if(!i?.success){r.error(i?.error||"保存失败");return}r.success("保存成功"),z(),await k()}catch{}}async function S(l){const e=D("div",{class:"announcement-view"},[l.content?D("div",{class:"announcement-view-text"},l.content):null,l.image_url?D("img",{class:"announcement-view-image",src:l.image_url,alt:"公告图片"}):null]);await C.alert(e,l.title||"公告",{confirmButtonText:"关闭",dangerouslyUseHTMLString:!1})}async function L(l){try{await C.confirm("确定启用该公告吗?启用后将自动停用其他公告。","启用公告",{confirmButtonText:"启用",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await te(l.id);if(!e?.success){r.error(e?.error||"启用失败");return}r.success("已启用"),await k()}catch{}}async function R(l){try{await C.confirm("确定停用该公告吗?","停用公告",{confirmButtonText:"停用",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await ne(l.id);if(!e?.success){r.error(e?.error||"停用失败");return}r.success("已停用"),await k()}catch{}}async function W(l){try{await C.confirm("确定删除该公告吗?删除后无法恢复。","删除公告",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const e=await ae(l.id);if(!e?.success){r.error(e?.error||"删除失败");return}r.success("已删除"),await k()}catch{}}return G(k),(l,e)=>{const d=p("el-input"),b=p("el-form-item"),i=p("el-button"),H=p("el-form"),N=p("el-card"),y=p("el-table-column"),E=p("el-tag"),K=p("el-table"),Q=J("loading");return f(),B("div",le,[e[17]||(e[17]=u("div",{class:"app-page-title"},[u("h2",null,"公告管理")],-1)),n(N,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:t(()=>[e[9]||(e[9]=u("h3",{class:"section-title"},"创建公告",-1)),n(H,{"label-width":"90px"},{default:t(()=>[n(b,{label:"公告标题"},{default:t(()=>[n(d,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=a=>s.value=a),placeholder:"请输入公告标题",maxlength:"100","show-word-limit":""},null,8,["modelValue"])]),_:1}),n(b,{label:"公告内容"},{default:t(()=>[n(d,{modelValue:_.value,"onUpdate:modelValue":e[1]||(e[1]=a=>_.value=a),type:"textarea",rows:5,placeholder:"请输入公告内容(将以弹窗形式展示)",maxlength:"2000","show-word-limit":""},null,8,["modelValue"])]),_:1}),n(b,{label:"公告图片"},{default:t(()=>[u("div",se,[n(i,{icon:O(j),loading:h.value,onClick:P},{default:t(()=>[...e[4]||(e[4]=[c("上传图片",-1)])]),_:1},8,["icon","loading"]),m.value?(f(),x(i,{key:0,onClick:U},{default:t(()=>[...e[5]||(e[5]=[c("移除",-1)])]),_:1})):I("",!0),m.value?(f(),B("span",oe,$(m.value),1)):I("",!0),u("input",{ref_key:"imageInputRef",ref:g,class:"image-input",type:"file",accept:"image/*",onChange:F},null,544)])]),_:1})]),_:1}),m.value?(f(),B("div",ie,[u("img",{src:m.value,alt:"公告图片预览"},null,8,re)])):I("",!0),u("div",ue,[n(i,{type:"primary",onClick:e[2]||(e[2]=a=>M(!0))},{default:t(()=>[...e[6]||(e[6]=[c("发布并启用",-1)])]),_:1}),n(i,{onClick:e[3]||(e[3]=a=>M(!1))},{default:t(()=>[...e[7]||(e[7]=[c("保存但不启用",-1)])]),_:1}),n(i,{onClick:z},{default:t(()=>[...e[8]||(e[8]=[c("清空",-1)])]),_:1})]),e[10]||(e[10]=u("div",{class:"help"}," 说明:启用公告后,用户登录进入系统将弹窗提示;用户可选择“当次关闭”或“永久关闭本次公告”。 ",-1))]),_:1}),n(N,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:t(()=>[e[16]||(e[16]=u("h3",{class:"section-title"},"公告列表",-1)),u("div",ce,[X((f(),x(K,{data:T.value,style:{width:"100%"}},{default:t(()=>[n(y,{prop:"id",label:"ID",width:"80"}),n(y,{label:"标题","min-width":"240"},{default:t(({row:a})=>[u("span",{class:"ellipsis",title:a.title},$(a.title),9,de)]),_:1}),n(y,{label:"状态",width:"120"},{default:t(({row:a})=>[n(E,{type:a.is_active?"success":"info",effect:"light"},{default:t(()=>[c($(a.is_active?"启用":"停用"),1)]),_:2},1032,["type"])]),_:1}),n(y,{label:"图片",width:"100"},{default:t(({row:a})=>[a.image_url?(f(),x(E,{key:0,type:"success",effect:"light"},{default:t(()=>[...e[11]||(e[11]=[c("有图",-1)])]),_:1})):(f(),B("span",me,"-"))]),_:1}),n(y,{prop:"created_at",label:"创建时间",width:"180"}),n(y,{label:"操作",width:"260",fixed:"right"},{default:t(({row:a})=>[u("div",fe,[n(i,{size:"small",onClick:V=>S(a)},{default:t(()=>[...e[12]||(e[12]=[c("查看",-1)])]),_:1},8,["onClick"]),a.is_active?(f(),x(i,{key:0,size:"small",onClick:V=>R(a)},{default:t(()=>[...e[13]||(e[13]=[c("停用",-1)])]),_:1},8,["onClick"])):(f(),x(i,{key:1,type:"success",size:"small",onClick:V=>L(a)},{default:t(()=>[...e[14]||(e[14]=[c("启用",-1)])]),_:1},8,["onClick"])),n(i,{type:"danger",size:"small",onClick:V=>W(a)},{default:t(()=>[...e[15]||(e[15]=[c("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[Q,A.value]])])]),_:1})])}}},ke=q(pe,[["__scopeId","data-v-6f55521c"]]);export{ke as default};
+import{B as j,f as r,E as C}from"./vendor-element-CJoVtPsD.js";import{a as w,_ as q}from"./index-ZQnpYe51.js";import{r as v,v as G,aW as p,b7 as J,z as B,A as f,B as u,S as n,L as t,N as I,K as x,u as O,P as c,Q as $,M as X,as as D}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function Y(){const{data:o}=await w.get("/announcements");return o}async function Z(o){const{data:s}=await w.post("/announcements",o);return s}async function ee(o){const s=new FormData;s.append("file",o);const{data:_}=await w.post("/announcements/upload_image",s);return _}async function te(o){const{data:s}=await w.post(`/announcements/${o}/activate`);return s}async function ne(o){const{data:s}=await w.post(`/announcements/${o}/deactivate`);return s}async function ae(o){const{data:s}=await w.delete(`/announcements/${o}`);return s}const le={class:"page-stack"},se={class:"image-upload-row"},oe={key:1,class:"image-url"},ie={key:0,class:"image-preview"},re=["src"],ue={class:"actions"},ce={class:"table-wrap"},de=["title"],me={key:1,class:"app-muted"},fe={class:"actions"},pe={__name:"AnnouncementsPage",setup(o){const s=v(""),_=v(""),m=v(""),g=v(null),h=v(!1),A=v(!1),T=v([]);async function k(){A.value=!0;try{T.value=await Y()}catch{T.value=[]}finally{A.value=!1}}function z(){s.value="",_.value="",m.value="",g.value&&(g.value.value="")}function P(){g.value?.click()}function U(){m.value="",g.value&&(g.value.value="")}async function F(l){const e=l.target?.files?.[0];if(e){if(e.type&&!e.type.startsWith("image/")){r.error("请选择图片文件"),l.target.value="";return}h.value=!0;try{const d=await ee(e);if(!d?.success||!d?.url){r.error(d?.error||"上传失败");return}m.value=d.url,r.success("上传成功")}catch{}finally{h.value=!1,l.target.value=""}}}async function M(l){const e=s.value.trim(),d=_.value.trim(),b=m.value.trim();if(!e||!d){r.error("标题和内容不能为空");return}try{const i=await Z({title:e,content:d,image_url:b,is_active:!!l});if(!i?.success){r.error(i?.error||"保存失败");return}r.success("保存成功"),z(),await k()}catch{}}async function S(l){const e=D("div",{class:"announcement-view"},[l.content?D("div",{class:"announcement-view-text"},l.content):null,l.image_url?D("img",{class:"announcement-view-image",src:l.image_url,alt:"公告图片"}):null]);await C.alert(e,l.title||"公告",{confirmButtonText:"关闭",dangerouslyUseHTMLString:!1})}async function L(l){try{await C.confirm("确定启用该公告吗?启用后将自动停用其他公告。","启用公告",{confirmButtonText:"启用",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await te(l.id);if(!e?.success){r.error(e?.error||"启用失败");return}r.success("已启用"),await k()}catch{}}async function R(l){try{await C.confirm("确定停用该公告吗?","停用公告",{confirmButtonText:"停用",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await ne(l.id);if(!e?.success){r.error(e?.error||"停用失败");return}r.success("已停用"),await k()}catch{}}async function W(l){try{await C.confirm("确定删除该公告吗?删除后无法恢复。","删除公告",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const e=await ae(l.id);if(!e?.success){r.error(e?.error||"删除失败");return}r.success("已删除"),await k()}catch{}}return G(k),(l,e)=>{const d=p("el-input"),b=p("el-form-item"),i=p("el-button"),H=p("el-form"),N=p("el-card"),y=p("el-table-column"),E=p("el-tag"),K=p("el-table"),Q=J("loading");return f(),B("div",le,[e[17]||(e[17]=u("div",{class:"app-page-title"},[u("h2",null,"公告管理")],-1)),n(N,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:t(()=>[e[9]||(e[9]=u("h3",{class:"section-title"},"创建公告",-1)),n(H,{"label-width":"90px"},{default:t(()=>[n(b,{label:"公告标题"},{default:t(()=>[n(d,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=a=>s.value=a),placeholder:"请输入公告标题",maxlength:"100","show-word-limit":""},null,8,["modelValue"])]),_:1}),n(b,{label:"公告内容"},{default:t(()=>[n(d,{modelValue:_.value,"onUpdate:modelValue":e[1]||(e[1]=a=>_.value=a),type:"textarea",rows:5,placeholder:"请输入公告内容(将以弹窗形式展示)",maxlength:"2000","show-word-limit":""},null,8,["modelValue"])]),_:1}),n(b,{label:"公告图片"},{default:t(()=>[u("div",se,[n(i,{icon:O(j),loading:h.value,onClick:P},{default:t(()=>[...e[4]||(e[4]=[c("上传图片",-1)])]),_:1},8,["icon","loading"]),m.value?(f(),x(i,{key:0,onClick:U},{default:t(()=>[...e[5]||(e[5]=[c("移除",-1)])]),_:1})):I("",!0),m.value?(f(),B("span",oe,$(m.value),1)):I("",!0),u("input",{ref_key:"imageInputRef",ref:g,class:"image-input",type:"file",accept:"image/*",onChange:F},null,544)])]),_:1})]),_:1}),m.value?(f(),B("div",ie,[u("img",{src:m.value,alt:"公告图片预览"},null,8,re)])):I("",!0),u("div",ue,[n(i,{type:"primary",onClick:e[2]||(e[2]=a=>M(!0))},{default:t(()=>[...e[6]||(e[6]=[c("发布并启用",-1)])]),_:1}),n(i,{onClick:e[3]||(e[3]=a=>M(!1))},{default:t(()=>[...e[7]||(e[7]=[c("保存但不启用",-1)])]),_:1}),n(i,{onClick:z},{default:t(()=>[...e[8]||(e[8]=[c("清空",-1)])]),_:1})]),e[10]||(e[10]=u("div",{class:"help"}," 说明:启用公告后,用户登录进入系统将弹窗提示;用户可选择“当次关闭”或“永久关闭本次公告”。 ",-1))]),_:1}),n(N,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:t(()=>[e[16]||(e[16]=u("h3",{class:"section-title"},"公告列表",-1)),u("div",ce,[X((f(),x(K,{data:T.value,style:{width:"100%"}},{default:t(()=>[n(y,{prop:"id",label:"ID",width:"80"}),n(y,{label:"标题","min-width":"240"},{default:t(({row:a})=>[u("span",{class:"ellipsis",title:a.title},$(a.title),9,de)]),_:1}),n(y,{label:"状态",width:"120"},{default:t(({row:a})=>[n(E,{type:a.is_active?"success":"info",effect:"light"},{default:t(()=>[c($(a.is_active?"启用":"停用"),1)]),_:2},1032,["type"])]),_:1}),n(y,{label:"图片",width:"100"},{default:t(({row:a})=>[a.image_url?(f(),x(E,{key:0,type:"success",effect:"light"},{default:t(()=>[...e[11]||(e[11]=[c("有图",-1)])]),_:1})):(f(),B("span",me,"-"))]),_:1}),n(y,{prop:"created_at",label:"创建时间",width:"180"}),n(y,{label:"操作",width:"260",fixed:"right"},{default:t(({row:a})=>[u("div",fe,[n(i,{size:"small",onClick:V=>S(a)},{default:t(()=>[...e[12]||(e[12]=[c("查看",-1)])]),_:1},8,["onClick"]),a.is_active?(f(),x(i,{key:0,size:"small",onClick:V=>R(a)},{default:t(()=>[...e[13]||(e[13]=[c("停用",-1)])]),_:1},8,["onClick"])):(f(),x(i,{key:1,type:"success",size:"small",onClick:V=>L(a)},{default:t(()=>[...e[14]||(e[14]=[c("启用",-1)])]),_:1},8,["onClick"])),n(i,{type:"danger",size:"small",onClick:V=>W(a)},{default:t(()=>[...e[15]||(e[15]=[c("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[Q,A.value]])])]),_:1})])}}},ke=q(pe,[["__scopeId","data-v-6f55521c"]]);export{ke as default};
diff --git a/static/admin/assets/EmailPage-CFpw2E0J.js b/static/admin/assets/EmailPage-BW-iqQTt.js
similarity index 99%
rename from static/admin/assets/EmailPage-CFpw2E0J.js
rename to static/admin/assets/EmailPage-BW-iqQTt.js
index 4c6e4ec..469740c 100644
--- a/static/admin/assets/EmailPage-CFpw2E0J.js
+++ b/static/admin/assets/EmailPage-BW-iqQTt.js
@@ -1 +1 @@
-import{a as Ne,c as De,b as He,f as ze,u as Qe}from"./email-DgztAWKJ.js";import{a as U,_ as Fe}from"./index-bXG7gEdM.js";import{M as me}from"./MetricGrid-CPdC57ab.js";import{E as B,f as d}from"./vendor-element-CJoVtPsD.js";import{r as m,a2 as pe,a1 as Ie,c as j,v as Ke,aW as c,b7 as je,z as x,A as f,B as u,M as G,S as t,K as M,L as n,P as p,Q as _,N as P,R as te,ak as ce}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function Ge(){const{data:b}=await U.get("/smtp/configs");return b}async function Oe(b){const{data:v}=await U.post("/smtp/configs",b);return v}async function Re(b,v){const{data:g}=await U.put(`/smtp/configs/${b}`,v);return g}async function We(b){const{data:v}=await U.delete(`/smtp/configs/${b}`);return v}async function Je(b,v){const{data:g}=await U.post(`/smtp/configs/${b}/test`,{email:v});return g}async function Xe(b){const{data:v}=await U.post(`/smtp/configs/${b}/primary`);return v}async function Ye(){const{data:b}=await U.post("/smtp/configs/primary/clear");return b}const Ze={class:"page-stack"},el={class:"help app-muted"},ll={class:"section-head"},tl={class:"table-wrap"},al={class:"sub-stats"},sl={class:"help app-muted"},nl={class:"section-head"},ol={class:"toolbar"},il={class:"table-wrap"},ul=["title"],rl=["title"],dl=["title"],ml={class:"pagination"},pl={class:"page-hint app-muted"},cl={style:{width:"100%"}},fl={key:0,class:"help"},_l={key:0},bl={key:0},vl={key:0},yl={class:"dialog-actions"},fe=15,gl={__name:"EmailPage",setup(b){const v=m(!1),g=m(!1),o=pe({enabled:!1,failover_enabled:!0,register_verify_enabled:!1,login_alert_enabled:!0,task_notify_enabled:!1,base_url:"",updated_at:null});let L=null;async function ae(){v.value=!0;try{const s=await He();o.enabled=!!s.enabled,o.failover_enabled=!!s.failover_enabled,o.register_verify_enabled=!!s.register_verify_enabled,o.login_alert_enabled=s.login_alert_enabled===void 0?!0:!!s.login_alert_enabled,o.task_notify_enabled=!!s.task_notify_enabled,o.base_url=s.base_url||"",o.updated_at=s.updated_at||null}catch{}finally{v.value=!1}}async function _e(){if(!v.value){g.value=!0;try{const s=await Qe({enabled:o.enabled,failover_enabled:o.failover_enabled,register_verify_enabled:o.register_verify_enabled,login_alert_enabled:o.login_alert_enabled,task_notify_enabled:o.task_notify_enabled,base_url:(o.base_url||"").trim()});if(!s?.success){d.error(s?.error||"更新失败");return}d.success("邮件设置已更新"),await ae()}catch{}finally{g.value=!1}}}function E(){L&&window.clearTimeout(L),L=window.setTimeout(_e,300)}Ie(()=>{L&&window.clearTimeout(L),L=null});const O=m(!1),R=m([]),S=m(!1),k=m(!1),W=m(!1),D=m(!1),a=pe({id:null,name:"默认配置",enabled:!0,host:"",port:465,username:"",password:"",use_ssl:!0,use_tls:!1,sender_name:"自动化学习",sender_email:"",daily_limit:0,priority:0}),H=[{key:"custom",label:"自定义(手动填写)",defaults:null,note:"适用于其他邮箱/自建SMTP",links:[]},{key:"gmail",label:"Gmail",defaults:{host:"smtp.gmail.com",port:465,use_ssl:!0,use_tls:!1},note:"通常需要开启两步验证并创建应用专用密码(App Password)",links:[{label:"SMTP 设置说明",url:"https://support.google.com/mail/answer/7126229?hl=zh-Hans"},{label:"App Password",url:"https://myaccount.google.com/apppasswords"}]},{key:"qq",label:"QQ 邮箱",defaults:{host:"smtp.qq.com",port:465,use_ssl:!0,use_tls:!1},note:"需要在邮箱设置中开启 SMTP 并获取授权码(不是QQ登录密码)",links:[{label:"QQ邮箱 SMTP 帮助",url:"https://service.mail.qq.com/cgi-bin/help?subtype=1&id=28&no=1001256"}]},{key:"163",label:"163 邮箱",defaults:{host:"smtp.163.com",port:465,use_ssl:!0,use_tls:!1},note:"需要在邮箱设置中开启 SMTP 并使用授权码/客户端授权密码",links:[{label:"网易邮箱 SMTP 帮助",url:"https://help.mail.163.com/faqDetail.do?code=d7a5dc8471a22b76"}]},{key:"126",label:"126 邮箱",defaults:{host:"smtp.126.com",port:465,use_ssl:!0,use_tls:!1},note:"需要在邮箱设置中开启 SMTP 并使用授权码/客户端授权密码",links:[{label:"网易邮箱帮助",url:"https://help.mail.163.com/"}]},{key:"outlook",label:"Outlook/Hotmail",defaults:{host:"smtp-mail.outlook.com",port:587,use_ssl:!1,use_tls:!0},note:"建议使用 TLS 587(部分账号需开启 SMTP AUTH)",links:[{label:"微软 SMTP 设置",url:"https://support.microsoft.com/office/pop-imap-and-smtp-settings-for-outlook-com-d088b0b7-0d38-4f9a-bc5d-509f9e4c6d3d"}]},{key:"office365",label:"Microsoft 365/Exchange",defaults:{host:"smtp.office365.com",port:587,use_ssl:!1,use_tls:!0},note:"企业邮箱常用配置(需启用 SMTP AUTH)",links:[{label:"微软官方说明",url:"https://learn.microsoft.com/exchange/clients-and-mobile-in-exchange-online/authenticated-client-smtp-submission"}]},{key:"icloud",label:"iCloud",defaults:{host:"smtp.mail.me.com",port:587,use_ssl:!1,use_tls:!0},note:"需要在 Apple ID 中生成“App 专用密码”",links:[{label:"Apple 邮件服务器设置",url:"https://support.apple.com/zh-cn/HT202304"}]},{key:"tencent_exmail",label:"腾讯企业邮箱",defaults:{host:"smtp.exmail.qq.com",port:465,use_ssl:!0,use_tls:!1},note:"企业邮箱常用配置",links:[{label:"腾讯企业邮箱帮助",url:"https://service.exmail.qq.com/cgi-bin/help?subtype=1&id=23&no=1001068"}]},{key:"aliyun_exmail",label:"阿里企业邮箱",defaults:{host:"smtp.mxhichina.com",port:465,use_ssl:!0,use_tls:!1},note:"企业邮箱常用配置",links:[{label:"阿里云文档",url:"https://help.aliyun.com/document_detail/50652.html"}]}],$=m("custom"),V=j(()=>H.find(s=>s.key===$.value)||H[0]),be=j(()=>k.value&&W.value?"留空保持不变":"SMTP密码或授权码");function ve(s){const e=String(s?.host||"").trim().toLowerCase();return e&&{"smtp.gmail.com":"gmail","smtp.qq.com":"qq","smtp.163.com":"163","smtp.126.com":"126","smtp-mail.outlook.com":"outlook","smtp.office365.com":"office365","smtp.mail.me.com":"icloud","smtp.exmail.qq.com":"tencent_exmail","smtp.mxhichina.com":"aliyun_exmail"}[e]||"custom"}function ye(s){const e=H.find(i=>i.key===s);!e||!e.defaults||(a.host=e.defaults.host,a.port=e.defaults.port,a.use_ssl=e.defaults.use_ssl,a.use_tls=e.defaults.use_tls)}function se(){a.id=null,a.name="默认配置",a.enabled=!0,a.host="",a.port=465,a.username="",a.password="",a.use_ssl=!0,a.use_tls=!1,a.sender_name="自动化学习",a.sender_email="",a.daily_limit=0,a.priority=0,W.value=!1,D.value=!1,$.value="custom"}async function q(){O.value=!0;try{R.value=await Ge()}catch{R.value=[]}finally{O.value=!1}}function ge(){k.value=!1,se(),$.value="custom",S.value=!0}function ke(s){k.value=!0,se(),a.id=s.id,a.name=s.name||"默认配置",a.enabled=!!s.enabled,a.host=s.host||"",a.port=s.port||465,a.username=s.username||"",a.password="",a.use_ssl=!!s.use_ssl,a.use_tls=!!s.use_tls,a.sender_name=s.sender_name||"自动化学习",a.sender_email=s.sender_email||"",a.daily_limit=s.daily_limit??0,a.priority=s.priority??0,W.value=!!s.has_password,D.value=!!s.is_primary,$.value=ve(s),S.value=!0}function ne(s){return s.is_primary?{label:"主",type:"warning"}:s.enabled?{label:"备用",type:"success"}:{label:"禁用",type:"info"}}function he(s){return s.daily_limit&&s.daily_limit>0?`${s.daily_sent}/${s.daily_limit}`:`${s.daily_sent}/∞`}async function Ve(){if(!a.host.trim()){d.error("SMTP服务器地址不能为空");return}if(!a.username.trim()){d.error("SMTP用户名不能为空");return}const s={name:a.name.trim()||"默认配置",enabled:!!a.enabled,priority:Number(a.priority)||0,host:a.host.trim(),port:Number(a.port)||465,username:a.username.trim(),use_ssl:!!a.use_ssl,use_tls:!!a.use_tls,sender_name:(a.sender_name||"").trim(),sender_email:(a.sender_email||"").trim(),daily_limit:Number(a.daily_limit)||0};try{if(k.value){const e={...s};a.password&&(e.password=a.password);const i=await Re(a.id,e);if(!i?.success){d.error(i?.error||"更新失败");return}d.success("保存成功")}else{const e={...s};a.password&&(e.password=a.password);const i=await Oe(e);if(!i?.success){d.error(i?.error||"创建失败");return}d.success("创建成功")}S.value=!1,await q()}catch{}}async function we(){if(!k.value||!a.id){d.error("请先保存配置后再测试");return}let s;try{const e=await B.prompt("请输入测试收件邮箱","测试连接",{inputPlaceholder:"name@example.com",confirmButtonText:"发送测试邮件",cancelButtonText:"取消",inputValidator:i=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(i||"").trim()),inputErrorMessage:"邮箱格式不正确"});s=String(e.value||"").trim()}catch{return}try{const e=await Je(a.id,s);e?.success?(d.success("测试成功,邮件已发送"),await q()):await B.alert(e?.error||"测试失败","测试失败",{confirmButtonText:"知道了"})}catch{}}async function Se(){if(!(!k.value||!a.id)){try{await B.confirm("确定将该配置设为主配置吗?","设为主配置",{confirmButtonText:"设为主配置",cancelButtonText:"取消",type:"warning"})}catch{return}try{const s=await Xe(a.id);if(!s?.success){d.error(s?.error||"设置失败");return}d.success("已设为主配置"),S.value=!1,await q()}catch{}}}async function Te(){if(k.value){try{await B.confirm("确定取消主配置吗?取消后将按优先级选择可用SMTP。","取消主配置",{confirmButtonText:"取消主配置",cancelButtonText:"保留",type:"warning"})}catch{return}try{const s=await Ye();if(!s?.success){d.error(s?.error||"操作失败");return}d.success("已取消主配置"),S.value=!1,await q()}catch{}}}async function xe(){if(!(!k.value||!a.id)){try{await B.confirm("确定删除该SMTP配置吗?此操作不可恢复。","删除配置",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const s=await We(a.id);if(!s?.success){d.error(s?.error||"删除失败");return}d.success("已删除"),S.value=!1,await q()}catch{}}}const A=m(!1),h=m({}),J=m(!1),z=m(""),Q=m(""),F=m(1),X=m([]),Y=m(0),Z=m(1);function Pe(s){return{register:"注册验证",reset:"密码重置",bind:"邮箱绑定",task_complete:"任务完成",security_alert:"安全告警"}[s]||s}function oe(s){return s?.username&&s?.user_id?`${s.username} (#${s.user_id})`:s?.user_id?`用户#${s.user_id}`:"系统"}const Ce=j(()=>[{key:"total_sent",label:"总发送",value:h.value?.total_sent||0,tone:"blue"},{key:"total_success",label:"成功",value:h.value?.total_success||0,tone:"green"},{key:"total_failed",label:"失败",value:h.value?.total_failed||0,tone:"red"},{key:"success_rate",label:"成功率",value:`${h.value?.success_rate||0}%`,tone:"purple"}]),Be=j(()=>[{key:"register_sent",label:"注册验证",value:h.value?.register_sent||0,tone:"cyan"},{key:"reset_sent",label:"密码重置",value:h.value?.reset_sent||0,tone:"orange"},{key:"bind_sent",label:"邮箱绑定",value:h.value?.bind_sent||0,tone:"purple"},{key:"task_complete_sent",label:"任务完成",value:h.value?.task_complete_sent||0,tone:"green"}]);async function Me(){A.value=!0;try{h.value=await ze()}catch{h.value={}}finally{A.value=!1}}async function N(s=1){J.value=!0;try{const e={page:s,page_size:fe};z.value&&(e.type=z.value),Q.value&&(e.status=Q.value);const i=await Ne(e);X.value=i?.logs||[],Y.value=i?.total||0,F.value=i?.page||s,Z.value=i?.total_pages||1}catch{X.value=[],Y.value=0,Z.value=1}finally{J.value=!1}}async function Ue(){let s;try{const e=await B.prompt("请输入保留天数(将删除该天数之前的日志)","清理日志",{inputValue:"30",confirmButtonText:"清理",cancelButtonText:"取消",inputValidator:i=>{const r=parseInt(String(i),10);return Number.isFinite(r)&&r>=7},inputErrorMessage:"天数必须大于等于7"});s=parseInt(String(e.value),10)}catch{return}try{await B.confirm(`确定删除 ${s} 天之前的邮件日志吗?`,"二次确认",{confirmButtonText:"删除",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await De(s);if(!e?.success){d.error(e?.error||"清理失败");return}d.success(`已清理 ${e.deleted} 条日志`),await N(1)}catch{}}async function Le(){await Promise.all([ae(),q(),Me(),N(1)])}return Ke(Le),(s,e)=>{const i=c("el-switch"),r=c("el-form-item"),Ee=c("el-divider"),C=c("el-input"),ie=c("el-form"),I=c("el-card"),T=c("el-button"),ue=c("el-tag"),y=c("el-table-column"),re=c("el-table"),w=c("el-option"),ee=c("el-select"),$e=c("el-pagination"),qe=c("el-link"),le=c("el-input-number"),Ae=c("el-dialog"),K=je("loading");return f(),x("div",Ze,[e[43]||(e[43]=u("div",{class:"app-page-title"},[u("h2",null,"邮件配置")],-1)),G((f(),M(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:n(()=>[e[29]||(e[29]=u("h3",{class:"section-title"},"全局设置",-1)),t(ie,{"label-width":"140px"},{default:n(()=>[t(r,{label:"启用邮件功能"},{default:n(()=>[t(i,{modelValue:o.enabled,"onUpdate:modelValue":e[0]||(e[0]=l=>o.enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"])]),_:1}),t(r,{label:"启用故障转移"},{default:n(()=>[t(i,{modelValue:o.failover_enabled,"onUpdate:modelValue":e[1]||(e[1]=l=>o.failover_enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"])]),_:1}),t(r,{label:"启用注册邮箱验证"},{default:n(()=>[t(i,{modelValue:o.register_verify_enabled,"onUpdate:modelValue":e[2]||(e[2]=l=>o.register_verify_enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"])]),_:1}),t(Ee,{"content-position":"left"},{default:n(()=>[...e[26]||(e[26]=[p("通知设置",-1)])]),_:1}),t(r,{label:"启用任务完成通知"},{default:n(()=>[t(i,{modelValue:o.task_notify_enabled,"onUpdate:modelValue":e[3]||(e[3]=l=>o.task_notify_enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"])]),_:1}),t(r,{label:"新设备登录提醒"},{default:n(()=>[t(i,{modelValue:o.login_alert_enabled,"onUpdate:modelValue":e[4]||(e[4]=l=>o.login_alert_enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"]),e[27]||(e[27]=u("div",{class:"help"},"当检测到新设备或新IP登录时,发送邮件提醒用户",-1))]),_:1}),t(r,{label:"网站基础URL"},{default:n(()=>[t(C,{modelValue:o.base_url,"onUpdate:modelValue":e[5]||(e[5]=l=>o.base_url=l),placeholder:"例如: https://example.com",disabled:g.value,onBlur:E},null,8,["modelValue","disabled"]),e[28]||(e[28]=u("div",{class:"help"},"用于生成邮件中的验证链接,留空则使用默认配置。",-1))]),_:1})]),_:1}),u("div",el,"最近更新时间:"+_(o.updated_at||"-"),1)]),_:1})),[[K,v.value]]),t(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:n(()=>[u("div",ll,[e[31]||(e[31]=u("h3",{class:"section-title"},"SMTP配置列表",-1)),t(T,{type:"primary",onClick:ge},{default:n(()=>[...e[30]||(e[30]=[p("+ 添加配置",-1)])]),_:1})]),u("div",tl,[G((f(),M(re,{data:R.value,style:{width:"100%"}},{default:n(()=>[t(y,{label:"状态",width:"90"},{default:n(({row:l})=>[t(ue,{type:ne(l).type,effect:"light"},{default:n(()=>[p(_(ne(l).label),1)]),_:2},1032,["type"])]),_:1}),t(y,{prop:"name",label:"名称","min-width":"160"}),t(y,{label:"服务器","min-width":"200"},{default:n(({row:l})=>[p(_(l.host)+":"+_(l.port),1)]),_:1}),t(y,{label:"今日/限额",width:"110"},{default:n(({row:l})=>[p(_(he(l)),1)]),_:1}),t(y,{label:"成功率",width:"100"},{default:n(({row:l})=>[p(_(l.success_rate)+"%",1)]),_:1}),t(y,{label:"操作",width:"120",fixed:"right"},{default:n(({row:l})=>[t(T,{size:"small",onClick:de=>ke(l)},{default:n(()=>[...e[32]||(e[32]=[p("编辑",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[K,O.value]])])]),_:1}),G((f(),M(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:n(()=>[e[33]||(e[33]=u("h3",{class:"section-title"},"邮件发送统计",-1)),t(me,{items:Ce.value,loading:A.value,"min-width":160},null,8,["items","loading"]),u("div",al,[t(me,{items:Be.value,loading:A.value,"min-width":150},null,8,["items","loading"])]),u("div",sl,"最后更新:"+_(h.value.last_updated||"-"),1)]),_:1})),[[K,A.value]]),t(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:n(()=>[u("div",nl,[e[35]||(e[35]=u("h3",{class:"section-title"},"邮件发送日志",-1)),u("div",ol,[t(ee,{modelValue:z.value,"onUpdate:modelValue":e[6]||(e[6]=l=>z.value=l),style:{width:"140px"},onChange:e[7]||(e[7]=l=>N(1))},{default:n(()=>[t(w,{label:"全部类型",value:""}),t(w,{label:"注册验证",value:"register"}),t(w,{label:"密码重置",value:"reset"}),t(w,{label:"邮箱绑定",value:"bind"}),t(w,{label:"任务完成",value:"task_complete"}),t(w,{label:"安全告警",value:"security_alert"})]),_:1},8,["modelValue"]),t(ee,{modelValue:Q.value,"onUpdate:modelValue":e[8]||(e[8]=l=>Q.value=l),style:{width:"120px"},onChange:e[9]||(e[9]=l=>N(1))},{default:n(()=>[t(w,{label:"全部状态",value:""}),t(w,{label:"成功",value:"success"}),t(w,{label:"失败",value:"failed"})]),_:1},8,["modelValue"]),t(T,{type:"danger",plain:"",onClick:Ue},{default:n(()=>[...e[34]||(e[34]=[p("清理日志",-1)])]),_:1})])]),u("div",il,[G((f(),M(re,{data:X.value,style:{width:"100%"}},{default:n(()=>[t(y,{prop:"created_at",label:"时间",width:"180"}),t(y,{prop:"email_to",label:"收件人","min-width":"180"}),t(y,{label:"来源用户","min-width":"160"},{default:n(({row:l})=>[u("span",{class:"ellipsis",title:oe(l)},_(oe(l)),9,ul)]),_:1}),t(y,{label:"类型",width:"120"},{default:n(({row:l})=>[p(_(Pe(l.email_type)),1)]),_:1}),t(y,{label:"主题","min-width":"220"},{default:n(({row:l})=>[u("span",{class:"ellipsis",title:l.subject},_(l.subject),9,rl)]),_:1}),t(y,{label:"状态",width:"90"},{default:n(({row:l})=>[t(ue,{type:l.status==="success"?"success":"danger",effect:"light"},{default:n(()=>[p(_(l.status==="success"?"成功":"失败"),1)]),_:2},1032,["type"])]),_:1}),t(y,{label:"错误","min-width":"200"},{default:n(({row:l})=>[u("span",{class:"ellipsis",title:l.error_message||""},_(l.error_message||"-"),9,dl)]),_:1})]),_:1},8,["data"])),[[K,J.value]])]),u("div",ml,[t($e,{"current-page":F.value,"onUpdate:currentPage":e[10]||(e[10]=l=>F.value=l),"page-size":fe,total:Y.value,layout:"prev, pager, next, ->, total",onCurrentChange:N},null,8,["current-page","total"]),u("div",pl,"第 "+_(F.value)+" / "+_(Z.value)+" 页",1)])]),_:1}),t(Ae,{modelValue:S.value,"onUpdate:modelValue":e[25]||(e[25]=l=>S.value=l),title:k.value?"编辑SMTP配置":"添加SMTP配置",width:"min(560px, 92vw)"},{footer:n(()=>[u("div",yl,[t(T,{onClick:we},{default:n(()=>[...e[36]||(e[36]=[p("测试连接",-1)])]),_:1}),k.value&&D.value?(f(),M(T,{key:0,type:"warning",plain:"",onClick:Te},{default:n(()=>[...e[37]||(e[37]=[p("取消主配置",-1)])]),_:1})):P("",!0),k.value&&!D.value?(f(),M(T,{key:1,onClick:Se},{default:n(()=>[...e[38]||(e[38]=[p("设为主配置",-1)])]),_:1})):P("",!0),k.value?(f(),M(T,{key:2,type:"danger",plain:"",onClick:xe},{default:n(()=>[...e[39]||(e[39]=[p("删除配置",-1)])]),_:1})):P("",!0),e[42]||(e[42]=u("div",{class:"spacer"},null,-1)),t(T,{onClick:e[24]||(e[24]=l=>S.value=!1)},{default:n(()=>[...e[40]||(e[40]=[p("取消",-1)])]),_:1}),t(T,{type:"primary",onClick:Ve},{default:n(()=>[...e[41]||(e[41]=[p("保存",-1)])]),_:1})])]),default:n(()=>[t(ie,{"label-width":"120px"},{default:n(()=>[t(r,{label:"名称"},{default:n(()=>[t(C,{modelValue:a.name,"onUpdate:modelValue":e[11]||(e[11]=l=>a.name=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"启用"},{default:n(()=>[t(i,{modelValue:a.enabled,"onUpdate:modelValue":e[12]||(e[12]=l=>a.enabled=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"邮箱模板"},{default:n(()=>[u("div",cl,[t(ee,{modelValue:$.value,"onUpdate:modelValue":e[13]||(e[13]=l=>$.value=l),placeholder:"选择常用邮箱模板",style:{width:"100%"},onChange:ye},{default:n(()=>[(f(),x(te,null,ce(H,l=>t(w,{key:l.key,label:l.label,value:l.key},null,8,["label","value"])),64))]),_:1},8,["modelValue"]),V.value.note||V.value.links&&V.value.links.length?(f(),x("div",fl,[V.value.note?(f(),x("span",_l,_(V.value.note),1)):P("",!0),V.value.links&&V.value.links.length?(f(),x(te,{key:1},[V.value.note?(f(),x("span",bl," · ")):P("",!0),(f(!0),x(te,null,ce(V.value.links,(l,de)=>(f(),x("span",{key:l.url},[t(qe,{href:l.url,target:"_blank",type:"primary",underline:!1},{default:n(()=>[p(_(l.label),1)]),_:2},1032,["href"]),de[t(C,{modelValue:a.host,"onUpdate:modelValue":e[14]||(e[14]=l=>a.host=l),placeholder:"smtp.example.com"},null,8,["modelValue"])]),_:1}),t(r,{label:"端口"},{default:n(()=>[t(le,{modelValue:a.port,"onUpdate:modelValue":e[15]||(e[15]=l=>a.port=l),min:1,max:65535},null,8,["modelValue"])]),_:1}),t(r,{label:"用户名"},{default:n(()=>[t(C,{modelValue:a.username,"onUpdate:modelValue":e[16]||(e[16]=l=>a.username=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"密码"},{default:n(()=>[t(C,{modelValue:a.password,"onUpdate:modelValue":e[17]||(e[17]=l=>a.password=l),type:"password","show-password":"",placeholder:be.value},null,8,["modelValue","placeholder"])]),_:1}),t(r,{label:"SSL"},{default:n(()=>[t(i,{modelValue:a.use_ssl,"onUpdate:modelValue":e[18]||(e[18]=l=>a.use_ssl=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"TLS"},{default:n(()=>[t(i,{modelValue:a.use_tls,"onUpdate:modelValue":e[19]||(e[19]=l=>a.use_tls=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"发件人名称"},{default:n(()=>[t(C,{modelValue:a.sender_name,"onUpdate:modelValue":e[20]||(e[20]=l=>a.sender_name=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"发件人邮箱"},{default:n(()=>[t(C,{modelValue:a.sender_email,"onUpdate:modelValue":e[21]||(e[21]=l=>a.sender_email=l),placeholder:"可选"},null,8,["modelValue"])]),_:1}),t(r,{label:"每日限额"},{default:n(()=>[t(le,{modelValue:a.daily_limit,"onUpdate:modelValue":e[22]||(e[22]=l=>a.daily_limit=l),min:0,max:1e6},null,8,["modelValue"])]),_:1}),t(r,{label:"优先级"},{default:n(()=>[t(le,{modelValue:a.priority,"onUpdate:modelValue":e[23]||(e[23]=l=>a.priority=l),min:0,max:1e3},null,8,["modelValue"])]),_:1})]),_:1})]),_:1},8,["modelValue","title"])])}}},Pl=Fe(gl,[["__scopeId","data-v-4f511165"]]);export{Pl as default};
+import{a as Ne,c as De,b as He,f as ze,u as Qe}from"./email-D8pS1Pbx.js";import{a as U,_ as Fe}from"./index-ZQnpYe51.js";import{M as me}from"./MetricGrid-DBgW22G4.js";import{E as B,f as d}from"./vendor-element-CJoVtPsD.js";import{r as m,a2 as pe,a1 as Ie,c as j,v as Ke,aW as c,b7 as je,z as x,A as f,B as u,M as G,S as t,K as M,L as n,P as p,Q as _,N as P,R as te,ak as ce}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function Ge(){const{data:b}=await U.get("/smtp/configs");return b}async function Oe(b){const{data:v}=await U.post("/smtp/configs",b);return v}async function Re(b,v){const{data:g}=await U.put(`/smtp/configs/${b}`,v);return g}async function We(b){const{data:v}=await U.delete(`/smtp/configs/${b}`);return v}async function Je(b,v){const{data:g}=await U.post(`/smtp/configs/${b}/test`,{email:v});return g}async function Xe(b){const{data:v}=await U.post(`/smtp/configs/${b}/primary`);return v}async function Ye(){const{data:b}=await U.post("/smtp/configs/primary/clear");return b}const Ze={class:"page-stack"},el={class:"help app-muted"},ll={class:"section-head"},tl={class:"table-wrap"},al={class:"sub-stats"},sl={class:"help app-muted"},nl={class:"section-head"},ol={class:"toolbar"},il={class:"table-wrap"},ul=["title"],rl=["title"],dl=["title"],ml={class:"pagination"},pl={class:"page-hint app-muted"},cl={style:{width:"100%"}},fl={key:0,class:"help"},_l={key:0},bl={key:0},vl={key:0},yl={class:"dialog-actions"},fe=15,gl={__name:"EmailPage",setup(b){const v=m(!1),g=m(!1),o=pe({enabled:!1,failover_enabled:!0,register_verify_enabled:!1,login_alert_enabled:!0,task_notify_enabled:!1,base_url:"",updated_at:null});let L=null;async function ae(){v.value=!0;try{const s=await He();o.enabled=!!s.enabled,o.failover_enabled=!!s.failover_enabled,o.register_verify_enabled=!!s.register_verify_enabled,o.login_alert_enabled=s.login_alert_enabled===void 0?!0:!!s.login_alert_enabled,o.task_notify_enabled=!!s.task_notify_enabled,o.base_url=s.base_url||"",o.updated_at=s.updated_at||null}catch{}finally{v.value=!1}}async function _e(){if(!v.value){g.value=!0;try{const s=await Qe({enabled:o.enabled,failover_enabled:o.failover_enabled,register_verify_enabled:o.register_verify_enabled,login_alert_enabled:o.login_alert_enabled,task_notify_enabled:o.task_notify_enabled,base_url:(o.base_url||"").trim()});if(!s?.success){d.error(s?.error||"更新失败");return}d.success("邮件设置已更新"),await ae()}catch{}finally{g.value=!1}}}function E(){L&&window.clearTimeout(L),L=window.setTimeout(_e,300)}Ie(()=>{L&&window.clearTimeout(L),L=null});const O=m(!1),R=m([]),S=m(!1),k=m(!1),W=m(!1),D=m(!1),a=pe({id:null,name:"默认配置",enabled:!0,host:"",port:465,username:"",password:"",use_ssl:!0,use_tls:!1,sender_name:"自动化学习",sender_email:"",daily_limit:0,priority:0}),H=[{key:"custom",label:"自定义(手动填写)",defaults:null,note:"适用于其他邮箱/自建SMTP",links:[]},{key:"gmail",label:"Gmail",defaults:{host:"smtp.gmail.com",port:465,use_ssl:!0,use_tls:!1},note:"通常需要开启两步验证并创建应用专用密码(App Password)",links:[{label:"SMTP 设置说明",url:"https://support.google.com/mail/answer/7126229?hl=zh-Hans"},{label:"App Password",url:"https://myaccount.google.com/apppasswords"}]},{key:"qq",label:"QQ 邮箱",defaults:{host:"smtp.qq.com",port:465,use_ssl:!0,use_tls:!1},note:"需要在邮箱设置中开启 SMTP 并获取授权码(不是QQ登录密码)",links:[{label:"QQ邮箱 SMTP 帮助",url:"https://service.mail.qq.com/cgi-bin/help?subtype=1&id=28&no=1001256"}]},{key:"163",label:"163 邮箱",defaults:{host:"smtp.163.com",port:465,use_ssl:!0,use_tls:!1},note:"需要在邮箱设置中开启 SMTP 并使用授权码/客户端授权密码",links:[{label:"网易邮箱 SMTP 帮助",url:"https://help.mail.163.com/faqDetail.do?code=d7a5dc8471a22b76"}]},{key:"126",label:"126 邮箱",defaults:{host:"smtp.126.com",port:465,use_ssl:!0,use_tls:!1},note:"需要在邮箱设置中开启 SMTP 并使用授权码/客户端授权密码",links:[{label:"网易邮箱帮助",url:"https://help.mail.163.com/"}]},{key:"outlook",label:"Outlook/Hotmail",defaults:{host:"smtp-mail.outlook.com",port:587,use_ssl:!1,use_tls:!0},note:"建议使用 TLS 587(部分账号需开启 SMTP AUTH)",links:[{label:"微软 SMTP 设置",url:"https://support.microsoft.com/office/pop-imap-and-smtp-settings-for-outlook-com-d088b0b7-0d38-4f9a-bc5d-509f9e4c6d3d"}]},{key:"office365",label:"Microsoft 365/Exchange",defaults:{host:"smtp.office365.com",port:587,use_ssl:!1,use_tls:!0},note:"企业邮箱常用配置(需启用 SMTP AUTH)",links:[{label:"微软官方说明",url:"https://learn.microsoft.com/exchange/clients-and-mobile-in-exchange-online/authenticated-client-smtp-submission"}]},{key:"icloud",label:"iCloud",defaults:{host:"smtp.mail.me.com",port:587,use_ssl:!1,use_tls:!0},note:"需要在 Apple ID 中生成“App 专用密码”",links:[{label:"Apple 邮件服务器设置",url:"https://support.apple.com/zh-cn/HT202304"}]},{key:"tencent_exmail",label:"腾讯企业邮箱",defaults:{host:"smtp.exmail.qq.com",port:465,use_ssl:!0,use_tls:!1},note:"企业邮箱常用配置",links:[{label:"腾讯企业邮箱帮助",url:"https://service.exmail.qq.com/cgi-bin/help?subtype=1&id=23&no=1001068"}]},{key:"aliyun_exmail",label:"阿里企业邮箱",defaults:{host:"smtp.mxhichina.com",port:465,use_ssl:!0,use_tls:!1},note:"企业邮箱常用配置",links:[{label:"阿里云文档",url:"https://help.aliyun.com/document_detail/50652.html"}]}],$=m("custom"),V=j(()=>H.find(s=>s.key===$.value)||H[0]),be=j(()=>k.value&&W.value?"留空保持不变":"SMTP密码或授权码");function ve(s){const e=String(s?.host||"").trim().toLowerCase();return e&&{"smtp.gmail.com":"gmail","smtp.qq.com":"qq","smtp.163.com":"163","smtp.126.com":"126","smtp-mail.outlook.com":"outlook","smtp.office365.com":"office365","smtp.mail.me.com":"icloud","smtp.exmail.qq.com":"tencent_exmail","smtp.mxhichina.com":"aliyun_exmail"}[e]||"custom"}function ye(s){const e=H.find(i=>i.key===s);!e||!e.defaults||(a.host=e.defaults.host,a.port=e.defaults.port,a.use_ssl=e.defaults.use_ssl,a.use_tls=e.defaults.use_tls)}function se(){a.id=null,a.name="默认配置",a.enabled=!0,a.host="",a.port=465,a.username="",a.password="",a.use_ssl=!0,a.use_tls=!1,a.sender_name="自动化学习",a.sender_email="",a.daily_limit=0,a.priority=0,W.value=!1,D.value=!1,$.value="custom"}async function q(){O.value=!0;try{R.value=await Ge()}catch{R.value=[]}finally{O.value=!1}}function ge(){k.value=!1,se(),$.value="custom",S.value=!0}function ke(s){k.value=!0,se(),a.id=s.id,a.name=s.name||"默认配置",a.enabled=!!s.enabled,a.host=s.host||"",a.port=s.port||465,a.username=s.username||"",a.password="",a.use_ssl=!!s.use_ssl,a.use_tls=!!s.use_tls,a.sender_name=s.sender_name||"自动化学习",a.sender_email=s.sender_email||"",a.daily_limit=s.daily_limit??0,a.priority=s.priority??0,W.value=!!s.has_password,D.value=!!s.is_primary,$.value=ve(s),S.value=!0}function ne(s){return s.is_primary?{label:"主",type:"warning"}:s.enabled?{label:"备用",type:"success"}:{label:"禁用",type:"info"}}function he(s){return s.daily_limit&&s.daily_limit>0?`${s.daily_sent}/${s.daily_limit}`:`${s.daily_sent}/∞`}async function Ve(){if(!a.host.trim()){d.error("SMTP服务器地址不能为空");return}if(!a.username.trim()){d.error("SMTP用户名不能为空");return}const s={name:a.name.trim()||"默认配置",enabled:!!a.enabled,priority:Number(a.priority)||0,host:a.host.trim(),port:Number(a.port)||465,username:a.username.trim(),use_ssl:!!a.use_ssl,use_tls:!!a.use_tls,sender_name:(a.sender_name||"").trim(),sender_email:(a.sender_email||"").trim(),daily_limit:Number(a.daily_limit)||0};try{if(k.value){const e={...s};a.password&&(e.password=a.password);const i=await Re(a.id,e);if(!i?.success){d.error(i?.error||"更新失败");return}d.success("保存成功")}else{const e={...s};a.password&&(e.password=a.password);const i=await Oe(e);if(!i?.success){d.error(i?.error||"创建失败");return}d.success("创建成功")}S.value=!1,await q()}catch{}}async function we(){if(!k.value||!a.id){d.error("请先保存配置后再测试");return}let s;try{const e=await B.prompt("请输入测试收件邮箱","测试连接",{inputPlaceholder:"name@example.com",confirmButtonText:"发送测试邮件",cancelButtonText:"取消",inputValidator:i=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(i||"").trim()),inputErrorMessage:"邮箱格式不正确"});s=String(e.value||"").trim()}catch{return}try{const e=await Je(a.id,s);e?.success?(d.success("测试成功,邮件已发送"),await q()):await B.alert(e?.error||"测试失败","测试失败",{confirmButtonText:"知道了"})}catch{}}async function Se(){if(!(!k.value||!a.id)){try{await B.confirm("确定将该配置设为主配置吗?","设为主配置",{confirmButtonText:"设为主配置",cancelButtonText:"取消",type:"warning"})}catch{return}try{const s=await Xe(a.id);if(!s?.success){d.error(s?.error||"设置失败");return}d.success("已设为主配置"),S.value=!1,await q()}catch{}}}async function Te(){if(k.value){try{await B.confirm("确定取消主配置吗?取消后将按优先级选择可用SMTP。","取消主配置",{confirmButtonText:"取消主配置",cancelButtonText:"保留",type:"warning"})}catch{return}try{const s=await Ye();if(!s?.success){d.error(s?.error||"操作失败");return}d.success("已取消主配置"),S.value=!1,await q()}catch{}}}async function xe(){if(!(!k.value||!a.id)){try{await B.confirm("确定删除该SMTP配置吗?此操作不可恢复。","删除配置",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const s=await We(a.id);if(!s?.success){d.error(s?.error||"删除失败");return}d.success("已删除"),S.value=!1,await q()}catch{}}}const A=m(!1),h=m({}),J=m(!1),z=m(""),Q=m(""),F=m(1),X=m([]),Y=m(0),Z=m(1);function Pe(s){return{register:"注册验证",reset:"密码重置",bind:"邮箱绑定",task_complete:"任务完成",security_alert:"安全告警"}[s]||s}function oe(s){return s?.username&&s?.user_id?`${s.username} (#${s.user_id})`:s?.user_id?`用户#${s.user_id}`:"系统"}const Ce=j(()=>[{key:"total_sent",label:"总发送",value:h.value?.total_sent||0,tone:"blue"},{key:"total_success",label:"成功",value:h.value?.total_success||0,tone:"green"},{key:"total_failed",label:"失败",value:h.value?.total_failed||0,tone:"red"},{key:"success_rate",label:"成功率",value:`${h.value?.success_rate||0}%`,tone:"purple"}]),Be=j(()=>[{key:"register_sent",label:"注册验证",value:h.value?.register_sent||0,tone:"cyan"},{key:"reset_sent",label:"密码重置",value:h.value?.reset_sent||0,tone:"orange"},{key:"bind_sent",label:"邮箱绑定",value:h.value?.bind_sent||0,tone:"purple"},{key:"task_complete_sent",label:"任务完成",value:h.value?.task_complete_sent||0,tone:"green"}]);async function Me(){A.value=!0;try{h.value=await ze()}catch{h.value={}}finally{A.value=!1}}async function N(s=1){J.value=!0;try{const e={page:s,page_size:fe};z.value&&(e.type=z.value),Q.value&&(e.status=Q.value);const i=await Ne(e);X.value=i?.logs||[],Y.value=i?.total||0,F.value=i?.page||s,Z.value=i?.total_pages||1}catch{X.value=[],Y.value=0,Z.value=1}finally{J.value=!1}}async function Ue(){let s;try{const e=await B.prompt("请输入保留天数(将删除该天数之前的日志)","清理日志",{inputValue:"30",confirmButtonText:"清理",cancelButtonText:"取消",inputValidator:i=>{const r=parseInt(String(i),10);return Number.isFinite(r)&&r>=7},inputErrorMessage:"天数必须大于等于7"});s=parseInt(String(e.value),10)}catch{return}try{await B.confirm(`确定删除 ${s} 天之前的邮件日志吗?`,"二次确认",{confirmButtonText:"删除",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await De(s);if(!e?.success){d.error(e?.error||"清理失败");return}d.success(`已清理 ${e.deleted} 条日志`),await N(1)}catch{}}async function Le(){await Promise.all([ae(),q(),Me(),N(1)])}return Ke(Le),(s,e)=>{const i=c("el-switch"),r=c("el-form-item"),Ee=c("el-divider"),C=c("el-input"),ie=c("el-form"),I=c("el-card"),T=c("el-button"),ue=c("el-tag"),y=c("el-table-column"),re=c("el-table"),w=c("el-option"),ee=c("el-select"),$e=c("el-pagination"),qe=c("el-link"),le=c("el-input-number"),Ae=c("el-dialog"),K=je("loading");return f(),x("div",Ze,[e[43]||(e[43]=u("div",{class:"app-page-title"},[u("h2",null,"邮件配置")],-1)),G((f(),M(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:n(()=>[e[29]||(e[29]=u("h3",{class:"section-title"},"全局设置",-1)),t(ie,{"label-width":"140px"},{default:n(()=>[t(r,{label:"启用邮件功能"},{default:n(()=>[t(i,{modelValue:o.enabled,"onUpdate:modelValue":e[0]||(e[0]=l=>o.enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"])]),_:1}),t(r,{label:"启用故障转移"},{default:n(()=>[t(i,{modelValue:o.failover_enabled,"onUpdate:modelValue":e[1]||(e[1]=l=>o.failover_enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"])]),_:1}),t(r,{label:"启用注册邮箱验证"},{default:n(()=>[t(i,{modelValue:o.register_verify_enabled,"onUpdate:modelValue":e[2]||(e[2]=l=>o.register_verify_enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"])]),_:1}),t(Ee,{"content-position":"left"},{default:n(()=>[...e[26]||(e[26]=[p("通知设置",-1)])]),_:1}),t(r,{label:"启用任务完成通知"},{default:n(()=>[t(i,{modelValue:o.task_notify_enabled,"onUpdate:modelValue":e[3]||(e[3]=l=>o.task_notify_enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"])]),_:1}),t(r,{label:"新设备登录提醒"},{default:n(()=>[t(i,{modelValue:o.login_alert_enabled,"onUpdate:modelValue":e[4]||(e[4]=l=>o.login_alert_enabled=l),disabled:g.value,onChange:E},null,8,["modelValue","disabled"]),e[27]||(e[27]=u("div",{class:"help"},"当检测到新设备或新IP登录时,发送邮件提醒用户",-1))]),_:1}),t(r,{label:"网站基础URL"},{default:n(()=>[t(C,{modelValue:o.base_url,"onUpdate:modelValue":e[5]||(e[5]=l=>o.base_url=l),placeholder:"例如: https://example.com",disabled:g.value,onBlur:E},null,8,["modelValue","disabled"]),e[28]||(e[28]=u("div",{class:"help"},"用于生成邮件中的验证链接,留空则使用默认配置。",-1))]),_:1})]),_:1}),u("div",el,"最近更新时间:"+_(o.updated_at||"-"),1)]),_:1})),[[K,v.value]]),t(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:n(()=>[u("div",ll,[e[31]||(e[31]=u("h3",{class:"section-title"},"SMTP配置列表",-1)),t(T,{type:"primary",onClick:ge},{default:n(()=>[...e[30]||(e[30]=[p("+ 添加配置",-1)])]),_:1})]),u("div",tl,[G((f(),M(re,{data:R.value,style:{width:"100%"}},{default:n(()=>[t(y,{label:"状态",width:"90"},{default:n(({row:l})=>[t(ue,{type:ne(l).type,effect:"light"},{default:n(()=>[p(_(ne(l).label),1)]),_:2},1032,["type"])]),_:1}),t(y,{prop:"name",label:"名称","min-width":"160"}),t(y,{label:"服务器","min-width":"200"},{default:n(({row:l})=>[p(_(l.host)+":"+_(l.port),1)]),_:1}),t(y,{label:"今日/限额",width:"110"},{default:n(({row:l})=>[p(_(he(l)),1)]),_:1}),t(y,{label:"成功率",width:"100"},{default:n(({row:l})=>[p(_(l.success_rate)+"%",1)]),_:1}),t(y,{label:"操作",width:"120",fixed:"right"},{default:n(({row:l})=>[t(T,{size:"small",onClick:de=>ke(l)},{default:n(()=>[...e[32]||(e[32]=[p("编辑",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[K,O.value]])])]),_:1}),G((f(),M(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:n(()=>[e[33]||(e[33]=u("h3",{class:"section-title"},"邮件发送统计",-1)),t(me,{items:Ce.value,loading:A.value,"min-width":160},null,8,["items","loading"]),u("div",al,[t(me,{items:Be.value,loading:A.value,"min-width":150},null,8,["items","loading"])]),u("div",sl,"最后更新:"+_(h.value.last_updated||"-"),1)]),_:1})),[[K,A.value]]),t(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:n(()=>[u("div",nl,[e[35]||(e[35]=u("h3",{class:"section-title"},"邮件发送日志",-1)),u("div",ol,[t(ee,{modelValue:z.value,"onUpdate:modelValue":e[6]||(e[6]=l=>z.value=l),style:{width:"140px"},onChange:e[7]||(e[7]=l=>N(1))},{default:n(()=>[t(w,{label:"全部类型",value:""}),t(w,{label:"注册验证",value:"register"}),t(w,{label:"密码重置",value:"reset"}),t(w,{label:"邮箱绑定",value:"bind"}),t(w,{label:"任务完成",value:"task_complete"}),t(w,{label:"安全告警",value:"security_alert"})]),_:1},8,["modelValue"]),t(ee,{modelValue:Q.value,"onUpdate:modelValue":e[8]||(e[8]=l=>Q.value=l),style:{width:"120px"},onChange:e[9]||(e[9]=l=>N(1))},{default:n(()=>[t(w,{label:"全部状态",value:""}),t(w,{label:"成功",value:"success"}),t(w,{label:"失败",value:"failed"})]),_:1},8,["modelValue"]),t(T,{type:"danger",plain:"",onClick:Ue},{default:n(()=>[...e[34]||(e[34]=[p("清理日志",-1)])]),_:1})])]),u("div",il,[G((f(),M(re,{data:X.value,style:{width:"100%"}},{default:n(()=>[t(y,{prop:"created_at",label:"时间",width:"180"}),t(y,{prop:"email_to",label:"收件人","min-width":"180"}),t(y,{label:"来源用户","min-width":"160"},{default:n(({row:l})=>[u("span",{class:"ellipsis",title:oe(l)},_(oe(l)),9,ul)]),_:1}),t(y,{label:"类型",width:"120"},{default:n(({row:l})=>[p(_(Pe(l.email_type)),1)]),_:1}),t(y,{label:"主题","min-width":"220"},{default:n(({row:l})=>[u("span",{class:"ellipsis",title:l.subject},_(l.subject),9,rl)]),_:1}),t(y,{label:"状态",width:"90"},{default:n(({row:l})=>[t(ue,{type:l.status==="success"?"success":"danger",effect:"light"},{default:n(()=>[p(_(l.status==="success"?"成功":"失败"),1)]),_:2},1032,["type"])]),_:1}),t(y,{label:"错误","min-width":"200"},{default:n(({row:l})=>[u("span",{class:"ellipsis",title:l.error_message||""},_(l.error_message||"-"),9,dl)]),_:1})]),_:1},8,["data"])),[[K,J.value]])]),u("div",ml,[t($e,{"current-page":F.value,"onUpdate:currentPage":e[10]||(e[10]=l=>F.value=l),"page-size":fe,total:Y.value,layout:"prev, pager, next, ->, total",onCurrentChange:N},null,8,["current-page","total"]),u("div",pl,"第 "+_(F.value)+" / "+_(Z.value)+" 页",1)])]),_:1}),t(Ae,{modelValue:S.value,"onUpdate:modelValue":e[25]||(e[25]=l=>S.value=l),title:k.value?"编辑SMTP配置":"添加SMTP配置",width:"min(560px, 92vw)"},{footer:n(()=>[u("div",yl,[t(T,{onClick:we},{default:n(()=>[...e[36]||(e[36]=[p("测试连接",-1)])]),_:1}),k.value&&D.value?(f(),M(T,{key:0,type:"warning",plain:"",onClick:Te},{default:n(()=>[...e[37]||(e[37]=[p("取消主配置",-1)])]),_:1})):P("",!0),k.value&&!D.value?(f(),M(T,{key:1,onClick:Se},{default:n(()=>[...e[38]||(e[38]=[p("设为主配置",-1)])]),_:1})):P("",!0),k.value?(f(),M(T,{key:2,type:"danger",plain:"",onClick:xe},{default:n(()=>[...e[39]||(e[39]=[p("删除配置",-1)])]),_:1})):P("",!0),e[42]||(e[42]=u("div",{class:"spacer"},null,-1)),t(T,{onClick:e[24]||(e[24]=l=>S.value=!1)},{default:n(()=>[...e[40]||(e[40]=[p("取消",-1)])]),_:1}),t(T,{type:"primary",onClick:Ve},{default:n(()=>[...e[41]||(e[41]=[p("保存",-1)])]),_:1})])]),default:n(()=>[t(ie,{"label-width":"120px"},{default:n(()=>[t(r,{label:"名称"},{default:n(()=>[t(C,{modelValue:a.name,"onUpdate:modelValue":e[11]||(e[11]=l=>a.name=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"启用"},{default:n(()=>[t(i,{modelValue:a.enabled,"onUpdate:modelValue":e[12]||(e[12]=l=>a.enabled=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"邮箱模板"},{default:n(()=>[u("div",cl,[t(ee,{modelValue:$.value,"onUpdate:modelValue":e[13]||(e[13]=l=>$.value=l),placeholder:"选择常用邮箱模板",style:{width:"100%"},onChange:ye},{default:n(()=>[(f(),x(te,null,ce(H,l=>t(w,{key:l.key,label:l.label,value:l.key},null,8,["label","value"])),64))]),_:1},8,["modelValue"]),V.value.note||V.value.links&&V.value.links.length?(f(),x("div",fl,[V.value.note?(f(),x("span",_l,_(V.value.note),1)):P("",!0),V.value.links&&V.value.links.length?(f(),x(te,{key:1},[V.value.note?(f(),x("span",bl," · ")):P("",!0),(f(!0),x(te,null,ce(V.value.links,(l,de)=>(f(),x("span",{key:l.url},[t(qe,{href:l.url,target:"_blank",type:"primary",underline:!1},{default:n(()=>[p(_(l.label),1)]),_:2},1032,["href"]),de[t(C,{modelValue:a.host,"onUpdate:modelValue":e[14]||(e[14]=l=>a.host=l),placeholder:"smtp.example.com"},null,8,["modelValue"])]),_:1}),t(r,{label:"端口"},{default:n(()=>[t(le,{modelValue:a.port,"onUpdate:modelValue":e[15]||(e[15]=l=>a.port=l),min:1,max:65535},null,8,["modelValue"])]),_:1}),t(r,{label:"用户名"},{default:n(()=>[t(C,{modelValue:a.username,"onUpdate:modelValue":e[16]||(e[16]=l=>a.username=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"密码"},{default:n(()=>[t(C,{modelValue:a.password,"onUpdate:modelValue":e[17]||(e[17]=l=>a.password=l),type:"password","show-password":"",placeholder:be.value},null,8,["modelValue","placeholder"])]),_:1}),t(r,{label:"SSL"},{default:n(()=>[t(i,{modelValue:a.use_ssl,"onUpdate:modelValue":e[18]||(e[18]=l=>a.use_ssl=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"TLS"},{default:n(()=>[t(i,{modelValue:a.use_tls,"onUpdate:modelValue":e[19]||(e[19]=l=>a.use_tls=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"发件人名称"},{default:n(()=>[t(C,{modelValue:a.sender_name,"onUpdate:modelValue":e[20]||(e[20]=l=>a.sender_name=l)},null,8,["modelValue"])]),_:1}),t(r,{label:"发件人邮箱"},{default:n(()=>[t(C,{modelValue:a.sender_email,"onUpdate:modelValue":e[21]||(e[21]=l=>a.sender_email=l),placeholder:"可选"},null,8,["modelValue"])]),_:1}),t(r,{label:"每日限额"},{default:n(()=>[t(le,{modelValue:a.daily_limit,"onUpdate:modelValue":e[22]||(e[22]=l=>a.daily_limit=l),min:0,max:1e6},null,8,["modelValue"])]),_:1}),t(r,{label:"优先级"},{default:n(()=>[t(le,{modelValue:a.priority,"onUpdate:modelValue":e[23]||(e[23]=l=>a.priority=l),min:0,max:1e3},null,8,["modelValue"])]),_:1})]),_:1})]),_:1},8,["modelValue","title"])])}}},Pl=Fe(gl,[["__scopeId","data-v-4f511165"]]);export{Pl as default};
diff --git a/static/admin/assets/FeedbacksPage-BXNEKJpt.js b/static/admin/assets/FeedbacksPage-C-dXtI0j.js
similarity index 97%
rename from static/admin/assets/FeedbacksPage-BXNEKJpt.js
rename to static/admin/assets/FeedbacksPage-C-dXtI0j.js
index d0dbe30..97ef92e 100644
--- a/static/admin/assets/FeedbacksPage-BXNEKJpt.js
+++ b/static/admin/assets/FeedbacksPage-C-dXtI0j.js
@@ -1 +1 @@
-import{_ as $,b as L,r as R,c as j,d as A}from"./index-bXG7gEdM.js";import{M as G}from"./MetricGrid-CPdC57ab.js";import{E as k,f as w}from"./vendor-element-CJoVtPsD.js";import{i as K,r as f,c as O,v as Q,aW as i,b7 as U,z as x,A as v,B as s,S as t,L as l,R as F,ak as W,Q as d,M as q,K as H,P as p,N as J}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";const X={class:"page-stack"},Y={class:"app-page-title"},Z={class:"toolbar"},ee={class:"section-head"},te={class:"app-muted"},ae={class:"table-wrap"},le={class:"ellipsis"},ne={class:"ellipsis"},se={class:"ellipsis"},oe={class:"actions"},ie={__name:"FeedbacksPage",setup(ce){const M=K("refreshNavBadges",null),u=f(!1),b=f(""),c=f({total:0,pending:0,replied:0,closed:0}),_=f([]),T=[{label:"全部状态",value:""},{label:"待处理",value:"pending"},{label:"已回复",value:"replied"},{label:"已关闭",value:"closed"}],V=O(()=>[{key:"total",label:"总反馈",value:c.value.total||0,tone:"blue"},{key:"pending",label:"待处理",value:c.value.pending||0,tone:"orange"},{key:"replied",label:"已回复",value:c.value.replied||0,tone:"green"},{key:"closed",label:"已关闭",value:c.value.closed||0,tone:"purple"}]);function B(n){return n==="pending"?{label:"待处理",type:"warning"}:n==="replied"?{label:"已回复",type:"success"}:n==="closed"?{label:"已关闭",type:"info"}:{label:n||"-",type:"info"}}async function r(){u.value=!0;try{const n=await L(b.value);_.value=n?.feedbacks||[],c.value=n?.stats||{total:0,pending:0,replied:0,closed:0}}catch{_.value=[],c.value={total:0,pending:0,replied:0,closed:0}}finally{u.value=!1}await M?.({pendingFeedbacks:c.value.pending||0})}async function N(n){let a;try{a=(await k.prompt("请输入回复内容","回复反馈",{inputType:"textarea",inputPlaceholder:"回复内容",confirmButtonText:"提交",cancelButtonText:"取消",inputValidator:g=>!!String(g||"").trim(),inputErrorMessage:"回复内容不能为空"})).value}catch{return}try{const m=await R(n.id,String(a||"").trim());w.success(m?.message||"回复成功"),await r()}catch{}}async function D(n){try{await k.confirm("确定要关闭这个反馈吗?","关闭反馈",{confirmButtonText:"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}try{const a=await j(n.id);w.success(a?.message||"反馈已关闭"),await r()}catch{}}async function E(n){try{await k.confirm("确定要删除这个反馈吗?此操作不可恢复!","删除反馈",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const a=await A(n.id);w.success(a?.message||"反馈已删除"),await r()}catch{}}return Q(r),(n,a)=>{const m=i("el-option"),g=i("el-select"),o=i("el-table-column"),y=i("el-tooltip"),z=i("el-tag"),h=i("el-button"),P=i("el-table"),S=i("el-card"),I=U("loading");return v(),x("div",X,[s("div",Y,[a[1]||(a[1]=s("h2",null,"反馈管理",-1)),s("div",Z,[t(g,{modelValue:b.value,"onUpdate:modelValue":a[0]||(a[0]=e=>b.value=e),style:{width:"160px"},onChange:r},{default:l(()=>[(v(),x(F,null,W(T,e=>t(m,{key:e.value,label:e.label,value:e.value},null,8,["label","value"])),64))]),_:1},8,["modelValue"])])]),t(G,{items:V.value,loading:u.value,"min-width":165},null,8,["items","loading"]),t(S,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[s("div",ee,[a[2]||(a[2]=s("h3",{class:"section-title"},"反馈列表",-1)),s("div",te,"共 "+d(_.value.length)+" 条(当前筛选)",1)]),s("div",ae,[q((v(),H(P,{data:_.value,style:{width:"100%"}},{default:l(()=>[t(o,{prop:"id",label:"ID",width:"80"}),t(o,{prop:"username",label:"用户",width:"140"}),t(o,{label:"标题","min-width":"180"},{default:l(({row:e})=>[t(y,{content:e.title,placement:"top","show-after":300},{default:l(()=>[s("span",le,d(e.title),1)]),_:2},1032,["content"])]),_:1}),t(o,{label:"描述","min-width":"220"},{default:l(({row:e})=>[t(y,{content:e.description,placement:"top","show-after":300},{default:l(()=>[s("span",ne,d(e.description),1)]),_:2},1032,["content"])]),_:1}),t(o,{prop:"contact",label:"联系方式","min-width":"160"},{default:l(({row:e})=>[p(d(e.contact||"-"),1)]),_:1}),t(o,{label:"状态",width:"110"},{default:l(({row:e})=>[t(z,{type:B(e.status).type,effect:"light"},{default:l(()=>[p(d(B(e.status).label),1)]),_:2},1032,["type"])]),_:1}),t(o,{prop:"created_at",label:"提交时间",width:"180"}),t(o,{label:"回复","min-width":"180"},{default:l(({row:e})=>[t(y,{content:e.admin_reply||"",placement:"top","show-after":300},{default:l(()=>[s("span",se,d(e.admin_reply||"-"),1)]),_:2},1032,["content"])]),_:1}),t(o,{label:"操作",width:"220",fixed:"right"},{default:l(({row:e})=>[s("div",oe,[e.status!=="closed"?(v(),x(F,{key:0},[t(h,{type:"primary",size:"small",onClick:C=>N(e)},{default:l(()=>[...a[3]||(a[3]=[p("回复",-1)])]),_:1},8,["onClick"]),t(h,{size:"small",onClick:C=>D(e)},{default:l(()=>[...a[4]||(a[4]=[p("关闭",-1)])]),_:1},8,["onClick"])],64)):J("",!0),t(h,{type:"danger",size:"small",onClick:C=>E(e)},{default:l(()=>[...a[5]||(a[5]=[p("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[I,u.value]])])]),_:1})])}}},fe=$(ie,[["__scopeId","data-v-910fe89b"]]);export{fe as default};
+import{_ as $,b as L,r as R,c as j,d as A}from"./index-ZQnpYe51.js";import{M as G}from"./MetricGrid-DBgW22G4.js";import{E as k,f as w}from"./vendor-element-CJoVtPsD.js";import{i as K,r as f,c as O,v as Q,aW as i,b7 as U,z as x,A as v,B as s,S as t,L as l,R as F,ak as W,Q as d,M as q,K as H,P as p,N as J}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";const X={class:"page-stack"},Y={class:"app-page-title"},Z={class:"toolbar"},ee={class:"section-head"},te={class:"app-muted"},ae={class:"table-wrap"},le={class:"ellipsis"},ne={class:"ellipsis"},se={class:"ellipsis"},oe={class:"actions"},ie={__name:"FeedbacksPage",setup(ce){const M=K("refreshNavBadges",null),u=f(!1),b=f(""),c=f({total:0,pending:0,replied:0,closed:0}),_=f([]),T=[{label:"全部状态",value:""},{label:"待处理",value:"pending"},{label:"已回复",value:"replied"},{label:"已关闭",value:"closed"}],V=O(()=>[{key:"total",label:"总反馈",value:c.value.total||0,tone:"blue"},{key:"pending",label:"待处理",value:c.value.pending||0,tone:"orange"},{key:"replied",label:"已回复",value:c.value.replied||0,tone:"green"},{key:"closed",label:"已关闭",value:c.value.closed||0,tone:"purple"}]);function B(n){return n==="pending"?{label:"待处理",type:"warning"}:n==="replied"?{label:"已回复",type:"success"}:n==="closed"?{label:"已关闭",type:"info"}:{label:n||"-",type:"info"}}async function r(){u.value=!0;try{const n=await L(b.value);_.value=n?.feedbacks||[],c.value=n?.stats||{total:0,pending:0,replied:0,closed:0}}catch{_.value=[],c.value={total:0,pending:0,replied:0,closed:0}}finally{u.value=!1}await M?.({pendingFeedbacks:c.value.pending||0})}async function N(n){let a;try{a=(await k.prompt("请输入回复内容","回复反馈",{inputType:"textarea",inputPlaceholder:"回复内容",confirmButtonText:"提交",cancelButtonText:"取消",inputValidator:g=>!!String(g||"").trim(),inputErrorMessage:"回复内容不能为空"})).value}catch{return}try{const m=await R(n.id,String(a||"").trim());w.success(m?.message||"回复成功"),await r()}catch{}}async function D(n){try{await k.confirm("确定要关闭这个反馈吗?","关闭反馈",{confirmButtonText:"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}try{const a=await j(n.id);w.success(a?.message||"反馈已关闭"),await r()}catch{}}async function E(n){try{await k.confirm("确定要删除这个反馈吗?此操作不可恢复!","删除反馈",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const a=await A(n.id);w.success(a?.message||"反馈已删除"),await r()}catch{}}return Q(r),(n,a)=>{const m=i("el-option"),g=i("el-select"),o=i("el-table-column"),y=i("el-tooltip"),z=i("el-tag"),h=i("el-button"),P=i("el-table"),S=i("el-card"),I=U("loading");return v(),x("div",X,[s("div",Y,[a[1]||(a[1]=s("h2",null,"反馈管理",-1)),s("div",Z,[t(g,{modelValue:b.value,"onUpdate:modelValue":a[0]||(a[0]=e=>b.value=e),style:{width:"160px"},onChange:r},{default:l(()=>[(v(),x(F,null,W(T,e=>t(m,{key:e.value,label:e.label,value:e.value},null,8,["label","value"])),64))]),_:1},8,["modelValue"])])]),t(G,{items:V.value,loading:u.value,"min-width":165},null,8,["items","loading"]),t(S,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[s("div",ee,[a[2]||(a[2]=s("h3",{class:"section-title"},"反馈列表",-1)),s("div",te,"共 "+d(_.value.length)+" 条(当前筛选)",1)]),s("div",ae,[q((v(),H(P,{data:_.value,style:{width:"100%"}},{default:l(()=>[t(o,{prop:"id",label:"ID",width:"80"}),t(o,{prop:"username",label:"用户",width:"140"}),t(o,{label:"标题","min-width":"180"},{default:l(({row:e})=>[t(y,{content:e.title,placement:"top","show-after":300},{default:l(()=>[s("span",le,d(e.title),1)]),_:2},1032,["content"])]),_:1}),t(o,{label:"描述","min-width":"220"},{default:l(({row:e})=>[t(y,{content:e.description,placement:"top","show-after":300},{default:l(()=>[s("span",ne,d(e.description),1)]),_:2},1032,["content"])]),_:1}),t(o,{prop:"contact",label:"联系方式","min-width":"160"},{default:l(({row:e})=>[p(d(e.contact||"-"),1)]),_:1}),t(o,{label:"状态",width:"110"},{default:l(({row:e})=>[t(z,{type:B(e.status).type,effect:"light"},{default:l(()=>[p(d(B(e.status).label),1)]),_:2},1032,["type"])]),_:1}),t(o,{prop:"created_at",label:"提交时间",width:"180"}),t(o,{label:"回复","min-width":"180"},{default:l(({row:e})=>[t(y,{content:e.admin_reply||"",placement:"top","show-after":300},{default:l(()=>[s("span",se,d(e.admin_reply||"-"),1)]),_:2},1032,["content"])]),_:1}),t(o,{label:"操作",width:"220",fixed:"right"},{default:l(({row:e})=>[s("div",oe,[e.status!=="closed"?(v(),x(F,{key:0},[t(h,{type:"primary",size:"small",onClick:C=>N(e)},{default:l(()=>[...a[3]||(a[3]=[p("回复",-1)])]),_:1},8,["onClick"]),t(h,{size:"small",onClick:C=>D(e)},{default:l(()=>[...a[4]||(a[4]=[p("关闭",-1)])]),_:1},8,["onClick"])],64)):J("",!0),t(h,{type:"danger",size:"small",onClick:C=>E(e)},{default:l(()=>[...a[5]||(a[5]=[p("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[I,u.value]])])]),_:1})])}}},fe=$(ie,[["__scopeId","data-v-910fe89b"]]);export{fe as default};
diff --git a/static/admin/assets/LogsPage-xSURzHTt.js b/static/admin/assets/LogsPage-BzvZ_jRl.js
similarity index 98%
rename from static/admin/assets/LogsPage-xSURzHTt.js
rename to static/admin/assets/LogsPage-BzvZ_jRl.js
index 1ced38d..0299eb7 100644
--- a/static/admin/assets/LogsPage-xSURzHTt.js
+++ b/static/admin/assets/LogsPage-BzvZ_jRl.js
@@ -1 +1 @@
-import{f as q}from"./users-kt1Evatk.js";import{e as G,g as H}from"./tasks-Y3hOqi3L.js";import{_ as J}from"./index-bXG7gEdM.js";import{E as P,f as X}from"./vendor-element-CJoVtPsD.js";import{r as u,c as Z,v as ee,aW as r,b7 as te,z as E,A as b,B as f,S as t,L as l,R as ae,ak as le,K as B,P as m,M as oe,Q as d}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";function ne(p){return String(p||"").trim()}function se(p){return!p.startsWith("user_scheduled")||!p.includes(":")?"":p.split(":",2)[1]||""}function ue(p){const s=ne(p);if(!s||s==="manual")return{group:"manual",label:"手动",type:"success",tooltip:""};if(s==="scheduled")return{group:"scheduled",label:"定时任务",type:"primary",tooltip:"系统定时"};if(s.startsWith("user_scheduled")){const i=se(s),v=String(i||"").replace(/^batch_/,"");return{group:"scheduled",label:"定时任务",type:"primary",tooltip:v?`用户定时批次:${v}`:"用户定时"}}return{group:"manual",label:"手动",type:"success",tooltip:{batch:"手动批量",manual_screenshot:"手动截图",immediate:"立即执行",resumed:"断点恢复"}[s]||s}}const re={class:"page-stack"},ie={class:"filters"},ce={class:"table-wrap"},de={class:"ellipsis"},pe={class:"pagination"},me={class:"page-hint app-muted"},F=20,fe={__name:"LogsPage",setup(p){const s=u(!1),S=u([]),y=u(0),i=u(1),v=u(!1),T=u([]),h=u(""),w=u(""),V=u(""),x=u(""),k=u(""),$=Z(()=>Math.max(1,Math.ceil((y.value||0)/F)));function z(o){if(o==null)return"-";const e=Number(o);return Number.isFinite(e)?e<60?`${e}秒`:`${Math.floor(e/60)}分${e%60}秒`:"-"}function _(o){const e=ue(o);return{key:e.group,label:e.label,type:e.type,tooltip:e.tooltip}}function D(o){return o==="success"?{label:"成功",type:"success"}:o==="failed"?{label:"失败",type:"danger"}:{label:o||"-",type:"info"}}async function Y(){v.value=!0;try{const o=await q();T.value=(o||[]).map(e=>({id:e.id,username:e.username}))}catch{T.value=[]}finally{v.value=!1}}async function M(){s.value=!0;try{const o=(i.value-1)*F,e={limit:F,offset:o};h.value&&(e.date=h.value),w.value&&(e.status=w.value),V.value&&(e.source=V.value),x.value&&(e.user_id=x.value),k.value&&(e.account=k.value);const g=await G(e);S.value=g?.logs||[],y.value=g?.total||0}catch{S.value=[],y.value=0}finally{s.value=!1}}function O(){i.value=1,M()}function R(){h.value="",w.value="",V.value="",x.value="",k.value="",i.value=1,M()}async function W(){let o;try{const e=await P.prompt("请输入要清理多少天前的日志(默认30天)","清理旧日志",{inputValue:"30",confirmButtonText:"下一步",cancelButtonText:"取消",inputValidator:g=>{const n=parseInt(String(g),10);return Number.isFinite(n)&&n>=1},inputErrorMessage:"请输入有效的天数(大于0的整数)"});o=parseInt(String(e.value),10)}catch{return}try{await P.confirm(`确定要删除 ${o} 天前的所有日志吗?此操作不可恢复!`,"二次确认",{confirmButtonText:"删除",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await H(o);X.success(e?.message||"清理成功"),i.value=1,await M()}catch{}}return ee(async()=>{await Y(),await M()}),(o,e)=>{const g=r("el-date-picker"),n=r("el-option"),U=r("el-select"),A=r("el-input"),C=r("el-button"),I=r("el-card"),c=r("el-table-column"),L=r("el-tag"),N=r("el-tooltip"),j=r("el-table"),K=r("el-pagination"),Q=te("loading");return b(),E("div",re,[e[9]||(e[9]=f("div",{class:"app-page-title"},[f("h2",null,"任务日志")],-1)),t(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[f("div",ie,[t(g,{modelValue:h.value,"onUpdate:modelValue":e[0]||(e[0]=a=>h.value=a),type:"date","value-format":"YYYY-MM-DD",placeholder:"日期",style:{width:"150px"}},null,8,["modelValue"]),t(U,{modelValue:w.value,"onUpdate:modelValue":e[1]||(e[1]=a=>w.value=a),placeholder:"状态",style:{width:"120px"}},{default:l(()=>[t(n,{label:"全部",value:""}),t(n,{label:"成功",value:"success"}),t(n,{label:"失败",value:"failed"})]),_:1},8,["modelValue"]),t(U,{modelValue:V.value,"onUpdate:modelValue":e[2]||(e[2]=a=>V.value=a),placeholder:"来源",style:{width:"120px"}},{default:l(()=>[t(n,{label:"全部",value:""}),t(n,{label:"手动",value:"manual"}),t(n,{label:"定时任务(系统)",value:"scheduled"}),t(n,{label:"定时任务(用户)",value:"user_scheduled"}),t(n,{label:"手动(批量)",value:"batch"}),t(n,{label:"手动(截图)",value:"manual_screenshot"}),t(n,{label:"手动(立即)",value:"immediate"}),t(n,{label:"手动(恢复)",value:"resumed"})]),_:1},8,["modelValue"]),t(U,{modelValue:x.value,"onUpdate:modelValue":e[3]||(e[3]=a=>x.value=a),placeholder:"用户",style:{width:"140px"},loading:v.value,filterable:"",clearable:""},{default:l(()=>[t(n,{label:"全部",value:""}),(b(!0),E(ae,null,le(T.value,a=>(b(),B(n,{key:a.id,label:a.username,value:String(a.id)},null,8,["label","value"]))),128))]),_:1},8,["modelValue","loading"]),t(A,{modelValue:k.value,"onUpdate:modelValue":e[4]||(e[4]=a=>k.value=a),placeholder:"账号关键字",style:{width:"170px"},clearable:""},null,8,["modelValue"]),t(C,{type:"primary",onClick:O},{default:l(()=>[...e[6]||(e[6]=[m("筛选",-1)])]),_:1}),t(C,{onClick:R},{default:l(()=>[...e[7]||(e[7]=[m("重置",-1)])]),_:1}),t(C,{type:"danger",plain:"",onClick:W},{default:l(()=>[...e[8]||(e[8]=[m("清理旧日志",-1)])]),_:1})])]),_:1}),t(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[f("div",ce,[oe((b(),B(j,{data:S.value,style:{width:"100%"}},{default:l(()=>[t(c,{prop:"created_at",label:"时间",width:"180"}),t(c,{label:"来源",width:"110"},{default:l(({row:a})=>[_(a.source).tooltip?(b(),B(N,{key:0,content:_(a.source).tooltip,placement:"top","show-after":300},{default:l(()=>[t(L,{type:_(a.source).type,effect:"light"},{default:l(()=>[m(d(_(a.source).label),1)]),_:2},1032,["type"])]),_:2},1032,["content"])):(b(),B(L,{key:1,type:_(a.source).type,effect:"light"},{default:l(()=>[m(d(_(a.source).label),1)]),_:2},1032,["type"]))]),_:1}),t(c,{prop:"user_username",label:"用户",width:"140"}),t(c,{prop:"username",label:"账号",width:"160"}),t(c,{prop:"browse_type",label:"浏览类型",width:"120"}),t(c,{label:"状态",width:"90"},{default:l(({row:a})=>[t(L,{type:D(a.status).type,effect:"light"},{default:l(()=>[m(d(D(a.status).label),1)]),_:2},1032,["type"])]),_:1}),t(c,{label:"内容/附件",width:"110"},{default:l(({row:a})=>[m(d(a.total_items)+" / "+d(a.total_attachments),1)]),_:1}),t(c,{label:"用时",width:"90"},{default:l(({row:a})=>[m(d(z(a.duration)),1)]),_:1}),t(c,{label:"失败原因","min-width":"220"},{default:l(({row:a})=>[t(N,{content:a.error_message||"",placement:"top","show-after":300},{default:l(()=>[f("span",de,d(a.error_message||"-"),1)]),_:2},1032,["content"])]),_:1})]),_:1},8,["data"])),[[Q,s.value]])]),f("div",pe,[t(K,{"current-page":i.value,"onUpdate:currentPage":e[5]||(e[5]=a=>i.value=a),"page-size":F,total:y.value,layout:"prev, pager, next, jumper, ->, total",onCurrentChange:M},null,8,["current-page","total"]),f("div",me,"第 "+d(i.value)+" / "+d($.value)+" 页",1)])]),_:1})])}}},Ve=J(fe,[["__scopeId","data-v-8803eb08"]]);export{Ve as default};
+import{f as q}from"./users-Czwk10aS.js";import{e as G,g as H}from"./tasks-BrCJEcYb.js";import{_ as J}from"./index-ZQnpYe51.js";import{E as P,f as X}from"./vendor-element-CJoVtPsD.js";import{r as u,c as Z,v as ee,aW as r,b7 as te,z as E,A as b,B as f,S as t,L as l,R as ae,ak as le,K as B,P as m,M as oe,Q as d}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";function ne(p){return String(p||"").trim()}function se(p){return!p.startsWith("user_scheduled")||!p.includes(":")?"":p.split(":",2)[1]||""}function ue(p){const s=ne(p);if(!s||s==="manual")return{group:"manual",label:"手动",type:"success",tooltip:""};if(s==="scheduled")return{group:"scheduled",label:"定时任务",type:"primary",tooltip:"系统定时"};if(s.startsWith("user_scheduled")){const i=se(s),v=String(i||"").replace(/^batch_/,"");return{group:"scheduled",label:"定时任务",type:"primary",tooltip:v?`用户定时批次:${v}`:"用户定时"}}return{group:"manual",label:"手动",type:"success",tooltip:{batch:"手动批量",manual_screenshot:"手动截图",immediate:"立即执行",resumed:"断点恢复"}[s]||s}}const re={class:"page-stack"},ie={class:"filters"},ce={class:"table-wrap"},de={class:"ellipsis"},pe={class:"pagination"},me={class:"page-hint app-muted"},F=20,fe={__name:"LogsPage",setup(p){const s=u(!1),S=u([]),y=u(0),i=u(1),v=u(!1),T=u([]),h=u(""),w=u(""),V=u(""),x=u(""),k=u(""),$=Z(()=>Math.max(1,Math.ceil((y.value||0)/F)));function z(o){if(o==null)return"-";const e=Number(o);return Number.isFinite(e)?e<60?`${e}秒`:`${Math.floor(e/60)}分${e%60}秒`:"-"}function _(o){const e=ue(o);return{key:e.group,label:e.label,type:e.type,tooltip:e.tooltip}}function D(o){return o==="success"?{label:"成功",type:"success"}:o==="failed"?{label:"失败",type:"danger"}:{label:o||"-",type:"info"}}async function Y(){v.value=!0;try{const o=await q();T.value=(o||[]).map(e=>({id:e.id,username:e.username}))}catch{T.value=[]}finally{v.value=!1}}async function M(){s.value=!0;try{const o=(i.value-1)*F,e={limit:F,offset:o};h.value&&(e.date=h.value),w.value&&(e.status=w.value),V.value&&(e.source=V.value),x.value&&(e.user_id=x.value),k.value&&(e.account=k.value);const g=await G(e);S.value=g?.logs||[],y.value=g?.total||0}catch{S.value=[],y.value=0}finally{s.value=!1}}function O(){i.value=1,M()}function R(){h.value="",w.value="",V.value="",x.value="",k.value="",i.value=1,M()}async function W(){let o;try{const e=await P.prompt("请输入要清理多少天前的日志(默认30天)","清理旧日志",{inputValue:"30",confirmButtonText:"下一步",cancelButtonText:"取消",inputValidator:g=>{const n=parseInt(String(g),10);return Number.isFinite(n)&&n>=1},inputErrorMessage:"请输入有效的天数(大于0的整数)"});o=parseInt(String(e.value),10)}catch{return}try{await P.confirm(`确定要删除 ${o} 天前的所有日志吗?此操作不可恢复!`,"二次确认",{confirmButtonText:"删除",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await H(o);X.success(e?.message||"清理成功"),i.value=1,await M()}catch{}}return ee(async()=>{await Y(),await M()}),(o,e)=>{const g=r("el-date-picker"),n=r("el-option"),U=r("el-select"),A=r("el-input"),C=r("el-button"),I=r("el-card"),c=r("el-table-column"),L=r("el-tag"),N=r("el-tooltip"),j=r("el-table"),K=r("el-pagination"),Q=te("loading");return b(),E("div",re,[e[9]||(e[9]=f("div",{class:"app-page-title"},[f("h2",null,"任务日志")],-1)),t(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[f("div",ie,[t(g,{modelValue:h.value,"onUpdate:modelValue":e[0]||(e[0]=a=>h.value=a),type:"date","value-format":"YYYY-MM-DD",placeholder:"日期",style:{width:"150px"}},null,8,["modelValue"]),t(U,{modelValue:w.value,"onUpdate:modelValue":e[1]||(e[1]=a=>w.value=a),placeholder:"状态",style:{width:"120px"}},{default:l(()=>[t(n,{label:"全部",value:""}),t(n,{label:"成功",value:"success"}),t(n,{label:"失败",value:"failed"})]),_:1},8,["modelValue"]),t(U,{modelValue:V.value,"onUpdate:modelValue":e[2]||(e[2]=a=>V.value=a),placeholder:"来源",style:{width:"120px"}},{default:l(()=>[t(n,{label:"全部",value:""}),t(n,{label:"手动",value:"manual"}),t(n,{label:"定时任务(系统)",value:"scheduled"}),t(n,{label:"定时任务(用户)",value:"user_scheduled"}),t(n,{label:"手动(批量)",value:"batch"}),t(n,{label:"手动(截图)",value:"manual_screenshot"}),t(n,{label:"手动(立即)",value:"immediate"}),t(n,{label:"手动(恢复)",value:"resumed"})]),_:1},8,["modelValue"]),t(U,{modelValue:x.value,"onUpdate:modelValue":e[3]||(e[3]=a=>x.value=a),placeholder:"用户",style:{width:"140px"},loading:v.value,filterable:"",clearable:""},{default:l(()=>[t(n,{label:"全部",value:""}),(b(!0),E(ae,null,le(T.value,a=>(b(),B(n,{key:a.id,label:a.username,value:String(a.id)},null,8,["label","value"]))),128))]),_:1},8,["modelValue","loading"]),t(A,{modelValue:k.value,"onUpdate:modelValue":e[4]||(e[4]=a=>k.value=a),placeholder:"账号关键字",style:{width:"170px"},clearable:""},null,8,["modelValue"]),t(C,{type:"primary",onClick:O},{default:l(()=>[...e[6]||(e[6]=[m("筛选",-1)])]),_:1}),t(C,{onClick:R},{default:l(()=>[...e[7]||(e[7]=[m("重置",-1)])]),_:1}),t(C,{type:"danger",plain:"",onClick:W},{default:l(()=>[...e[8]||(e[8]=[m("清理旧日志",-1)])]),_:1})])]),_:1}),t(I,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[f("div",ce,[oe((b(),B(j,{data:S.value,style:{width:"100%"}},{default:l(()=>[t(c,{prop:"created_at",label:"时间",width:"180"}),t(c,{label:"来源",width:"110"},{default:l(({row:a})=>[_(a.source).tooltip?(b(),B(N,{key:0,content:_(a.source).tooltip,placement:"top","show-after":300},{default:l(()=>[t(L,{type:_(a.source).type,effect:"light"},{default:l(()=>[m(d(_(a.source).label),1)]),_:2},1032,["type"])]),_:2},1032,["content"])):(b(),B(L,{key:1,type:_(a.source).type,effect:"light"},{default:l(()=>[m(d(_(a.source).label),1)]),_:2},1032,["type"]))]),_:1}),t(c,{prop:"user_username",label:"用户",width:"140"}),t(c,{prop:"username",label:"账号",width:"160"}),t(c,{prop:"browse_type",label:"浏览类型",width:"120"}),t(c,{label:"状态",width:"90"},{default:l(({row:a})=>[t(L,{type:D(a.status).type,effect:"light"},{default:l(()=>[m(d(D(a.status).label),1)]),_:2},1032,["type"])]),_:1}),t(c,{label:"内容/附件",width:"110"},{default:l(({row:a})=>[m(d(a.total_items)+" / "+d(a.total_attachments),1)]),_:1}),t(c,{label:"用时",width:"90"},{default:l(({row:a})=>[m(d(z(a.duration)),1)]),_:1}),t(c,{label:"失败原因","min-width":"220"},{default:l(({row:a})=>[t(N,{content:a.error_message||"",placement:"top","show-after":300},{default:l(()=>[f("span",de,d(a.error_message||"-"),1)]),_:2},1032,["content"])]),_:1})]),_:1},8,["data"])),[[Q,s.value]])]),f("div",pe,[t(K,{"current-page":i.value,"onUpdate:currentPage":e[5]||(e[5]=a=>i.value=a),"page-size":F,total:y.value,layout:"prev, pager, next, jumper, ->, total",onCurrentChange:M},null,8,["current-page","total"]),f("div",me,"第 "+d(i.value)+" / "+d($.value)+" 页",1)])]),_:1})])}}},Ve=J(fe,[["__scopeId","data-v-8803eb08"]]);export{Ve as default};
diff --git a/static/admin/assets/MetricGrid-CPdC57ab.js b/static/admin/assets/MetricGrid-DBgW22G4.js
similarity index 94%
rename from static/admin/assets/MetricGrid-CPdC57ab.js
rename to static/admin/assets/MetricGrid-DBgW22G4.js
index 5c4ad76..c45efb6 100644
--- a/static/admin/assets/MetricGrid-CPdC57ab.js
+++ b/static/admin/assets/MetricGrid-DBgW22G4.js
@@ -1 +1 @@
-import{_}from"./index-bXG7gEdM.js";import{aW as c,z as s,A as t,R as r,ak as u,E as p,B as o,N as l,S as y,L as h,K as i,O as k,Q as n,P as v,D as f}from"./vendor-sLgkZK1v.js";const b={class:"metric-top"},x={key:0,class:"metric-icon"},B={class:"metric-label"},N={class:"metric-value"},g={key:0,class:"metric-hint app-muted"},C={__name:"MetricGrid",props:{items:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},minWidth:{type:Number,default:180}},setup(a){return(V,z)=>{const d=c("el-icon"),m=c("el-skeleton");return t(),s("div",{class:"metric-grid",style:f({"--metric-min":`${a.minWidth}px`})},[(t(!0),s(r,null,u(a.items,e=>(t(),s("div",{key:e?.key||e?.label,class:p(["metric-card",`metric-tone--${e?.tone||"blue"}`])},[o("div",b,[e?.icon?(t(),s("div",x,[y(d,null,{default:h(()=>[(t(),i(k(e.icon)))]),_:2},1024)])):l("",!0),o("div",B,n(e?.label||"-"),1)]),o("div",N,[a.loading?(t(),i(m,{key:0,rows:1,animated:""})):(t(),s(r,{key:1},[v(n(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",g,n(e?.hint||e?.sub),1)):l("",!0)],2))),128))],4)}}},S=_(C,[["__scopeId","data-v-00e217d4"]]);export{S as M};
+import{_}from"./index-ZQnpYe51.js";import{aW as c,z as s,A as t,R as r,ak as u,E as p,B as o,N as l,S as y,L as h,K as i,O as k,Q as n,P as v,D as f}from"./vendor-sLgkZK1v.js";const b={class:"metric-top"},x={key:0,class:"metric-icon"},B={class:"metric-label"},N={class:"metric-value"},g={key:0,class:"metric-hint app-muted"},C={__name:"MetricGrid",props:{items:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},minWidth:{type:Number,default:180}},setup(a){return(V,z)=>{const d=c("el-icon"),m=c("el-skeleton");return t(),s("div",{class:"metric-grid",style:f({"--metric-min":`${a.minWidth}px`})},[(t(!0),s(r,null,u(a.items,e=>(t(),s("div",{key:e?.key||e?.label,class:p(["metric-card",`metric-tone--${e?.tone||"blue"}`])},[o("div",b,[e?.icon?(t(),s("div",x,[y(d,null,{default:h(()=>[(t(),i(k(e.icon)))]),_:2},1024)])):l("",!0),o("div",B,n(e?.label||"-"),1)]),o("div",N,[a.loading?(t(),i(m,{key:0,rows:1,animated:""})):(t(),s(r,{key:1},[v(n(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",g,n(e?.hint||e?.sub),1)):l("",!0)],2))),128))],4)}}},S=_(C,[["__scopeId","data-v-00e217d4"]]);export{S as M};
diff --git a/static/admin/assets/ReportPage-21i0jfUn.css b/static/admin/assets/ReportPage-21i0jfUn.css
deleted file mode 100644
index b897cc9..0000000
--- a/static/admin/assets/ReportPage-21i0jfUn.css
+++ /dev/null
@@ -1 +0,0 @@
-.page-stack[data-v-48412586]{display:flex;flex-direction:column;gap:14px}.report-hero[data-v-48412586]{position:relative;overflow:hidden;border-radius:18px;border:1px solid rgba(17,24,39,.1);background:radial-gradient(circle at 10% 10%,rgba(59,130,246,.18),transparent 48%),radial-gradient(circle at 80% 0%,rgba(236,72,153,.16),transparent 45%),radial-gradient(circle at 90% 90%,rgba(16,185,129,.14),transparent 42%),#ffffffb8;box-shadow:0 14px 40px #0f172a14;-webkit-backdrop-filter:saturate(180%) blur(12px);backdrop-filter:saturate(180%) blur(12px);padding:16px}.hero-head[data-v-48412586]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:14px}.hero-main h2[data-v-48412586]{margin:0;font-size:19px;font-weight:900;letter-spacing:.2px}.hero-meta[data-v-48412586]{margin-top:6px;font-size:12px;display:flex;align-items:center;flex-wrap:wrap;gap:8px}.hero-dot[data-v-48412586]{opacity:.65}.hero-actions[data-v-48412586]{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.mobile-report[data-v-48412586]{display:grid;gap:12px;grid-template-columns:repeat(3,minmax(0,1fr))}.mobile-module-card[data-v-48412586]{position:relative;overflow:hidden;border-radius:14px;border:1px solid rgba(17,24,39,.12);background:#ffffffe6;box-shadow:var(--app-shadow-soft)}.mobile-module-card[data-v-48412586]:before{content:"";position:absolute;left:0;top:0;right:0;height:3px;background:var(--mobile-accent, #3b82f6)}.mobile-tone-blue[data-v-48412586]{--mobile-accent: linear-gradient(90deg, #3b82f6, #06b6d4)}.mobile-tone-cyan[data-v-48412586]{--mobile-accent: linear-gradient(90deg, #06b6d4, #3b82f6)}.mobile-tone-purple[data-v-48412586]{--mobile-accent: linear-gradient(90deg, #8b5cf6, #ec4899)}.mobile-tone-orange[data-v-48412586]{--mobile-accent: linear-gradient(90deg, #f59e0b, #f97316)}.mobile-tone-green[data-v-48412586]{--mobile-accent: linear-gradient(90deg, #10b981, #22c55e)}.mobile-tone-red[data-v-48412586]{--mobile-accent: linear-gradient(90deg, #ef4444, #f43f5e)}.mobile-module-head[data-v-48412586]{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}.mobile-module-title[data-v-48412586]{font-size:13px;font-weight:900;color:#0f172a}.mobile-module-desc[data-v-48412586]{min-width:0;max-width:68%;font-size:11px;line-height:1.4;text-align:right}.mobile-metrics[data-v-48412586]{margin-top:10px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.mobile-metric-item[data-v-48412586]{padding:8px 10px;border-radius:10px;border:1px solid rgba(17,24,39,.08);background:#f8fafce6}.mobile-metric-label[data-v-48412586]{font-size:11px;line-height:1.35}.mobile-metric-value[data-v-48412586]{margin-top:4px;font-size:14px;font-weight:800;color:#0f172a;line-height:1.3;word-break:break-word}.hero-overview-grid[data-v-48412586]{display:none}.panel[data-v-48412586]{border-radius:18px;border:1px solid rgba(17,24,39,.1);background:#ffffffb8;box-shadow:var(--app-shadow);-webkit-backdrop-filter:saturate(180%) blur(10px);backdrop-filter:saturate(180%) blur(10px)}.panel-head[data-v-48412586]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:14px}.head-left[data-v-48412586]{display:flex;align-items:center;gap:12px;min-width:0}.head-text[data-v-48412586]{min-width:0}.head-icon[data-v-48412586]{width:40px;height:40px;border-radius:16px;display:flex;align-items:center;justify-content:center;border:1px solid rgba(17,24,39,.08);flex:0 0 auto}.tone-blue[data-v-48412586]{background:#3b82f61f;color:#1d4ed8}.tone-cyan[data-v-48412586]{background:#22d3ee1f;color:#0369a1}.tone-purple[data-v-48412586]{background:#8b5cf61f;color:#6d28d9}.tone-orange[data-v-48412586]{background:#f59e0b1f;color:#b45309}.tone-green[data-v-48412586]{background:#10b9811f;color:#047857}.tone-red[data-v-48412586]{background:#ef44441f;color:#b91c1c}.panel-title[data-v-48412586]{font-size:14px;font-weight:900}.panel-sub[data-v-48412586]{margin-top:4px;font-size:12px;color:var(--app-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.metrics-block[data-v-48412586]{display:flex;flex-direction:column;gap:10px}.block-title[data-v-48412586]{font-size:13px;font-weight:900;letter-spacing:.2px}.divider[data-v-48412586]{height:1px;background:linear-gradient(90deg,transparent,rgba(17,24,39,.12),transparent);margin:14px 0}.queue-tabs[data-v-48412586] .el-tabs__header{margin:0 0 10px}.tab-label[data-v-48412586]{display:inline-flex;align-items:center;gap:6px}.table-wrap[data-v-48412586]{overflow-x:auto;border-radius:10px;border:1px solid var(--app-border);background:#fff}.help[data-v-48412586]{margin-top:10px;font-size:12px}.resource-grid[data-v-48412586]{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.resource-item[data-v-48412586]{border:1px solid rgba(17,24,39,.08);border-radius:16px;padding:12px;background:#ffffffb3}.resource-k[data-v-48412586]{font-size:12px;margin-bottom:8px}.resource-sub[data-v-48412586]{margin-top:8px;font-size:12px}.config-grid[data-v-48412586]{display:grid;grid-template-columns:1fr;gap:10px}.config-item[data-v-48412586]{border:1px solid rgba(17,24,39,.08);border-radius:16px;padding:12px;background:#ffffffb3}.config-k[data-v-48412586]{font-size:12px}.config-v[data-v-48412586]{margin-top:8px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}.config-inline[data-v-48412586]{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.config-split[data-v-48412586]{opacity:.65}.config-sub[data-v-48412586]{margin-top:8px;font-size:12px}.err[data-v-48412586]{color:#b91c1c}[data-v-48412586] .el-table{--el-table-border-color: rgba(17, 24, 39, .08);--el-table-header-bg-color: rgba(246, 247, 251, .8)}[data-v-48412586] .el-table th.el-table__cell{background:#f6f7fbcc}@media(max-width:1200px){.mobile-report[data-v-48412586]{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:768px){.mobile-report[data-v-48412586]{grid-template-columns:1fr;gap:10px}.report-hero[data-v-48412586]{border-radius:14px;padding:12px}.hero-main h2[data-v-48412586]{font-size:17px}.hero-meta[data-v-48412586]{margin-top:4px;gap:6px;font-size:11px}.resource-grid[data-v-48412586]{grid-template-columns:1fr}}@media(max-width:420px){.mobile-metrics[data-v-48412586]{grid-template-columns:1fr}.mobile-module-desc[data-v-48412586]{max-width:62%}}
diff --git a/static/admin/assets/ReportPage-BITaX3hh.js b/static/admin/assets/ReportPage-BITaX3hh.js
deleted file mode 100644
index 01547e1..0000000
--- a/static/admin/assets/ReportPage-BITaX3hh.js
+++ /dev/null
@@ -1 +0,0 @@
-import{q as Le,r as Ie,v as We,x as Ae,y as Ne,l as Ee,A as Be}from"./vendor-element-CJoVtPsD.js";import{a as De,_ as ze,f as Fe}from"./index-bXG7gEdM.js";import{f as Ve}from"./email-DgztAWKJ.js";import{f as Ue,a as Oe,b as Ge,c as je,d as He}from"./tasks-Y3hOqi3L.js";import{f as Qe}from"./system-1p1K-j-4.js";import{M as Ze}from"./MetricGrid-CPdC57ab.js";import{i as J,r as o,c as t,v as Ke,Y as Ye,aW as Je,z as p,A as m,B as i,S as Xe,N as A,Q as y,R as X,ak as ee,K as el,L as ll,E as al}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function tl(){const{data:N}=await De.get("/browser_pool/stats");return N}const ul={class:"page-stack"},sl={class:"report-hero"},nl={class:"hero-head"},ol={class:"hero-main"},rl={class:"hero-meta app-muted"},il={key:0},cl={key:1,class:"hero-dot"},vl={key:2},dl={class:"mobile-report"},_l={class:"mobile-module-head"},bl={class:"mobile-module-title"},ml={class:"mobile-module-desc app-muted"},fl={class:"mobile-metrics"},pl={class:"mobile-metric-label app-muted"},hl={class:"mobile-metric-value"},kl=5e3,gl=2e4,yl={__name:"ReportPage",setup(N){const le=J("refreshStats",null),ae=J("adminStats",null),$=o(!1),M=o(!1),h=o(""),q=o(null),r=o(null),n=o(null),b=o(null),d=o(null),S=o(null),k=o(null),c=o(null),_=o(null);o("running");function te(){try{h.value=new Date().toLocaleString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{h.value=""}}function e(l){const a=Number(l);return Number.isFinite(a)?a:0}function ue(l){const a=String(l??"").trim();if(!a)return 0;const s=a.endsWith("%")?a.slice(0,-1):a,u=Number(s);return!Number.isFinite(u)||u<0?0:u>1e3?1e3:u}function P(l){return`${Math.round(ue(l))}%`}function R(l){const a=Number(l);return!Number.isFinite(a)||a<0?"-":a>=100?`${Math.round(a)}ms`:`${a.toFixed(1)}ms`}function se(l){const a=Number(l);if(!Number.isFinite(a)||a<=0)return"-";try{return new Date(a*1e3).toLocaleTimeString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{return"-"}}function ne(l){const a=String(l??"").trim();return!a||a==="manual"?"手动":a==="scheduled"?"系统定时":a==="batch"?"批量执行":a==="resumed"?"断点续跑":a.startsWith("user_scheduled:")?"用户定时":a}const E=t(()=>{const l=ae?.value||{},a=e(r.value?.max_concurrent);return[{label:"总用户数",value:e(l.total_users),icon:Le,tone:"blue"},{label:"今日注册",value:e(l.new_users_today),icon:Ie,tone:"green"},{label:"近7天注册",value:e(l.new_users_7d),icon:We,tone:"purple"},{label:"总账号数",value:e(l.total_accounts),icon:Ae,tone:"cyan"},{label:"VIP用户",value:e(l.vip_users),icon:Ne,tone:"orange"},{label:"运行中任务",value:e(r.value?.running_count),icon:Ee,tone:"green",sub:a?`并发上限 ${a}`:""},{label:"排队任务",value:e(r.value?.queuing_count),icon:Be,tone:"purple"}]}),v=t(()=>q.value?.today||{}),f=t(()=>q.value?.total||{});t(()=>r.value?.running||[]);const x=t(()=>r.value?.queuing||[]),oe=t(()=>e(r.value?.running_count)),re=t(()=>e(r.value?.queuing_count)),ie=t(()=>{const l=k.value?.workers;return Array.isArray(l)?[...l].sort((a,s)=>e(a?.worker_id)-e(s?.worker_id)):[]}),B=t(()=>e(k.value?.total_workers)),C=t(()=>ie.value.filter(l=>!!l?.has_browser).length),D=t(()=>e(k.value?.idle_workers)),z=t(()=>e(k.value?.queue_size)),T=t(()=>e(k.value?.active_workers)),F=t(()=>{const l=e(v.value.success_tasks),a=e(v.value.failed_tasks),s=l+a;return s>0?Math.round(l/s*1e3)/10:0}),L=t(()=>e(n.value?.success_rate));t(()=>[{label:"总任务",value:e(v.value.total_tasks),tone:"blue"},{label:"成功",value:e(v.value.success_tasks),tone:"green"},{label:"失败",value:e(v.value.failed_tasks),tone:"red"},{label:"浏览内容",value:e(v.value.total_items),tone:"purple"},{label:"查看附件",value:e(v.value.total_attachments),tone:"cyan"}]),t(()=>[{label:"总任务",value:e(f.value.total_tasks),tone:"blue"},{label:"成功",value:e(f.value.success_tasks),tone:"green"},{label:"失败",value:e(f.value.failed_tasks),tone:"red"},{label:"浏览内容",value:e(f.value.total_items),tone:"purple"},{label:"查看附件",value:e(f.value.total_attachments),tone:"cyan"}]),t(()=>[{label:"总发送",value:e(n.value?.total_sent),tone:"blue"},{label:"成功",value:e(n.value?.total_success),tone:"green"},{label:"失败",value:e(n.value?.total_failed),tone:"red"},{label:"成功率",value:`${L.value}%`,tone:"purple"}]),t(()=>[{label:"注册验证",value:e(n.value?.register_sent),tone:"cyan"},{label:"密码重置",value:e(n.value?.reset_sent),tone:"orange"},{label:"邮箱绑定",value:e(n.value?.bind_sent),tone:"purple"},{label:"任务完成",value:e(n.value?.task_complete_sent),tone:"green"}]),t(()=>[{label:"总反馈",value:e(b.value?.total),tone:"blue"},{label:"待处理",value:e(b.value?.pending),tone:"orange"},{label:"已回复",value:e(b.value?.replied),tone:"green"}]),t(()=>[{label:"总 Worker",value:B.value,tone:"blue"},{label:"活跃 Worker",value:C.value,tone:"green"},{label:"空闲 Worker",value:D.value,tone:"cyan"},{label:"忙碌 Worker",value:T.value,tone:"orange"},{label:"队列",value:z.value,tone:"purple"}]);const ce=t(()=>(c.value?.schedule_enabled??0)===1),ve=t(()=>c.value?.schedule_time||"-"),de=t(()=>c.value?.schedule_browse_type||"-"),_e=t(()=>String(c.value?.schedule_weekdays||"").trim());t(()=>{const l=_e.value;if(!l)return"";const a={1:"周一",2:"周二",3:"周三",4:"周四",5:"周五",6:"周六",7:"周日"},s=l.split(",").map(u=>u.trim()).filter(Boolean);return s.length?s.map(u=>a[Number(u)]||u).join("、"):l});const be=t(()=>(c.value?.proxy_enabled??0)===1);t(()=>c.value?.proxy_api_url||"");const V=t(()=>e(c.value?.proxy_expire_minutes)),I=t(()=>e(c.value?.max_concurrent_global)),me=t(()=>e(c.value?.max_concurrent_per_account)),fe=t(()=>e(c.value?.max_screenshot_concurrent)),pe=t(()=>{const l=e(r.value?.running_count),a=e(r.value?.queuing_count),s=e(r.value?.max_concurrent);return`运行中 ${l} / 排队 ${a} / 并发上限 ${s||I.value||"-"}`}),he=t(()=>E.value.map(l=>({label:l.label,value:l.sub?`${l.value}(${l.sub})`:l.value}))),ke=t(()=>[{label:"今日总任务",value:e(v.value.total_tasks)},{label:"今日成功",value:e(v.value.success_tasks)},{label:"今日失败",value:e(v.value.failed_tasks)},{label:"今日成功率",value:`${F.value}%`},{label:"累计任务",value:e(f.value.total_tasks)},{label:"累计成功",value:e(f.value.success_tasks)}]),ge=t(()=>[{label:"运行中",value:oe.value},{label:"排队中",value:re.value},{label:"并发上限",value:e(r.value?.max_concurrent)||I.value||"-"},{label:"排队首条来源",value:ne(x.value[0]?.source)},{label:"排队首条状态",value:x.value[0]?.detail_status||x.value[0]?.status||"-"},{label:"最长等待",value:x.value[0]?.elapsed_display||"-"}]),ye=t(()=>[{label:"总发送",value:e(n.value?.total_sent)},{label:"成功",value:e(n.value?.total_success)},{label:"失败",value:e(n.value?.total_failed)},{label:"成功率",value:`${L.value}%`},{label:"注册验证",value:e(n.value?.register_sent)},{label:"重置密码",value:e(n.value?.reset_sent)}]),we=t(()=>[{label:"总反馈",value:e(b.value?.total)},{label:"待处理",value:e(b.value?.pending)},{label:"已回复",value:e(b.value?.replied)}]),Se=t(()=>[{label:"CPU",value:P(d.value?.cpu_percent)},{label:"内存",value:P(d.value?.memory_percent)},{label:"磁盘",value:P(d.value?.disk_percent)},{label:"容器状态",value:S.value?.status||"-"},{label:"容器名",value:S.value?.container_name||"-"},{label:"容器运行",value:S.value?.uptime||"-"}]),xe=t(()=>[{label:"总 Worker",value:B.value},{label:"活跃 Worker",value:C.value},{label:"忙碌 Worker",value:T.value},{label:"空闲 Worker",value:D.value},{label:"任务队列",value:z.value}]),$e=t(()=>{const l=_.value?.top_paths;return Array.isArray(l)?l.slice(0,3):[]}),Me=t(()=>{const l=[{label:"总请求",value:e(_.value?.total_requests)},{label:"API请求",value:e(_.value?.api_requests)},{label:"慢请求",value:e(_.value?.slow_requests)},{label:"错误请求",value:e(_.value?.error_requests)}];return $e.value.forEach((a,s)=>{const u=String(a?.path||"-");l.push({label:`慢接口${s+1}`,value:`${u} · 峰值 ${R(a?.max_ms)}`})}),l}),qe=t(()=>{const l=R(_.value?.avg_duration_ms),a=R(_.value?.max_duration_ms),s=se(_.value?.last_request_ts);return`均值 ${l} · 峰值 ${a} · 最近 ${s}`}),Pe=t(()=>[{label:"定时任务",value:ce.value?"启用":"关闭"},{label:"执行时间",value:ve.value||"-"},{label:"浏览类型",value:de.value||"-"},{label:"代理",value:be.value?"启用":"关闭"},{label:"代理有效期",value:V.value?`${V.value} 分钟`:"-"},{label:"全局并发",value:I.value||"-"},{label:"单账号并发",value:me.value||"-"},{label:"截图并发",value:fe.value||"-"}]),Re=t(()=>[{key:"overview",title:"平台概览",desc:h.value?`更新 ${h.value}`:"核心指标",tone:"blue",items:he.value},{key:"task",title:"任务概览",desc:e(v.value.total_tasks)>0?`今日成功率 ${F.value}%`:"今日暂无任务",tone:"purple",items:ke.value},{key:"queue",title:"队列监控",desc:pe.value,tone:"blue",items:ge.value},{key:"email",title:"邮件报表",desc:`成功率 ${L.value}%`,tone:"cyan",items:ye.value},{key:"feedback",title:"反馈概览",desc:`待处理 ${e(b.value?.pending)} 条`,tone:"orange",items:we.value},{key:"resource",title:"系统资源",desc:d.value?.uptime?`运行 ${d.value.uptime}`:"运行状态获取中",tone:"green",items:Se.value},{key:"request",title:"接口性能",desc:qe.value,tone:"purple",items:Me.value},{key:"worker",title:"截图线程池",desc:`活跃 ${C.value} · 忙碌 ${T.value}`,tone:"cyan",items:xe.value},{key:"config",title:"配置概览",desc:"并发 / 代理 / 定时任务",tone:"red",items:Pe.value}]);async function U(l={}){const a=l.showLoading??!0;if(!M.value){M.value=!0,a&&($.value=!0);try{const[s,u,g,j,H,Q,Z,K,Y]=await Promise.allSettled([Ue(),Oe(),Ve(),Fe(),Ge(),je(),tl(),He(),Qe()]);s.status==="fulfilled"&&(q.value=s.value),u.status==="fulfilled"&&(r.value=u.value),g.status==="fulfilled"&&(n.value=g.value),j.status==="fulfilled"&&(b.value=j.value),H.status==="fulfilled"&&(d.value=H.value),Q.status==="fulfilled"&&(S.value=Q.value),Z.status==="fulfilled"&&(k.value=Z.value),K.status==="fulfilled"&&(_.value=K.value),Y.status==="fulfilled"&&(c.value=Y.value),await le?.(),te()}finally{M.value=!1,a&&($.value=!1)}}}let w=null;function Ce(){return typeof document>"u"?!1:document.visibilityState==="hidden"}function Te(){return Ce()?gl:kl}function O(){w&&(clearTimeout(w),w=null)}function W(){O(),w=window.setTimeout(async()=>{w=null,await U({showLoading:!1}).catch(()=>{}),W()},Te())}function G(){W()}return Ke(()=>{U({showLoading:!1}).catch(()=>{}).finally(()=>{W()}),window.addEventListener("visibilitychange",G)}),Ye(()=>{O(),window.removeEventListener("visibilitychange",G)}),(l,a)=>{const s=Je("el-card");return m(),p("div",ul,[i("section",sl,[i("div",nl,[i("div",ol,[a[0]||(a[0]=i("h2",null,"报表中心",-1)),i("div",rl,[h.value?(m(),p("span",il,"更新时间:"+y(h.value),1)):A("",!0),d.value?.uptime?(m(),p("span",cl,"·")):A("",!0),d.value?.uptime?(m(),p("span",vl,"运行 "+y(d.value.uptime),1)):A("",!0)])])]),Xe(Ze,{class:"hero-overview-grid",items:E.value,loading:$.value,"min-width":165},null,8,["items","loading"])]),i("section",dl,[(m(!0),p(X,null,ee(Re.value,u=>(m(),el(s,{key:u.key,shadow:"never",class:al(["mobile-module-card",`mobile-tone-${u.tone}`]),"body-style":{padding:"12px"}},{default:ll(()=>[i("div",_l,[i("div",bl,y(u.title),1),i("div",ml,y(u.desc),1)]),i("div",fl,[(m(!0),p(X,null,ee(u.items,g=>(m(),p("div",{key:`${u.key}-${g.label}`,class:"mobile-metric-item"},[i("div",pl,y(g.label),1),i("div",hl,y(g.value),1)]))),128))])]),_:2},1032,["class"]))),128))])])}}},Tl=ze(yl,[["__scopeId","data-v-48412586"]]);export{Tl as default};
diff --git a/static/admin/assets/ReportPage-DlF2eaTa.css b/static/admin/assets/ReportPage-DlF2eaTa.css
new file mode 100644
index 0000000..2d9ff32
--- /dev/null
+++ b/static/admin/assets/ReportPage-DlF2eaTa.css
@@ -0,0 +1 @@
+.page-stack[data-v-f984ef82]{display:flex;flex-direction:column;gap:14px}.report-hero[data-v-f984ef82]{position:relative;overflow:hidden;border-radius:18px;border:1px solid rgba(17,24,39,.1);background:radial-gradient(circle at 10% 10%,rgba(59,130,246,.18),transparent 48%),radial-gradient(circle at 80% 0%,rgba(236,72,153,.16),transparent 45%),radial-gradient(circle at 90% 90%,rgba(16,185,129,.14),transparent 42%),#ffffffb8;box-shadow:0 14px 40px #0f172a14;-webkit-backdrop-filter:saturate(180%) blur(12px);backdrop-filter:saturate(180%) blur(12px);padding:16px}.hero-head[data-v-f984ef82]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:14px}.hero-main h2[data-v-f984ef82]{margin:0;font-size:19px;font-weight:900;letter-spacing:.2px}.hero-meta[data-v-f984ef82]{margin-top:6px;font-size:12px;display:flex;align-items:center;flex-wrap:wrap;gap:8px}.hero-dot[data-v-f984ef82]{opacity:.65}.hero-actions[data-v-f984ef82]{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.mobile-report[data-v-f984ef82]{display:grid;gap:12px;grid-template-columns:repeat(3,minmax(0,1fr))}.mobile-module-card[data-v-f984ef82]{position:relative;overflow:hidden;border-radius:14px;border:1px solid rgba(17,24,39,.12);background:#ffffffe6;box-shadow:var(--app-shadow-soft)}.mobile-module-card[data-v-f984ef82]:before{content:"";position:absolute;left:0;top:0;right:0;height:3px;background:var(--mobile-accent, #3b82f6)}.mobile-tone-blue[data-v-f984ef82]{--mobile-accent: linear-gradient(90deg, #3b82f6, #06b6d4)}.mobile-tone-cyan[data-v-f984ef82]{--mobile-accent: linear-gradient(90deg, #06b6d4, #3b82f6)}.mobile-tone-purple[data-v-f984ef82]{--mobile-accent: linear-gradient(90deg, #8b5cf6, #ec4899)}.mobile-tone-orange[data-v-f984ef82]{--mobile-accent: linear-gradient(90deg, #f59e0b, #f97316)}.mobile-tone-green[data-v-f984ef82]{--mobile-accent: linear-gradient(90deg, #10b981, #22c55e)}.mobile-tone-red[data-v-f984ef82]{--mobile-accent: linear-gradient(90deg, #ef4444, #f43f5e)}.mobile-module-head[data-v-f984ef82]{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}.mobile-module-title[data-v-f984ef82]{font-size:13px;font-weight:900;color:#0f172a}.mobile-module-desc[data-v-f984ef82]{min-width:0;max-width:68%;font-size:11px;line-height:1.4;text-align:right}.mobile-metrics[data-v-f984ef82]{margin-top:10px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.mobile-metric-item[data-v-f984ef82]{padding:8px 10px;border-radius:10px;border:1px solid rgba(17,24,39,.08);background:#f8fafce6}.mobile-metric-label[data-v-f984ef82]{font-size:11px;line-height:1.35}.mobile-metric-value[data-v-f984ef82]{margin-top:4px;font-size:14px;font-weight:800;color:#0f172a;line-height:1.3;word-break:break-word}.module-extra-actions[data-v-f984ef82]{margin-top:10px;display:flex;justify-content:flex-end}.request-dialog-summary[data-v-f984ef82]{display:flex;flex-wrap:wrap;gap:10px 16px;font-size:12px;margin-bottom:12px}.request-dialog-block+.request-dialog-block[data-v-f984ef82]{margin-top:14px}.request-dialog-title[data-v-f984ef82]{font-size:13px;font-weight:800;margin-bottom:8px}.hero-overview-grid[data-v-f984ef82]{display:none}.panel[data-v-f984ef82]{border-radius:18px;border:1px solid rgba(17,24,39,.1);background:#ffffffb8;box-shadow:var(--app-shadow);-webkit-backdrop-filter:saturate(180%) blur(10px);backdrop-filter:saturate(180%) blur(10px)}.panel-head[data-v-f984ef82]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:14px}.head-left[data-v-f984ef82]{display:flex;align-items:center;gap:12px;min-width:0}.head-text[data-v-f984ef82]{min-width:0}.head-icon[data-v-f984ef82]{width:40px;height:40px;border-radius:16px;display:flex;align-items:center;justify-content:center;border:1px solid rgba(17,24,39,.08);flex:0 0 auto}.tone-blue[data-v-f984ef82]{background:#3b82f61f;color:#1d4ed8}.tone-cyan[data-v-f984ef82]{background:#22d3ee1f;color:#0369a1}.tone-purple[data-v-f984ef82]{background:#8b5cf61f;color:#6d28d9}.tone-orange[data-v-f984ef82]{background:#f59e0b1f;color:#b45309}.tone-green[data-v-f984ef82]{background:#10b9811f;color:#047857}.tone-red[data-v-f984ef82]{background:#ef44441f;color:#b91c1c}.panel-title[data-v-f984ef82]{font-size:14px;font-weight:900}.panel-sub[data-v-f984ef82]{margin-top:4px;font-size:12px;color:var(--app-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.metrics-block[data-v-f984ef82]{display:flex;flex-direction:column;gap:10px}.block-title[data-v-f984ef82]{font-size:13px;font-weight:900;letter-spacing:.2px}.divider[data-v-f984ef82]{height:1px;background:linear-gradient(90deg,transparent,rgba(17,24,39,.12),transparent);margin:14px 0}.queue-tabs[data-v-f984ef82] .el-tabs__header{margin:0 0 10px}.tab-label[data-v-f984ef82]{display:inline-flex;align-items:center;gap:6px}.table-wrap[data-v-f984ef82]{overflow-x:auto;border-radius:10px;border:1px solid var(--app-border);background:#fff}.help[data-v-f984ef82]{margin-top:10px;font-size:12px}.resource-grid[data-v-f984ef82]{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.resource-item[data-v-f984ef82]{border:1px solid rgba(17,24,39,.08);border-radius:16px;padding:12px;background:#ffffffb3}.resource-k[data-v-f984ef82]{font-size:12px;margin-bottom:8px}.resource-sub[data-v-f984ef82]{margin-top:8px;font-size:12px}.config-grid[data-v-f984ef82]{display:grid;grid-template-columns:1fr;gap:10px}.config-item[data-v-f984ef82]{border:1px solid rgba(17,24,39,.08);border-radius:16px;padding:12px;background:#ffffffb3}.config-k[data-v-f984ef82]{font-size:12px}.config-v[data-v-f984ef82]{margin-top:8px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}.config-inline[data-v-f984ef82]{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.config-split[data-v-f984ef82]{opacity:.65}.config-sub[data-v-f984ef82]{margin-top:8px;font-size:12px}.err[data-v-f984ef82]{color:#b91c1c}[data-v-f984ef82] .el-table{--el-table-border-color: rgba(17, 24, 39, .08);--el-table-header-bg-color: rgba(246, 247, 251, .8)}[data-v-f984ef82] .el-table th.el-table__cell{background:#f6f7fbcc}@media(max-width:1200px){.mobile-report[data-v-f984ef82]{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:768px){.mobile-report[data-v-f984ef82]{grid-template-columns:1fr;gap:10px}.report-hero[data-v-f984ef82]{border-radius:14px;padding:12px}.hero-main h2[data-v-f984ef82]{font-size:17px}.hero-meta[data-v-f984ef82]{margin-top:4px;gap:6px;font-size:11px}.resource-grid[data-v-f984ef82]{grid-template-columns:1fr}}@media(max-width:420px){.mobile-metrics[data-v-f984ef82]{grid-template-columns:1fr}.mobile-module-desc[data-v-f984ef82]{max-width:62%}}
diff --git a/static/admin/assets/ReportPage-JOjMl_cz.js b/static/admin/assets/ReportPage-JOjMl_cz.js
new file mode 100644
index 0000000..f26e8ca
--- /dev/null
+++ b/static/admin/assets/ReportPage-JOjMl_cz.js
@@ -0,0 +1 @@
+import{q as Fe,r as Ue,v as Oe,x as Ge,y as Ze,l as je,A as He}from"./vendor-element-CJoVtPsD.js";import{a as Qe,_ as Ke,f as Ye}from"./index-ZQnpYe51.js";import{f as Je}from"./email-D8pS1Pbx.js";import{f as Xe,a as et,b as tt,c as lt,d as at}from"./tasks-BrCJEcYb.js";import{f as st}from"./system-DO-Wv0rM.js";import{M as ut}from"./MetricGrid-DBgW22G4.js";import{i as le,r as d,c as a,v as ot,Y as nt,aW as M,z as k,A as h,B as u,S as r,N as L,Q as p,R as ae,ak as se,L as x,P as ue,K as rt,E as it}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function ct(){const{data:G}=await Qe.get("/browser_pool/stats");return G}const vt={class:"page-stack"},dt={class:"report-hero"},_t={class:"hero-head"},mt={class:"hero-main"},bt={class:"hero-meta app-muted"},pt={key:0},ft={key:1,class:"hero-dot"},ht={key:2},gt={class:"mobile-report"},kt={class:"mobile-module-head"},wt={class:"mobile-module-title"},yt={class:"mobile-module-desc app-muted"},xt={class:"mobile-metrics"},St={class:"mobile-metric-label app-muted"},qt={class:"mobile-metric-value"},$t={key:0,class:"module-extra-actions"},Mt={class:"request-dialog-summary app-muted"},Tt={class:"request-dialog-block"},Pt={class:"table-wrap"},Rt={class:"request-dialog-block"},Ct={class:"table-wrap"},At=5e3,Nt=2e4,Lt={__name:"ReportPage",setup(G){const oe=le("refreshStats",null),ne=le("adminStats",null),I=d(!1),W=d(!1),S=d(""),D=d(null),_=d(null),c=d(null),g=d(null),f=d(null),R=d(null),q=d(null),m=d(null),o=d(null),E=d(!1);d("running");function re(){try{S.value=new Date().toLocaleString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{S.value=""}}function t(l){const e=Number(l);return Number.isFinite(e)?e:0}function ie(l){const e=String(l??"").trim();if(!e)return 0;const s=e.endsWith("%")?e.slice(0,-1):e,n=Number(s);return!Number.isFinite(n)||n<0?0:n>1e3?1e3:n}function z(l){return`${Math.round(ie(l))}%`}function w(l){const e=Number(l);return!Number.isFinite(e)||e<0?"-":e>=100?`${Math.round(e)}ms`:`${e.toFixed(1)}ms`}function ce(l){const e=Number(l);if(!Number.isFinite(e)||e<=0)return"-";try{return new Date(e*1e3).toLocaleTimeString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{return"-"}}function ve(l){const e=Number(l);if(!Number.isFinite(e)||e<=0)return"-";try{return new Date(e*1e3).toLocaleString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{return"-"}}function de(l){const e=String(l??"").trim();return!e||e==="manual"?"手动":e==="scheduled"?"系统定时":e==="batch"?"批量执行":e==="resumed"?"断点续跑":e.startsWith("user_scheduled:")?"用户定时":e}const Z=a(()=>{const l=ne?.value||{},e=t(_.value?.max_concurrent);return[{label:"总用户数",value:t(l.total_users),icon:Fe,tone:"blue"},{label:"今日注册",value:t(l.new_users_today),icon:Ue,tone:"green"},{label:"近7天注册",value:t(l.new_users_7d),icon:Oe,tone:"purple"},{label:"总账号数",value:t(l.total_accounts),icon:Ge,tone:"cyan"},{label:"VIP用户",value:t(l.vip_users),icon:Ze,tone:"orange"},{label:"运行中任务",value:t(_.value?.running_count),icon:je,tone:"green",sub:e?`并发上限 ${e}`:""},{label:"排队任务",value:t(_.value?.queuing_count),icon:He,tone:"purple"}]}),b=a(()=>D.value?.today||{}),y=a(()=>D.value?.total||{});a(()=>_.value?.running||[]);const C=a(()=>_.value?.queuing||[]),_e=a(()=>t(_.value?.running_count)),me=a(()=>t(_.value?.queuing_count)),be=a(()=>{const l=q.value?.workers;return Array.isArray(l)?[...l].sort((e,s)=>t(e?.worker_id)-t(s?.worker_id)):[]}),j=a(()=>t(q.value?.total_workers)),B=a(()=>be.value.filter(l=>!!l?.has_browser).length),H=a(()=>t(q.value?.idle_workers)),Q=a(()=>t(q.value?.queue_size)),V=a(()=>t(q.value?.active_workers)),K=a(()=>{const l=t(b.value.success_tasks),e=t(b.value.failed_tasks),s=l+e;return s>0?Math.round(l/s*1e3)/10:0}),F=a(()=>t(c.value?.success_rate));a(()=>[{label:"总任务",value:t(b.value.total_tasks),tone:"blue"},{label:"成功",value:t(b.value.success_tasks),tone:"green"},{label:"失败",value:t(b.value.failed_tasks),tone:"red"},{label:"浏览内容",value:t(b.value.total_items),tone:"purple"},{label:"查看附件",value:t(b.value.total_attachments),tone:"cyan"}]),a(()=>[{label:"总任务",value:t(y.value.total_tasks),tone:"blue"},{label:"成功",value:t(y.value.success_tasks),tone:"green"},{label:"失败",value:t(y.value.failed_tasks),tone:"red"},{label:"浏览内容",value:t(y.value.total_items),tone:"purple"},{label:"查看附件",value:t(y.value.total_attachments),tone:"cyan"}]),a(()=>[{label:"总发送",value:t(c.value?.total_sent),tone:"blue"},{label:"成功",value:t(c.value?.total_success),tone:"green"},{label:"失败",value:t(c.value?.total_failed),tone:"red"},{label:"成功率",value:`${F.value}%`,tone:"purple"}]),a(()=>[{label:"注册验证",value:t(c.value?.register_sent),tone:"cyan"},{label:"密码重置",value:t(c.value?.reset_sent),tone:"orange"},{label:"邮箱绑定",value:t(c.value?.bind_sent),tone:"purple"},{label:"任务完成",value:t(c.value?.task_complete_sent),tone:"green"}]),a(()=>[{label:"总反馈",value:t(g.value?.total),tone:"blue"},{label:"待处理",value:t(g.value?.pending),tone:"orange"},{label:"已回复",value:t(g.value?.replied),tone:"green"}]),a(()=>[{label:"总 Worker",value:j.value,tone:"blue"},{label:"活跃 Worker",value:B.value,tone:"green"},{label:"空闲 Worker",value:H.value,tone:"cyan"},{label:"忙碌 Worker",value:V.value,tone:"orange"},{label:"队列",value:Q.value,tone:"purple"}]);const pe=a(()=>(m.value?.schedule_enabled??0)===1),fe=a(()=>m.value?.schedule_time||"-"),he=a(()=>m.value?.schedule_browse_type||"-"),ge=a(()=>String(m.value?.schedule_weekdays||"").trim());a(()=>{const l=ge.value;if(!l)return"";const e={1:"周一",2:"周二",3:"周三",4:"周四",5:"周五",6:"周六",7:"周日"},s=l.split(",").map(n=>n.trim()).filter(Boolean);return s.length?s.map(n=>e[Number(n)]||n).join("、"):l});const ke=a(()=>(m.value?.proxy_enabled??0)===1);a(()=>m.value?.proxy_api_url||"");const Y=a(()=>t(m.value?.proxy_expire_minutes)),U=a(()=>t(m.value?.max_concurrent_global)),we=a(()=>t(m.value?.max_concurrent_per_account)),ye=a(()=>t(m.value?.max_screenshot_concurrent)),xe=a(()=>{const l=t(_.value?.running_count),e=t(_.value?.queuing_count),s=t(_.value?.max_concurrent);return`运行中 ${l} / 排队 ${e} / 并发上限 ${s||U.value||"-"}`}),Se=a(()=>Z.value.map(l=>({label:l.label,value:l.sub?`${l.value}(${l.sub})`:l.value}))),qe=a(()=>[{label:"今日总任务",value:t(b.value.total_tasks)},{label:"今日成功",value:t(b.value.success_tasks)},{label:"今日失败",value:t(b.value.failed_tasks)},{label:"今日成功率",value:`${K.value}%`},{label:"累计任务",value:t(y.value.total_tasks)},{label:"累计成功",value:t(y.value.success_tasks)}]),$e=a(()=>[{label:"运行中",value:_e.value},{label:"排队中",value:me.value},{label:"并发上限",value:t(_.value?.max_concurrent)||U.value||"-"},{label:"排队首条来源",value:de(C.value[0]?.source)},{label:"排队首条状态",value:C.value[0]?.detail_status||C.value[0]?.status||"-"},{label:"最长等待",value:C.value[0]?.elapsed_display||"-"}]),Me=a(()=>[{label:"总发送",value:t(c.value?.total_sent)},{label:"成功",value:t(c.value?.total_success)},{label:"失败",value:t(c.value?.total_failed)},{label:"成功率",value:`${F.value}%`},{label:"注册验证",value:t(c.value?.register_sent)},{label:"重置密码",value:t(c.value?.reset_sent)}]),Te=a(()=>[{label:"总反馈",value:t(g.value?.total)},{label:"待处理",value:t(g.value?.pending)},{label:"已回复",value:t(g.value?.replied)}]),Pe=a(()=>[{label:"CPU",value:z(f.value?.cpu_percent)},{label:"内存",value:z(f.value?.memory_percent)},{label:"磁盘",value:z(f.value?.disk_percent)},{label:"容器状态",value:R.value?.status||"-"},{label:"容器名",value:R.value?.container_name||"-"},{label:"容器运行",value:R.value?.uptime||"-"}]),Re=a(()=>[{label:"总 Worker",value:j.value},{label:"活跃 Worker",value:B.value},{label:"忙碌 Worker",value:V.value},{label:"空闲 Worker",value:H.value},{label:"任务队列",value:Q.value}]),Ce=a(()=>{const l=o.value?.top_paths;return Array.isArray(l)?l.slice(0,3):[]}),Ae=a(()=>{const l=[{label:"总请求",value:t(o.value?.total_requests)},{label:"API请求",value:t(o.value?.api_requests)},{label:"慢请求",value:t(o.value?.slow_requests)},{label:"错误请求",value:t(o.value?.error_requests)}];return Ce.value.forEach((e,s)=>{const n=String(e?.path||"-");l.push({label:`慢接口${s+1}`,value:`${n} · 峰值 ${w(e?.max_ms)}`})}),l}),Ne=a(()=>{const l=w(o.value?.avg_duration_ms),e=w(o.value?.max_duration_ms),s=ce(o.value?.last_request_ts),n=w(o.value?.slow_threshold_ms);return`均值 ${l} · 峰值 ${e} · 慢阈 ${n} · 最近 ${s}`}),Le=a(()=>(Array.isArray(o.value?.top_paths)?o.value.top_paths:[]).map((e,s)=>({rank:s+1,path:String(e?.path||"-"),count:t(e?.count),avg_ms:w(e?.avg_ms),max_ms:w(e?.max_ms),status_5xx:t(e?.status_5xx)}))),Ie=a(()=>[...Array.isArray(o.value?.recent_slow)?o.value.recent_slow:[]].sort((e,s)=>Number(s?.time||0)-Number(e?.time||0)).map(e=>({time_text:ve(e?.time),method:String(e?.method||"-").toUpperCase(),path:String(e?.path||"-"),status:t(e?.status),duration_ms:w(e?.duration_ms)})));function We(l){const e=t(l);return e>=500?"danger":e>=400?"warning":e>=300?"info":"success"}function De(){E.value=!0}const Ee=a(()=>[{label:"定时任务",value:pe.value?"启用":"关闭"},{label:"执行时间",value:fe.value||"-"},{label:"浏览类型",value:he.value||"-"},{label:"代理",value:ke.value?"启用":"关闭"},{label:"代理有效期",value:Y.value?`${Y.value} 分钟`:"-"},{label:"全局并发",value:U.value||"-"},{label:"单账号并发",value:we.value||"-"},{label:"截图并发",value:ye.value||"-"}]),ze=a(()=>[{key:"overview",title:"平台概览",desc:S.value?`更新 ${S.value}`:"核心指标",tone:"blue",items:Se.value},{key:"task",title:"任务概览",desc:t(b.value.total_tasks)>0?`今日成功率 ${K.value}%`:"今日暂无任务",tone:"purple",items:qe.value},{key:"queue",title:"队列监控",desc:xe.value,tone:"blue",items:$e.value},{key:"email",title:"邮件报表",desc:`成功率 ${F.value}%`,tone:"cyan",items:Me.value},{key:"feedback",title:"反馈概览",desc:`待处理 ${t(g.value?.pending)} 条`,tone:"orange",items:Te.value},{key:"resource",title:"系统资源",desc:f.value?.uptime?`运行 ${f.value.uptime}`:"运行状态获取中",tone:"green",items:Pe.value},{key:"request",title:"接口性能",desc:Ne.value,tone:"purple",items:Ae.value},{key:"worker",title:"截图线程池",desc:`活跃 ${B.value} · 忙碌 ${V.value}`,tone:"cyan",items:Re.value},{key:"config",title:"配置概览",desc:"并发 / 代理 / 定时任务",tone:"red",items:Ee.value}]);async function J(l={}){const e=l.showLoading??!0;if(!W.value){W.value=!0,e&&(I.value=!0);try{const[s,n,v,P,A,N,i,$,te]=await Promise.allSettled([Xe(),et(),Je(),Ye(),tt(),lt(),ct(),at(),st()]);s.status==="fulfilled"&&(D.value=s.value),n.status==="fulfilled"&&(_.value=n.value),v.status==="fulfilled"&&(c.value=v.value),P.status==="fulfilled"&&(g.value=P.value),A.status==="fulfilled"&&(f.value=A.value),N.status==="fulfilled"&&(R.value=N.value),i.status==="fulfilled"&&(q.value=i.value),$.status==="fulfilled"&&(o.value=$.value),te.status==="fulfilled"&&(m.value=te.value),await oe?.(),re()}finally{W.value=!1,e&&(I.value=!1)}}}let T=null;function Be(){return typeof document>"u"?!1:document.visibilityState==="hidden"}function Ve(){return Be()?Nt:At}function X(){T&&(clearTimeout(T),T=null)}function O(){X(),T=window.setTimeout(async()=>{T=null,await J({showLoading:!1}).catch(()=>{}),O()},Ve())}function ee(){O()}return ot(()=>{J({showLoading:!1}).catch(()=>{}).finally(()=>{O()}),window.addEventListener("visibilitychange",ee)}),nt(()=>{X(),window.removeEventListener("visibilitychange",ee)}),(l,e)=>{const s=M("el-button"),n=M("el-card"),v=M("el-table-column"),P=M("el-table"),A=M("el-tag"),N=M("el-dialog");return h(),k("div",vt,[u("section",dt,[u("div",_t,[u("div",mt,[e[1]||(e[1]=u("h2",null,"报表中心",-1)),u("div",bt,[S.value?(h(),k("span",pt,"更新时间:"+p(S.value),1)):L("",!0),f.value?.uptime?(h(),k("span",ft,"·")):L("",!0),f.value?.uptime?(h(),k("span",ht,"运行 "+p(f.value.uptime),1)):L("",!0)])])]),r(ut,{class:"hero-overview-grid",items:Z.value,loading:I.value,"min-width":165},null,8,["items","loading"])]),u("section",gt,[(h(!0),k(ae,null,se(ze.value,i=>(h(),rt(n,{key:i.key,shadow:"never",class:it(["mobile-module-card",`mobile-tone-${i.tone}`]),"body-style":{padding:"12px"}},{default:x(()=>[u("div",kt,[u("div",wt,p(i.title),1),u("div",yt,p(i.desc),1)]),u("div",xt,[(h(!0),k(ae,null,se(i.items,$=>(h(),k("div",{key:`${i.key}-${$.label}`,class:"mobile-metric-item"},[u("div",St,p($.label),1),u("div",qt,p($.value),1)]))),128))]),i.key==="request"?(h(),k("div",$t,[r(s,{size:"small",type:"primary",plain:"",onClick:De},{default:x(()=>[...e[2]||(e[2]=[ue("查看慢接口详情",-1)])]),_:1})])):L("",!0)]),_:2},1032,["class"]))),128))]),r(N,{modelValue:E.value,"onUpdate:modelValue":e[0]||(e[0]=i=>E.value=i),title:"慢接口详情",width:"min(1080px, 96vw)"},{default:x(()=>[u("div",Mt,[u("span",null,"总请求:"+p(t(o.value?.total_requests)),1),u("span",null,"API请求:"+p(t(o.value?.api_requests)),1),u("span",null,"慢请求:"+p(t(o.value?.slow_requests)),1),u("span",null,"错误请求:"+p(t(o.value?.error_requests)),1)]),u("div",Tt,[e[3]||(e[3]=u("div",{class:"request-dialog-title"},"慢接口排行榜",-1)),u("div",Pt,[r(P,{data:Le.value,size:"small","max-height":"280"},{default:x(()=>[r(v,{prop:"rank",label:"#",width:"60"}),r(v,{prop:"path",label:"接口路径","min-width":"340","show-overflow-tooltip":""}),r(v,{prop:"count",label:"请求数",width:"100"}),r(v,{prop:"avg_ms",label:"平均耗时",width:"120"}),r(v,{prop:"max_ms",label:"峰值耗时",width:"120"}),r(v,{prop:"status_5xx",label:"5xx",width:"90"})]),_:1},8,["data"])])]),u("div",Rt,[e[4]||(e[4]=u("div",{class:"request-dialog-title"},"最近慢请求",-1)),u("div",Ct,[r(P,{data:Ie.value,size:"small","max-height":"320"},{default:x(()=>[r(v,{prop:"time_text",label:"时间",width:"180"}),r(v,{prop:"method",label:"方法",width:"90"}),r(v,{prop:"path",label:"接口路径","min-width":"320","show-overflow-tooltip":""}),r(v,{label:"状态",width:"100"},{default:x(i=>[r(A,{size:"small",type:We(i.row.status)},{default:x(()=>[ue(p(i.row.status||"-"),1)]),_:2},1032,["type"])]),_:1}),r(v,{prop:"duration_ms",label:"耗时",width:"110"})]),_:1},8,["data"])])])]),_:1},8,["modelValue"])])}}},Ot=Ke(Lt,[["__scopeId","data-v-f984ef82"]]);export{Ot as default};
diff --git a/static/admin/assets/SecurityPage-CQuWzZ_p.js b/static/admin/assets/SecurityPage-DlbTJ4QF.js
similarity index 99%
rename from static/admin/assets/SecurityPage-CQuWzZ_p.js
rename to static/admin/assets/SecurityPage-DlbTJ4QF.js
index 7957284..17457d0 100644
--- a/static/admin/assets/SecurityPage-CQuWzZ_p.js
+++ b/static/admin/assets/SecurityPage-DlbTJ4QF.js
@@ -1,4 +1,4 @@
-import{a as g,_ as ze}from"./index-bXG7gEdM.js";import{M as qe}from"./MetricGrid-CPdC57ab.js";import{E as ee,f as w}from"./vendor-element-CJoVtPsD.js";import{r as d,c as oe,v as Ee,aW as v,b7 as Ae,z as T,A as c,B as p,S as a,L as l,P as i,R as Ke,ak as Oe,K as b,M as te,Q as f,N as ue}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function Ge(){const{data:u}=await g.get("/admin/security/dashboard");return u}async function He(u){const{data:m}=await g.get("/admin/security/threats",{params:u});return m}async function Qe(){const{data:u}=await g.get("/admin/security/banned-ips");return u}async function We(){const{data:u}=await g.get("/admin/security/banned-users");return u}async function Je(u){const{data:m}=await g.post("/admin/security/ban-ip",u);return m}async function Xe(u){const{data:m}=await g.post("/admin/security/unban-ip",{ip:u});return m}async function Ye(u){const{data:m}=await g.post("/admin/security/ban-user",u);return m}async function Ze(u){const{data:m}=await g.post("/admin/security/unban-user",{user_id:u});return m}async function et(u){const m=encodeURIComponent(String(u||"").trim()),{data:V}=await g.get(`/admin/security/ip-risk/${m}`);return V}async function tt(u){const{data:m}=await g.post("/admin/security/ip-risk/clear",{ip:u});return m}async function at(u){const m=encodeURIComponent(String(u||"").trim()),{data:V}=await g.get(`/admin/security/user-risk/${m}`);return V}async function lt(){const{data:u}=await g.post("/admin/security/cleanup",{});return u}const nt={class:"page-stack"},it={class:"app-page-title"},st={class:"toolbar"},ot={class:"filters"},ut={class:"table-wrap"},rt={key:1},dt={key:1},pt={class:"mono ellipsis"},ct={class:"ellipsis"},ft={class:"pagination"},mt={class:"page-hint app-muted"},vt={class:"toolbar"},_t={class:"table-wrap"},yt={class:"table-wrap"},bt={class:"filters"},gt={class:"filters"},kt={class:"risk-head"},ht={class:"risk-title"},wt={key:0},It={key:1},Vt={class:"toolbar"},xt={class:"table-wrap"},Ct={class:"mono ellipsis"},St={class:"ellipsis"},Pt={class:"dialog-actions"},re=20,Tt={__name:"SecurityPage",setup(u){const m=d("threats"),V=d(!1),j=d(null),ae=d(!1),z=d([]),q=d(0),C=d(1),U=d(""),B=d(""),$=d(!1),de=d([]),pe=d([]),ce=d("ips"),S=d(!1),R=d(!1),o=d({kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}),E=d("ip"),k=d(!1),A=d(""),K=d(""),_=d(null),h=d(""),Ie=["sql_injection","xss","path_traversal","command_injection","ssrf","scanner","bruteforce","csrf","xxe","file_upload"];function le(n){const e=Number(n);return Number.isFinite(e)?e:0}function N(n){const e=Number(n||0);return e>=80?{label:"高",type:"danger"}:e>=50?{label:"中",type:"warning"}:{label:"低",type:"success"}}function fe(n){const e=String(n||"").trim();return e||"永久"}function me(n){const e=[];return n?.field_name&&e.push(`字段: ${n.field_name}`),n?.rule&&e.push(`规则: ${n.rule}`),n?.matched&&e.push(`匹配: ${n.matched}`),n?.value_preview&&e.push(`值: ${n.value_preview}`),e.length?e.join(" · "):"-"}function O(n){const e=String(n?.request_method||"").trim(),s=String(n?.request_path||"").trim();return`${e} ${s}`.trim()||"-"}const Ve=oe(()=>{const n=new Set(Ie),e=j.value?.recent_threat_events||[];for(const s of e){const y=String(s?.threat_type||"").trim();y&&n.add(y)}for(const s of z.value||[]){const y=String(s?.threat_type||"").trim();y&&n.add(y)}return Array.from(n).sort((s,y)=>s.localeCompare(y)).map(s=>({label:s,value:s}))}),xe=oe(()=>{const n=j.value||{};return[{key:"threat_events_24h",label:"最近24小时威胁事件",value:le(n.threat_events_24h),tone:"red",hint:"用于衡量当前攻击面活跃度"},{key:"banned_ip_count",label:"当前封禁 IP 数",value:le(n.banned_ip_count),tone:"orange",hint:"自动与人工封禁总量"},{key:"banned_user_count",label:"当前封禁用户数",value:le(n.banned_user_count),tone:"purple",hint:"高风险账户拦截情况"}]}),Ce=oe(()=>Math.max(1,Math.ceil((q.value||0)/re)));async function D(){V.value=!0;try{j.value=await Ge()}catch{j.value=null}finally{V.value=!1}}async function G(){ae.value=!0;try{const n={page:C.value,per_page:re};U.value&&(n.event_type=U.value),B.value&&(n.severity=B.value);const e=await He(n);z.value=e?.items||[],q.value=e?.total||0}catch{z.value=[],q.value=0}finally{ae.value=!1}}async function F(){if(!$.value){$.value=!0;try{const[n,e]=await Promise.allSettled([Qe(),We()]);de.value=n.status==="fulfilled"?n.value?.items||[]:[],pe.value=e.status==="fulfilled"?e.value?.items||[]:[]}finally{$.value=!1}}}async function ve(){await Promise.allSettled([D(),G(),F()])}function Se(){C.value=1,G()}function Pe(){U.value="",B.value="",C.value=1,G()}function _e(){o.value={kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}}function H(n="ip",e={}){_e(),o.value.kind=n==="user"?"user":"ip",o.value.kind==="ip"?o.value.ip=String(e.ip||"").trim():o.value.user_id=String(e.user_id||"").trim(),e.reason&&(o.value.reason=String(e.reason||"").trim()),S.value=!0}async function Te(){const n=o.value.kind,e=String(o.value.reason||"").trim(),s=!!o.value.permanent,y=Number(o.value.duration_hours||24);if(!e){w.error("原因不能为空");return}if(n==="ip"){const I=String(o.value.ip||"").trim();if(!I){w.error("IP不能为空");return}R.value=!0;try{await Je({ip:I,reason:e,duration_hours:y,permanent:s}),w.success("IP已封禁"),S.value=!1,await Promise.allSettled([D(),F()])}catch{}finally{R.value=!1}return}const W=String(o.value.user_id||"").trim(),r=Number.parseInt(W,10);if(!Number.isFinite(r)){w.error("用户ID无效");return}R.value=!0;try{await Ye({user_id:r,reason:e,duration_hours:y,permanent:s}),w.success("用户已封禁"),S.value=!1,await Promise.allSettled([D(),F()])}catch{}finally{R.value=!1}}async function ye(n){const e=String(n||"").trim();if(e){try{await ee.confirm(`确定解除对 IP ${e} 的封禁吗?`,"解除封禁",{confirmButtonText:"解除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await Xe(e),w.success("已解除IP封禁"),await Promise.allSettled([D(),F()])}catch{}}}async function be(n){const e=Number.parseInt(String(n||"").trim(),10);if(Number.isFinite(e)){try{await ee.confirm(`确定解除对 用户ID ${e} 的封禁吗?`,"解除封禁",{confirmButtonText:"解除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await Ze(e),w.success("已解除用户封禁"),await Promise.allSettled([D(),F()])}catch{}}}function ge(n){const e=String(n||"").trim();e&&(m.value="risk",E.value="ip",A.value=e,Q())}function ke(n){const e=String(n||"").trim();e&&(m.value="risk",E.value="user",K.value=e,ne())}async function Q(){const n=String(A.value||"").trim();if(!n){w.error("请输入IP");return}k.value=!0;try{_.value=await et(n),h.value="ip"}catch{_.value=null,h.value=""}finally{k.value=!1}}async function ne(){const n=String(K.value||"").trim(),e=Number.parseInt(n,10);if(!Number.isFinite(e)){w.error("请输入有效的用户ID");return}k.value=!0;try{_.value=await at(e),h.value="user"}catch{_.value=null,h.value=""}finally{k.value=!1}}function Ue(){!_.value||!h.value||(h.value==="ip"?H("ip",{ip:_.value?.ip,reason:"风险查询手动封禁"}):H("user",{user_id:_.value?.user_id,reason:"风险查询手动封禁"}))}async function Be(){!_.value||!h.value||(h.value==="ip"?(await ye(_.value?.ip),await Q()):(await be(_.value?.user_id),await ne()))}async function $e(){if(h.value!=="ip")return;const n=String(_.value?.ip||"").trim();if(n){try{await ee.confirm(`确定清除 IP ${n} 的风险分吗?
+import{a as g,_ as ze}from"./index-ZQnpYe51.js";import{M as qe}from"./MetricGrid-DBgW22G4.js";import{E as ee,f as w}from"./vendor-element-CJoVtPsD.js";import{r as d,c as oe,v as Ee,aW as v,b7 as Ae,z as T,A as c,B as p,S as a,L as l,P as i,R as Ke,ak as Oe,K as b,M as te,Q as f,N as ue}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function Ge(){const{data:u}=await g.get("/admin/security/dashboard");return u}async function He(u){const{data:m}=await g.get("/admin/security/threats",{params:u});return m}async function Qe(){const{data:u}=await g.get("/admin/security/banned-ips");return u}async function We(){const{data:u}=await g.get("/admin/security/banned-users");return u}async function Je(u){const{data:m}=await g.post("/admin/security/ban-ip",u);return m}async function Xe(u){const{data:m}=await g.post("/admin/security/unban-ip",{ip:u});return m}async function Ye(u){const{data:m}=await g.post("/admin/security/ban-user",u);return m}async function Ze(u){const{data:m}=await g.post("/admin/security/unban-user",{user_id:u});return m}async function et(u){const m=encodeURIComponent(String(u||"").trim()),{data:V}=await g.get(`/admin/security/ip-risk/${m}`);return V}async function tt(u){const{data:m}=await g.post("/admin/security/ip-risk/clear",{ip:u});return m}async function at(u){const m=encodeURIComponent(String(u||"").trim()),{data:V}=await g.get(`/admin/security/user-risk/${m}`);return V}async function lt(){const{data:u}=await g.post("/admin/security/cleanup",{});return u}const nt={class:"page-stack"},it={class:"app-page-title"},st={class:"toolbar"},ot={class:"filters"},ut={class:"table-wrap"},rt={key:1},dt={key:1},pt={class:"mono ellipsis"},ct={class:"ellipsis"},ft={class:"pagination"},mt={class:"page-hint app-muted"},vt={class:"toolbar"},_t={class:"table-wrap"},yt={class:"table-wrap"},bt={class:"filters"},gt={class:"filters"},kt={class:"risk-head"},ht={class:"risk-title"},wt={key:0},It={key:1},Vt={class:"toolbar"},xt={class:"table-wrap"},Ct={class:"mono ellipsis"},St={class:"ellipsis"},Pt={class:"dialog-actions"},re=20,Tt={__name:"SecurityPage",setup(u){const m=d("threats"),V=d(!1),j=d(null),ae=d(!1),z=d([]),q=d(0),C=d(1),U=d(""),B=d(""),$=d(!1),de=d([]),pe=d([]),ce=d("ips"),S=d(!1),R=d(!1),o=d({kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}),E=d("ip"),k=d(!1),A=d(""),K=d(""),_=d(null),h=d(""),Ie=["sql_injection","xss","path_traversal","command_injection","ssrf","scanner","bruteforce","csrf","xxe","file_upload"];function le(n){const e=Number(n);return Number.isFinite(e)?e:0}function N(n){const e=Number(n||0);return e>=80?{label:"高",type:"danger"}:e>=50?{label:"中",type:"warning"}:{label:"低",type:"success"}}function fe(n){const e=String(n||"").trim();return e||"永久"}function me(n){const e=[];return n?.field_name&&e.push(`字段: ${n.field_name}`),n?.rule&&e.push(`规则: ${n.rule}`),n?.matched&&e.push(`匹配: ${n.matched}`),n?.value_preview&&e.push(`值: ${n.value_preview}`),e.length?e.join(" · "):"-"}function O(n){const e=String(n?.request_method||"").trim(),s=String(n?.request_path||"").trim();return`${e} ${s}`.trim()||"-"}const Ve=oe(()=>{const n=new Set(Ie),e=j.value?.recent_threat_events||[];for(const s of e){const y=String(s?.threat_type||"").trim();y&&n.add(y)}for(const s of z.value||[]){const y=String(s?.threat_type||"").trim();y&&n.add(y)}return Array.from(n).sort((s,y)=>s.localeCompare(y)).map(s=>({label:s,value:s}))}),xe=oe(()=>{const n=j.value||{};return[{key:"threat_events_24h",label:"最近24小时威胁事件",value:le(n.threat_events_24h),tone:"red",hint:"用于衡量当前攻击面活跃度"},{key:"banned_ip_count",label:"当前封禁 IP 数",value:le(n.banned_ip_count),tone:"orange",hint:"自动与人工封禁总量"},{key:"banned_user_count",label:"当前封禁用户数",value:le(n.banned_user_count),tone:"purple",hint:"高风险账户拦截情况"}]}),Ce=oe(()=>Math.max(1,Math.ceil((q.value||0)/re)));async function D(){V.value=!0;try{j.value=await Ge()}catch{j.value=null}finally{V.value=!1}}async function G(){ae.value=!0;try{const n={page:C.value,per_page:re};U.value&&(n.event_type=U.value),B.value&&(n.severity=B.value);const e=await He(n);z.value=e?.items||[],q.value=e?.total||0}catch{z.value=[],q.value=0}finally{ae.value=!1}}async function F(){if(!$.value){$.value=!0;try{const[n,e]=await Promise.allSettled([Qe(),We()]);de.value=n.status==="fulfilled"?n.value?.items||[]:[],pe.value=e.status==="fulfilled"?e.value?.items||[]:[]}finally{$.value=!1}}}async function ve(){await Promise.allSettled([D(),G(),F()])}function Se(){C.value=1,G()}function Pe(){U.value="",B.value="",C.value=1,G()}function _e(){o.value={kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}}function H(n="ip",e={}){_e(),o.value.kind=n==="user"?"user":"ip",o.value.kind==="ip"?o.value.ip=String(e.ip||"").trim():o.value.user_id=String(e.user_id||"").trim(),e.reason&&(o.value.reason=String(e.reason||"").trim()),S.value=!0}async function Te(){const n=o.value.kind,e=String(o.value.reason||"").trim(),s=!!o.value.permanent,y=Number(o.value.duration_hours||24);if(!e){w.error("原因不能为空");return}if(n==="ip"){const I=String(o.value.ip||"").trim();if(!I){w.error("IP不能为空");return}R.value=!0;try{await Je({ip:I,reason:e,duration_hours:y,permanent:s}),w.success("IP已封禁"),S.value=!1,await Promise.allSettled([D(),F()])}catch{}finally{R.value=!1}return}const W=String(o.value.user_id||"").trim(),r=Number.parseInt(W,10);if(!Number.isFinite(r)){w.error("用户ID无效");return}R.value=!0;try{await Ye({user_id:r,reason:e,duration_hours:y,permanent:s}),w.success("用户已封禁"),S.value=!1,await Promise.allSettled([D(),F()])}catch{}finally{R.value=!1}}async function ye(n){const e=String(n||"").trim();if(e){try{await ee.confirm(`确定解除对 IP ${e} 的封禁吗?`,"解除封禁",{confirmButtonText:"解除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await Xe(e),w.success("已解除IP封禁"),await Promise.allSettled([D(),F()])}catch{}}}async function be(n){const e=Number.parseInt(String(n||"").trim(),10);if(Number.isFinite(e)){try{await ee.confirm(`确定解除对 用户ID ${e} 的封禁吗?`,"解除封禁",{confirmButtonText:"解除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await Ze(e),w.success("已解除用户封禁"),await Promise.allSettled([D(),F()])}catch{}}}function ge(n){const e=String(n||"").trim();e&&(m.value="risk",E.value="ip",A.value=e,Q())}function ke(n){const e=String(n||"").trim();e&&(m.value="risk",E.value="user",K.value=e,ne())}async function Q(){const n=String(A.value||"").trim();if(!n){w.error("请输入IP");return}k.value=!0;try{_.value=await et(n),h.value="ip"}catch{_.value=null,h.value=""}finally{k.value=!1}}async function ne(){const n=String(K.value||"").trim(),e=Number.parseInt(n,10);if(!Number.isFinite(e)){w.error("请输入有效的用户ID");return}k.value=!0;try{_.value=await at(e),h.value="user"}catch{_.value=null,h.value=""}finally{k.value=!1}}function Ue(){!_.value||!h.value||(h.value==="ip"?H("ip",{ip:_.value?.ip,reason:"风险查询手动封禁"}):H("user",{user_id:_.value?.user_id,reason:"风险查询手动封禁"}))}async function Be(){!_.value||!h.value||(h.value==="ip"?(await ye(_.value?.ip),await Q()):(await be(_.value?.user_id),await ne()))}async function $e(){if(h.value!=="ip")return;const n=String(_.value?.ip||"").trim();if(n){try{await ee.confirm(`确定清除 IP ${n} 的风险分吗?
清除风险分不会删除威胁历史,也不会解除封禁。`,"清除风险分",{confirmButtonText:"清除",cancelButtonText:"取消",type:"warning"})}catch{return}if(!k.value){k.value=!0;try{await tt(n),w.success("IP风险分已清零")}catch{}finally{k.value=!1}await Q()}}}const ie=d(!1);async function Re(){try{await ee.confirm(`确定清理过期封禁记录,并衰减风险分吗?
diff --git a/static/admin/assets/SettingsPage-Ct3Jh1K3.js b/static/admin/assets/SettingsPage-cWvS8Esv.js
similarity index 98%
rename from static/admin/assets/SettingsPage-Ct3Jh1K3.js
rename to static/admin/assets/SettingsPage-cWvS8Esv.js
index 72ddf0d..22eb087 100644
--- a/static/admin/assets/SettingsPage-Ct3Jh1K3.js
+++ b/static/admin/assets/SettingsPage-cWvS8Esv.js
@@ -1 +1 @@
-import{a as m,_ as h}from"./index-bXG7gEdM.js";import{f as u,E as x}from"./vendor-element-CJoVtPsD.js";import{r as p,aW as i,z as T,A as P,B as r,S as a,L as o,P as b}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function S(l){const{data:s}=await m.put("/admin/username",{new_username:l});return s}async function A(l){const{data:s}=await m.put("/admin/password",{new_password:l});return s}async function C(){const{data:l}=await m.post("/logout");return l}const E={class:"page-stack"},U={__name:"SettingsPage",setup(l){const s=p(""),d=p(""),n=p(!1);function k(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:e.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function f(){try{await C()}catch{}finally{window.location.href="/yuyx"}}async function B(){const t=s.value.trim();if(!t){u.error("请输入新用户名");return}try{await x.confirm(`确定将管理员用户名修改为「${t}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await S(t),u.success("用户名修改成功,请重新登录"),s.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function V(){const t=d.value;if(!t){u.error("请输入新密码");return}const e=k(t);if(!e.ok){u.error(e.message);return}try{await x.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await A(t),u.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(t,e)=>{const g=i("el-input"),v=i("el-form-item"),w=i("el-form"),y=i("el-button"),_=i("el-card");return P(),T("div",E,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新用户名"},{default:o(()=>[a(g,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=c=>s.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:B},{default:o(()=>[...e[2]||(e[2]=[b("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新密码"},{default:o(()=>[a(g,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:V},{default:o(()=>[...e[4]||(e[4]=[b("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1})])}}},W=h(U,[["__scopeId","data-v-83d3840a"]]);export{W as default};
+import{a as m,_ as h}from"./index-ZQnpYe51.js";import{f as u,E as x}from"./vendor-element-CJoVtPsD.js";import{r as p,aW as i,z as T,A as P,B as r,S as a,L as o,P as b}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function S(l){const{data:s}=await m.put("/admin/username",{new_username:l});return s}async function A(l){const{data:s}=await m.put("/admin/password",{new_password:l});return s}async function C(){const{data:l}=await m.post("/logout");return l}const E={class:"page-stack"},U={__name:"SettingsPage",setup(l){const s=p(""),d=p(""),n=p(!1);function k(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:e.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function f(){try{await C()}catch{}finally{window.location.href="/yuyx"}}async function B(){const t=s.value.trim();if(!t){u.error("请输入新用户名");return}try{await x.confirm(`确定将管理员用户名修改为「${t}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await S(t),u.success("用户名修改成功,请重新登录"),s.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function V(){const t=d.value;if(!t){u.error("请输入新密码");return}const e=k(t);if(!e.ok){u.error(e.message);return}try{await x.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await A(t),u.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(t,e)=>{const g=i("el-input"),v=i("el-form-item"),w=i("el-form"),y=i("el-button"),_=i("el-card");return P(),T("div",E,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新用户名"},{default:o(()=>[a(g,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=c=>s.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:B},{default:o(()=>[...e[2]||(e[2]=[b("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新密码"},{default:o(()=>[a(g,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:V},{default:o(()=>[...e[4]||(e[4]=[b("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1})])}}},W=h(U,[["__scopeId","data-v-83d3840a"]]);export{W as default};
diff --git a/static/admin/assets/SystemPage-DE9Z1xG-.js b/static/admin/assets/SystemPage-BdmQOog3.js
similarity index 99%
rename from static/admin/assets/SystemPage-DE9Z1xG-.js
rename to static/admin/assets/SystemPage-BdmQOog3.js
index 91eda69..97b4eff 100644
--- a/static/admin/assets/SystemPage-DE9Z1xG-.js
+++ b/static/admin/assets/SystemPage-BdmQOog3.js
@@ -1,4 +1,4 @@
-import{f as Ve,u as Y}from"./system-1p1K-j-4.js";import{a as P,_ as ge}from"./index-bXG7gEdM.js";import{E as ne,f as m}from"./vendor-element-CJoVtPsD.js";import{r as n,c as ke,w as xe,a1 as be,v as we,aW as v,b7 as Ue,M as Ce,A as V,z as g,B as s,S as l,L as t,P as k,N as Z,Q as ee}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function le(r={}){const{data:c}=await P.get("/kdocs/status",{params:r});return c}async function Pe(r={}){const c={force:!0,...r},{data:x}=await P.post("/kdocs/qr",c);return x}async function Ie(){const{data:r}=await P.post("/kdocs/clear-login",{});return r}async function Ae(){const{data:r}=await P.get("/proxy/config");return r}async function Ne(r){const{data:c}=await P.post("/proxy/config",r);return c}async function Se(r){const{data:c}=await P.post("/proxy/test",r);return c}const De={class:"page-stack"},Ke={class:"config-grid"},Ee={class:"row-actions"},Be={class:"row-actions"},Te={class:"row-actions"},Le={class:"section-head"},Qe={class:"status-inline app-muted"},$e={key:0},he={key:1},qe={key:2},Me={class:"kdocs-inline"},Re={class:"kdocs-range"},ze={class:"row-actions"},Fe={key:0,class:"help"},He={key:1,class:"help"},Oe={class:"kdocs-qr"},Ge=["src"],We={__name:"SystemPage",setup(r){const c=n(!1),x=n(2),A=n(1),N=n(3),I=n(!1),f=n(""),S=n(3),D=n(!1),K=n(10),E=n(7),B=n(!1),T=n(""),L=n(""),Q=n(""),$=n(0),h=n("A"),q=n("D"),M=n(0),R=n(0),z=n(!1),F=n(""),p=n({}),b=n(!1),w=n(""),ae=n(!1),H=n(!1),U=n(!1),C=n(!1),O=n("");let G=null;const oe=ke(()=>H.value||U.value||C.value);function d(a){if(!a){O.value="";return}const e=new Date().toLocaleTimeString("zh-CN",{hour12:!1});O.value=`${a} (${e})`}async function ue(){c.value=!0;try{const[a,e,i]=await Promise.all([Ve(),Ae(),le().catch(()=>({}))]);x.value=a.max_concurrent_global??2,A.value=a.max_concurrent_per_account??1,N.value=a.max_screenshot_concurrent??3,D.value=(a.auto_approve_enabled??0)===1,K.value=a.auto_approve_hourly_limit??10,E.value=a.auto_approve_vip_days??7,I.value=(e.proxy_enabled??0)===1,f.value=e.proxy_api_url||"",S.value=e.proxy_expire_minutes??3,B.value=(a.kdocs_enabled??0)===1,T.value=a.kdocs_doc_url||"",L.value=a.kdocs_default_unit||"",Q.value=a.kdocs_sheet_name||"",$.value=a.kdocs_sheet_index??0,h.value=(a.kdocs_unit_column||"A").toUpperCase(),q.value=(a.kdocs_image_column||"D").toUpperCase(),M.value=a.kdocs_row_start??0,R.value=a.kdocs_row_end??0,z.value=(a.kdocs_admin_notify_enabled??0)===1,F.value=a.kdocs_admin_notify_email||"",p.value=i||{}}catch{}finally{c.value=!1}}async function de(){const a={max_concurrent_global:Number(x.value),max_concurrent_per_account:Number(A.value),max_screenshot_concurrent:Number(N.value)};try{await ne.confirm(`确定更新并发配置吗?
+import{f as Ve,u as Y}from"./system-DO-Wv0rM.js";import{a as P,_ as ge}from"./index-ZQnpYe51.js";import{E as ne,f as m}from"./vendor-element-CJoVtPsD.js";import{r as n,c as ke,w as xe,a1 as be,v as we,aW as v,b7 as Ue,M as Ce,A as V,z as g,B as s,S as l,L as t,P as k,N as Z,Q as ee}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function le(r={}){const{data:c}=await P.get("/kdocs/status",{params:r});return c}async function Pe(r={}){const c={force:!0,...r},{data:x}=await P.post("/kdocs/qr",c);return x}async function Ie(){const{data:r}=await P.post("/kdocs/clear-login",{});return r}async function Ae(){const{data:r}=await P.get("/proxy/config");return r}async function Ne(r){const{data:c}=await P.post("/proxy/config",r);return c}async function Se(r){const{data:c}=await P.post("/proxy/test",r);return c}const De={class:"page-stack"},Ke={class:"config-grid"},Ee={class:"row-actions"},Be={class:"row-actions"},Te={class:"row-actions"},Le={class:"section-head"},Qe={class:"status-inline app-muted"},$e={key:0},he={key:1},qe={key:2},Me={class:"kdocs-inline"},Re={class:"kdocs-range"},ze={class:"row-actions"},Fe={key:0,class:"help"},He={key:1,class:"help"},Oe={class:"kdocs-qr"},Ge=["src"],We={__name:"SystemPage",setup(r){const c=n(!1),x=n(2),A=n(1),N=n(3),I=n(!1),f=n(""),S=n(3),D=n(!1),K=n(10),E=n(7),B=n(!1),T=n(""),L=n(""),Q=n(""),$=n(0),h=n("A"),q=n("D"),M=n(0),R=n(0),z=n(!1),F=n(""),p=n({}),b=n(!1),w=n(""),ae=n(!1),H=n(!1),U=n(!1),C=n(!1),O=n("");let G=null;const oe=ke(()=>H.value||U.value||C.value);function d(a){if(!a){O.value="";return}const e=new Date().toLocaleTimeString("zh-CN",{hour12:!1});O.value=`${a} (${e})`}async function ue(){c.value=!0;try{const[a,e,i]=await Promise.all([Ve(),Ae(),le().catch(()=>({}))]);x.value=a.max_concurrent_global??2,A.value=a.max_concurrent_per_account??1,N.value=a.max_screenshot_concurrent??3,D.value=(a.auto_approve_enabled??0)===1,K.value=a.auto_approve_hourly_limit??10,E.value=a.auto_approve_vip_days??7,I.value=(e.proxy_enabled??0)===1,f.value=e.proxy_api_url||"",S.value=e.proxy_expire_minutes??3,B.value=(a.kdocs_enabled??0)===1,T.value=a.kdocs_doc_url||"",L.value=a.kdocs_default_unit||"",Q.value=a.kdocs_sheet_name||"",$.value=a.kdocs_sheet_index??0,h.value=(a.kdocs_unit_column||"A").toUpperCase(),q.value=(a.kdocs_image_column||"D").toUpperCase(),M.value=a.kdocs_row_start??0,R.value=a.kdocs_row_end??0,z.value=(a.kdocs_admin_notify_enabled??0)===1,F.value=a.kdocs_admin_notify_email||"",p.value=i||{}}catch{}finally{c.value=!1}}async function de(){const a={max_concurrent_global:Number(x.value),max_concurrent_per_account:Number(A.value),max_screenshot_concurrent:Number(N.value)};try{await ne.confirm(`确定更新并发配置吗?
全局并发数: ${a.max_concurrent_global}
单账号并发数: ${a.max_concurrent_per_account}
diff --git a/static/admin/assets/UsersPage-BqAhpZky.js b/static/admin/assets/UsersPage-CZk3ZLBk.js
similarity index 99%
rename from static/admin/assets/UsersPage-BqAhpZky.js
rename to static/admin/assets/UsersPage-CZk3ZLBk.js
index 615bbb4..05a977a 100644
--- a/static/admin/assets/UsersPage-BqAhpZky.js
+++ b/static/admin/assets/UsersPage-CZk3ZLBk.js
@@ -1 +1 @@
-import{a as Z,r as q,s as K,b as Q,c as F,d as G,f as H}from"./users-kt1Evatk.js";import{_ as J}from"./index-bXG7gEdM.js";import{E as v,f as h}from"./vendor-element-CJoVtPsD.js";import{i as O,r as P,v as X,aW as _,b7 as Y,z as V,A as r,B as y,S as o,L as s,M as ee,K as f,N as m,Q as b,P as c}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";function I(g){if(!g)return null;if(g instanceof Date)return g;let a=String(g).trim();if(!a)return null;/^\d{4}-\d{2}-\d{2}$/.test(a)&&(a=`${a}T00:00:00`);let l=a.includes("T")?a:a.replace(" ","T");l=l.replace(/\.(\d{3})\d+/,".$1"),/([zZ]|[+-]\d{2}:\d{2})$/.test(l)||(l=`${l}+08:00`);const u=new Date(l);return Number.isNaN(u.getTime())?null:u}function D(g){const a=String(g||"");if(!a)return{ok:!1,message:"密码不能为空"};if(a.length<8)return{ok:!1,message:"密码长度不能少于8个字符"};if(a.length>128)return{ok:!1,message:"密码长度不能超过128个字符"};const l=/[a-zA-Z]/.test(a),x=/\d/.test(a);return!l||!x?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}const te={class:"page-stack"},ne={class:"table-wrap"},se={class:"user-block"},ae={class:"user-main"},ie={key:0,class:"app-muted user-sub"},re={key:1,class:"vip-sub"},le={key:0,class:"app-muted"},oe={class:"actions"},ce={__name:"UsersPage",setup(g){const a=O("refreshStats",null),l=P(!1),x=P([]);function u(n){const e=n?.vip_expire_time;if(!e)return!1;if(String(e).startsWith("2099-12-31"))return!0;const i=I(e);return i?i.getTime()>Date.now():!1}function C(n){const e=n?.vip_expire_time;if(!e||!u(n))return"";if(String(e).startsWith("2099-12-31"))return"永久VIP";const i=I(e);if(!i)return`到期: ${e}`;const d=Math.ceil((i.getTime()-Date.now())/(1e3*60*60*24));return`到期: ${e}(剩${d}天)`}function B(n){return n==="rejected"?{label:"禁用",type:"danger"}:{label:"正常",type:"success"}}async function w(){l.value=!0;try{x.value=await H()}catch{x.value=[]}finally{l.value=!1}}async function U(){await w()}async function z(n){try{await v.confirm(`确定启用用户「${n.username}」吗?启用后用户可正常登录。`,"启用用户",{confirmButtonText:"启用",cancelButtonText:"取消",type:"success"})}catch{return}try{await Z(n.id),h.success("用户已启用"),await w(),await a?.()}catch{}}async function S(n){try{await v.confirm(`确定禁用用户「${n.username}」吗?禁用后用户将无法登录。`,"禁用用户",{confirmButtonText:"禁用",cancelButtonText:"取消",type:"warning"})}catch{return}try{await q(n.id),h.success("用户已禁用"),await w(),await a?.()}catch{}}async function N(n){try{await v.confirm(`确定删除用户「${n.username}」吗?此操作将删除该用户的所有数据,不可恢复!`,"删除用户",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{await G(n.id),h.success("用户已删除"),await w(),await a?.()}catch{}}async function $(n,e){const i={7:"一周",30:"一个月",365:"一年",999999:"永久"}[e]||`${e}天`;try{await v.confirm(`确定为用户「${n.username}」开通 ${i} VIP 吗?`,"设置VIP",{confirmButtonText:"确认",cancelButtonText:"取消",type:"warning"})}catch{return}try{const d=await K(n.id,e);h.success(d?.message||"VIP设置成功"),await w(),await a?.()}catch{}}async function M(n){try{await v.confirm(`确定移除用户「${n.username}」的 VIP 吗?`,"移除VIP",{confirmButtonText:"移除",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await Q(n.id);h.success(e?.message||"VIP已移除"),await w(),await a?.()}catch{}}async function E(n){let e;try{e=(await v.prompt("请输入新密码(至少8位且包含字母和数字)","重置密码",{confirmButtonText:"提交",cancelButtonText:"取消",inputType:"password",inputPlaceholder:"新密码",inputValidator:k=>D(k).ok,inputErrorMessage:"密码至少8位且包含字母和数字"})).value}catch{return}const i=D(e);if(!i.ok){h.error(i.message);return}try{await v.confirm(`确定将用户「${n.username}」的密码重置为该新密码吗?`,"二次确认",{confirmButtonText:"确认重置",cancelButtonText:"取消",type:"warning"})}catch{return}try{const d=await F(n.id,e);h.success(d?.message||"密码重置成功")}catch{}}return X(U),(n,e)=>{const i=_("el-table-column"),d=_("el-tag"),k=_("el-button"),T=_("el-dropdown-item"),j=_("el-dropdown-menu"),A=_("el-dropdown"),L=_("el-table"),R=_("el-card"),W=Y("loading");return r(),V("div",te,[e[11]||(e[11]=y("div",{class:"app-page-title"},[y("h2",null,"用户")],-1)),o(R,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:s(()=>[y("div",ne,[ee((r(),f(L,{data:x.value,style:{width:"100%"}},{default:s(()=>[o(i,{prop:"id",label:"ID",width:"80"}),o(i,{label:"用户","min-width":"240"},{default:s(({row:t})=>[y("div",se,[y("div",ae,[y("strong",null,b(t.username),1),u(t)?(r(),f(d,{key:0,type:"warning",effect:"light",size:"small"},{default:s(()=>[...e[0]||(e[0]=[c("VIP",-1)])]),_:1})):m("",!0)]),t.email?(r(),V("div",ie,b(t.email),1)):m("",!0),C(t)?(r(),V("div",re,b(C(t)),1)):m("",!0)])]),_:1}),o(i,{label:"状态",width:"120"},{default:s(({row:t})=>[o(d,{type:B(t.status).type,effect:"light"},{default:s(()=>[c(b(B(t.status).label),1)]),_:2},1032,["type"])]),_:1}),o(i,{label:"时间","min-width":"220"},{default:s(({row:t})=>[y("div",null,b(t.created_at),1),t.vip_expire_time?(r(),V("div",le,"VIP到期: "+b(t.vip_expire_time),1)):m("",!0)]),_:1}),o(i,{label:"操作",width:"280",fixed:"right"},{default:s(({row:t})=>[y("div",oe,[t.status==="rejected"?(r(),f(k,{key:0,type:"success",size:"small",onClick:p=>z(t)},{default:s(()=>[...e[1]||(e[1]=[c("启用",-1)])]),_:1},8,["onClick"])):(r(),f(k,{key:1,type:"warning",size:"small",onClick:p=>S(t)},{default:s(()=>[...e[2]||(e[2]=[c("禁用",-1)])]),_:1},8,["onClick"])),o(A,{trigger:"click"},{dropdown:s(()=>[o(j,null,{default:s(()=>[u(t)?m("",!0):(r(),f(T,{key:0,onClick:p=>$(t,7)},{default:s(()=>[...e[4]||(e[4]=[c("开通一周",-1)])]),_:1},8,["onClick"])),u(t)?m("",!0):(r(),f(T,{key:1,onClick:p=>$(t,30)},{default:s(()=>[...e[5]||(e[5]=[c("开通一月",-1)])]),_:1},8,["onClick"])),u(t)?m("",!0):(r(),f(T,{key:2,onClick:p=>$(t,365)},{default:s(()=>[...e[6]||(e[6]=[c("开通一年",-1)])]),_:1},8,["onClick"])),u(t)?m("",!0):(r(),f(T,{key:3,onClick:p=>$(t,999999)},{default:s(()=>[...e[7]||(e[7]=[c("永久VIP",-1)])]),_:1},8,["onClick"])),u(t)?(r(),f(T,{key:4,onClick:p=>M(t)},{default:s(()=>[...e[8]||(e[8]=[c("移除VIP",-1)])]),_:1},8,["onClick"])):m("",!0)]),_:2},1024)]),default:s(()=>[o(k,{size:"small"},{default:s(()=>[...e[3]||(e[3]=[c("VIP",-1)])]),_:1})]),_:2},1024),o(k,{size:"small",onClick:p=>E(t)},{default:s(()=>[...e[9]||(e[9]=[c("重置密码",-1)])]),_:1},8,["onClick"]),o(k,{type:"danger",size:"small",onClick:p=>N(t)},{default:s(()=>[...e[10]||(e[10]=[c("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[W,l.value]])])]),_:1})])}}},ye=J(ce,[["__scopeId","data-v-9e917879"]]);export{ye as default};
+import{a as Z,r as q,s as K,b as Q,c as F,d as G,f as H}from"./users-Czwk10aS.js";import{_ as J}from"./index-ZQnpYe51.js";import{E as v,f as h}from"./vendor-element-CJoVtPsD.js";import{i as O,r as P,v as X,aW as _,b7 as Y,z as V,A as r,B as y,S as o,L as s,M as ee,K as f,N as m,Q as b,P as c}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";function I(g){if(!g)return null;if(g instanceof Date)return g;let a=String(g).trim();if(!a)return null;/^\d{4}-\d{2}-\d{2}$/.test(a)&&(a=`${a}T00:00:00`);let l=a.includes("T")?a:a.replace(" ","T");l=l.replace(/\.(\d{3})\d+/,".$1"),/([zZ]|[+-]\d{2}:\d{2})$/.test(l)||(l=`${l}+08:00`);const u=new Date(l);return Number.isNaN(u.getTime())?null:u}function D(g){const a=String(g||"");if(!a)return{ok:!1,message:"密码不能为空"};if(a.length<8)return{ok:!1,message:"密码长度不能少于8个字符"};if(a.length>128)return{ok:!1,message:"密码长度不能超过128个字符"};const l=/[a-zA-Z]/.test(a),x=/\d/.test(a);return!l||!x?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}const te={class:"page-stack"},ne={class:"table-wrap"},se={class:"user-block"},ae={class:"user-main"},ie={key:0,class:"app-muted user-sub"},re={key:1,class:"vip-sub"},le={key:0,class:"app-muted"},oe={class:"actions"},ce={__name:"UsersPage",setup(g){const a=O("refreshStats",null),l=P(!1),x=P([]);function u(n){const e=n?.vip_expire_time;if(!e)return!1;if(String(e).startsWith("2099-12-31"))return!0;const i=I(e);return i?i.getTime()>Date.now():!1}function C(n){const e=n?.vip_expire_time;if(!e||!u(n))return"";if(String(e).startsWith("2099-12-31"))return"永久VIP";const i=I(e);if(!i)return`到期: ${e}`;const d=Math.ceil((i.getTime()-Date.now())/(1e3*60*60*24));return`到期: ${e}(剩${d}天)`}function B(n){return n==="rejected"?{label:"禁用",type:"danger"}:{label:"正常",type:"success"}}async function w(){l.value=!0;try{x.value=await H()}catch{x.value=[]}finally{l.value=!1}}async function U(){await w()}async function z(n){try{await v.confirm(`确定启用用户「${n.username}」吗?启用后用户可正常登录。`,"启用用户",{confirmButtonText:"启用",cancelButtonText:"取消",type:"success"})}catch{return}try{await Z(n.id),h.success("用户已启用"),await w(),await a?.()}catch{}}async function S(n){try{await v.confirm(`确定禁用用户「${n.username}」吗?禁用后用户将无法登录。`,"禁用用户",{confirmButtonText:"禁用",cancelButtonText:"取消",type:"warning"})}catch{return}try{await q(n.id),h.success("用户已禁用"),await w(),await a?.()}catch{}}async function N(n){try{await v.confirm(`确定删除用户「${n.username}」吗?此操作将删除该用户的所有数据,不可恢复!`,"删除用户",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{await G(n.id),h.success("用户已删除"),await w(),await a?.()}catch{}}async function $(n,e){const i={7:"一周",30:"一个月",365:"一年",999999:"永久"}[e]||`${e}天`;try{await v.confirm(`确定为用户「${n.username}」开通 ${i} VIP 吗?`,"设置VIP",{confirmButtonText:"确认",cancelButtonText:"取消",type:"warning"})}catch{return}try{const d=await K(n.id,e);h.success(d?.message||"VIP设置成功"),await w(),await a?.()}catch{}}async function M(n){try{await v.confirm(`确定移除用户「${n.username}」的 VIP 吗?`,"移除VIP",{confirmButtonText:"移除",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await Q(n.id);h.success(e?.message||"VIP已移除"),await w(),await a?.()}catch{}}async function E(n){let e;try{e=(await v.prompt("请输入新密码(至少8位且包含字母和数字)","重置密码",{confirmButtonText:"提交",cancelButtonText:"取消",inputType:"password",inputPlaceholder:"新密码",inputValidator:k=>D(k).ok,inputErrorMessage:"密码至少8位且包含字母和数字"})).value}catch{return}const i=D(e);if(!i.ok){h.error(i.message);return}try{await v.confirm(`确定将用户「${n.username}」的密码重置为该新密码吗?`,"二次确认",{confirmButtonText:"确认重置",cancelButtonText:"取消",type:"warning"})}catch{return}try{const d=await F(n.id,e);h.success(d?.message||"密码重置成功")}catch{}}return X(U),(n,e)=>{const i=_("el-table-column"),d=_("el-tag"),k=_("el-button"),T=_("el-dropdown-item"),j=_("el-dropdown-menu"),A=_("el-dropdown"),L=_("el-table"),R=_("el-card"),W=Y("loading");return r(),V("div",te,[e[11]||(e[11]=y("div",{class:"app-page-title"},[y("h2",null,"用户")],-1)),o(R,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:s(()=>[y("div",ne,[ee((r(),f(L,{data:x.value,style:{width:"100%"}},{default:s(()=>[o(i,{prop:"id",label:"ID",width:"80"}),o(i,{label:"用户","min-width":"240"},{default:s(({row:t})=>[y("div",se,[y("div",ae,[y("strong",null,b(t.username),1),u(t)?(r(),f(d,{key:0,type:"warning",effect:"light",size:"small"},{default:s(()=>[...e[0]||(e[0]=[c("VIP",-1)])]),_:1})):m("",!0)]),t.email?(r(),V("div",ie,b(t.email),1)):m("",!0),C(t)?(r(),V("div",re,b(C(t)),1)):m("",!0)])]),_:1}),o(i,{label:"状态",width:"120"},{default:s(({row:t})=>[o(d,{type:B(t.status).type,effect:"light"},{default:s(()=>[c(b(B(t.status).label),1)]),_:2},1032,["type"])]),_:1}),o(i,{label:"时间","min-width":"220"},{default:s(({row:t})=>[y("div",null,b(t.created_at),1),t.vip_expire_time?(r(),V("div",le,"VIP到期: "+b(t.vip_expire_time),1)):m("",!0)]),_:1}),o(i,{label:"操作",width:"280",fixed:"right"},{default:s(({row:t})=>[y("div",oe,[t.status==="rejected"?(r(),f(k,{key:0,type:"success",size:"small",onClick:p=>z(t)},{default:s(()=>[...e[1]||(e[1]=[c("启用",-1)])]),_:1},8,["onClick"])):(r(),f(k,{key:1,type:"warning",size:"small",onClick:p=>S(t)},{default:s(()=>[...e[2]||(e[2]=[c("禁用",-1)])]),_:1},8,["onClick"])),o(A,{trigger:"click"},{dropdown:s(()=>[o(j,null,{default:s(()=>[u(t)?m("",!0):(r(),f(T,{key:0,onClick:p=>$(t,7)},{default:s(()=>[...e[4]||(e[4]=[c("开通一周",-1)])]),_:1},8,["onClick"])),u(t)?m("",!0):(r(),f(T,{key:1,onClick:p=>$(t,30)},{default:s(()=>[...e[5]||(e[5]=[c("开通一月",-1)])]),_:1},8,["onClick"])),u(t)?m("",!0):(r(),f(T,{key:2,onClick:p=>$(t,365)},{default:s(()=>[...e[6]||(e[6]=[c("开通一年",-1)])]),_:1},8,["onClick"])),u(t)?m("",!0):(r(),f(T,{key:3,onClick:p=>$(t,999999)},{default:s(()=>[...e[7]||(e[7]=[c("永久VIP",-1)])]),_:1},8,["onClick"])),u(t)?(r(),f(T,{key:4,onClick:p=>M(t)},{default:s(()=>[...e[8]||(e[8]=[c("移除VIP",-1)])]),_:1},8,["onClick"])):m("",!0)]),_:2},1024)]),default:s(()=>[o(k,{size:"small"},{default:s(()=>[...e[3]||(e[3]=[c("VIP",-1)])]),_:1})]),_:2},1024),o(k,{size:"small",onClick:p=>E(t)},{default:s(()=>[...e[9]||(e[9]=[c("重置密码",-1)])]),_:1},8,["onClick"]),o(k,{type:"danger",size:"small",onClick:p=>N(t)},{default:s(()=>[...e[10]||(e[10]=[c("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[W,l.value]])])]),_:1})])}}},ye=J(ce,[["__scopeId","data-v-9e917879"]]);export{ye as default};
diff --git a/static/admin/assets/email-DgztAWKJ.js b/static/admin/assets/email-D8pS1Pbx.js
similarity index 86%
rename from static/admin/assets/email-DgztAWKJ.js
rename to static/admin/assets/email-D8pS1Pbx.js
index c4257e9..0f82ec3 100644
--- a/static/admin/assets/email-DgztAWKJ.js
+++ b/static/admin/assets/email-D8pS1Pbx.js
@@ -1 +1 @@
-import{a as n}from"./index-bXG7gEdM.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};
+import{a as n}from"./index-ZQnpYe51.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};
diff --git a/static/admin/assets/index-bXG7gEdM.js b/static/admin/assets/index-ZQnpYe51.js
similarity index 88%
rename from static/admin/assets/index-bXG7gEdM.js
rename to static/admin/assets/index-ZQnpYe51.js
index c9bc71d..e032e71 100644
--- a/static/admin/assets/index-bXG7gEdM.js
+++ b/static/admin/assets/index-ZQnpYe51.js
@@ -1,2 +1,2 @@
-const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./ReportPage-BITaX3hh.js","./vendor-element-CJoVtPsD.js","./vendor-sLgkZK1v.js","./vendor-vue-CWkOjFoA.js","./vendor-element-C68yOrAy.css","./email-DgztAWKJ.js","./tasks-Y3hOqi3L.js","./system-1p1K-j-4.js","./MetricGrid-CPdC57ab.js","./MetricGrid-yP_dkP6X.css","./vendor-axios-B9ygI19o.js","./ReportPage-21i0jfUn.css","./UsersPage-BqAhpZky.js","./users-kt1Evatk.js","./UsersPage-Cow_LicY.css","./FeedbacksPage-BXNEKJpt.js","./FeedbacksPage-mrXjCiV2.css","./LogsPage-xSURzHTt.js","./LogsPage-D1bozCEo.css","./AnnouncementsPage-fRPrWBwG.js","./AnnouncementsPage-DOwZaaOu.css","./EmailPage-CFpw2E0J.js","./EmailPage-BmPCDPYC.css","./SecurityPage-CQuWzZ_p.js","./SecurityPage-DN76ndc_.css","./SystemPage-DE9Z1xG-.js","./SystemPage-DMyNBO3N.css","./SettingsPage-Ct3Jh1K3.js","./SettingsPage-DaB8PeRL.css"])))=>i.map(i=>d[i]);
-import{aW as m,K as w,A as _,r as O,c as H,v as me,a1 as fe,L as o,N as W,S as d,B as u,z as V,R as z,ak as Y,O as Q,Q as L,P as X,by as pe,p as D,bu as _e}from"./vendor-sLgkZK1v.js";import{u as he,p as ye,q as ge,r as be}from"./vendor-vue-CWkOjFoA.js";import{E as te,f as ne,g as ve,u as we,h as Ee,j as ke,k as Pe,m as Re,n as Te,t as Se,o as Oe,p as Le,z as Ae}from"./vendor-element-CJoVtPsD.js";import{a as xe}from"./vendor-axios-B9ygI19o.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))a(s);new MutationObserver(s=>{for(const l of s)if(l.type==="childList")for(const i of l.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&a(i)}).observe(document,{childList:!0,subtree:!0});function n(s){const l={};return s.integrity&&(l.integrity=s.integrity),s.referrerPolicy&&(l.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?l.credentials="include":s.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function a(s){if(s.ep)return;s.ep=!0;const l=n(s);fetch(s.href,l)}})();const ae=(e,t)=>{const n=e.__vccOpts||e;for(const[a,s]of t)n[a]=s;return n},Ne={};function Be(e,t){const n=m("RouterView");return _(),w(n)}const Ve=ae(Ne,[["render",Be]]),Ce="modulepreload",De=function(e,t){return new URL(e,t).href},J={},E=function(t,n,a){let s=Promise.resolve();if(n&&n.length>0){let T=function(p){return Promise.all(p.map(b=>Promise.resolve(b).then(P=>({status:"fulfilled",value:P}),P=>({status:"rejected",reason:P}))))};const i=document.getElementsByTagName("link"),f=document.querySelector("meta[property=csp-nonce]"),k=f?.nonce||f?.getAttribute("nonce");s=T(n.map(p=>{if(p=De(p,a),p in J)return;J[p]=!0;const b=p.endsWith(".css"),P=b?'[rel="stylesheet"]':"";if(a)for(let R=i.length-1;R>=0;R--){const v=i[R];if(v.href===p&&(!b||v.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${p}"]${P}`))return;const h=document.createElement("link");if(h.rel=b?"stylesheet":Ce,b||(h.as="script"),h.crossOrigin="",h.href=p,k&&h.setAttribute("nonce",k),document.head.appendChild(h),b)return new Promise((R,v)=>{h.addEventListener("load",R),h.addEventListener("error",()=>v(new Error(`Unable to preload CSS for ${p}`)))})}))}function l(i){const f=new Event("vite:preloadError",{cancelable:!0});if(f.payload=i,window.dispatchEvent(f),!f.defaultPrevented)throw i}return s.then(i=>{for(const f of i||[])f.status==="rejected"&&l(f.reason);return t().catch(l)})};let Z="",ee=0;const Ie=new Set([408,425,429,500,502,503,504]),Me=1,$e=300;function A(e,t,n=1500){const a=Date.now();e===Z&&a-ee=Me)return!1;const a=String(e?.code||"");if(a==="ECONNABORTED"||a==="ERR_NETWORK")return!0;const s=Number(e?.response?.status||0);return Ie.has(s)}function je(e){return new Promise(t=>{window.setTimeout(t,Math.max(0,Number(e||0)))})}async function Ke(e,t){const n=e?.config||{},a=Number(n.__retry_count||0);n.__retry_count=a+1;const s=$e*(a+1);return await je(s),t.request(n)}const y=xe.create({baseURL:"/yuyx/api",timeout:3e4,withCredentials:!0});let x=null;async function Ge(){return x||(x=te.prompt("请输入管理员密码进行二次确认","安全确认",{inputType:"password",inputPlaceholder:"管理员密码",confirmButtonText:"确认",cancelButtonText:"取消",inputValidator:e=>!!String(e||"").trim(),inputErrorMessage:"密码不能为空"}).then(async e=>{const t=String(e.value||"").trim();await y.post("/admin/reauth",{password:t}),ne.success("已通过安全确认")}).finally(()=>{x=null}),x)}y.interceptors.request.use(e=>{const t=String(e?.method||"GET").toUpperCase();if(!["GET","HEAD","OPTIONS"].includes(t)){const n=Fe("csrf_token");n&&(e.headers=e.headers||{},e.headers["X-CSRF-Token"]=n)}return e});y.interceptors.response.use(e=>e,async e=>{const t=e?.response?.status,n=e?.response?.data,a=n?.error||n?.message||e?.message||"请求失败";if(n?.code==="reauth_required"&&e?.config&&!e.config.__reauth_retry)try{return e.config.__reauth_retry=!0,await Ge(),y.request(e.config)}catch{return Promise.reject(e)}return qe(e)?Ke(e,y):(t===401?(A("401",a,3e3),(window.location?.pathname||"").startsWith("/yuyx")||(window.location.href="/yuyx")):t===403?A("403",a,5e3):t?A(`http:${t}:${a}`,a):e?.code==="ECONNABORTED"?A("timeout","请求超时",3e3):A(`net:${a}`,a,3e3),Promise.reject(e))});async function wt(e=""){const{data:t}=await y.get("/feedbacks",{params:e?{status:e}:{}});return t}async function He(){const{data:e}=await y.get("/feedbacks",{params:{limit:1,offset:0}});return e?.stats}async function Et(e,t){const{data:n}=await y.post(`/feedbacks/${e}/reply`,{reply:t});return n}async function kt(e){const{data:t}=await y.post(`/feedbacks/${e}/close`);return t}async function Pt(e){const{data:t}=await y.delete(`/feedbacks/${e}`);return t}async function We(){const{data:e}=await y.get("/stats");return e}const ze={class:"menu-label"},Ye={key:1,class:"menu-label"},Qe={class:"header-left"},Xe={class:"header-right"},Je={class:"admin-name"},Ze={class:"main-shell"},et={class:"menu-label"},tt={key:1,class:"menu-label"},nt=6e4,at=18e4,st={__name:"AdminLayout",setup(e){const t=he(),n=ye(),a=O({}),s=H(()=>a.value?.admin_username||"");async function l(){try{a.value=await We()}finally{}}const i=O(!1),f=O(0);let k=null;async function T(g=null){if(g&&typeof g=="object"){Object.prototype.hasOwnProperty.call(g,"pendingFeedbacks")&&(f.value=Number(g.pendingFeedbacks||0));return}if(!i.value){i.value=!0;try{const r=await He();f.value=Number(r?.pending||0)}finally{i.value=!1}}}function p(){return typeof document>"u"?!1:document.visibilityState==="hidden"}function b(){return p()?at:nt}function P(){k&&(window.clearTimeout(k),k=null)}function h(){P(),k=window.setTimeout(async()=>{k=null,await T().catch(()=>{}),h()},b())}function R(){h()}D("refreshStats",l),D("adminStats",a),D("refreshNavBadges",T);const v=O(!1),S=O(!1);let N;function C(){v.value=!!N?.matches,v.value||(S.value=!1)}me(async()=>{N=window.matchMedia("(max-width: 768px)"),N.addEventListener?.("change",C),C(),await l(),await T(),h(),window.addEventListener("visibilitychange",R)}),fe(()=>{N?.removeEventListener?.("change",C),P(),window.removeEventListener("visibilitychange",R)});const I=[{path:"/reports",label:"报表",icon:ve},{path:"/users",label:"用户",icon:we},{path:"/feedbacks",label:"反馈",icon:Ee,badgeKey:"feedbacks"},{path:"/logs",label:"任务日志",icon:ke},{path:"/announcements",label:"公告",icon:Pe},{path:"/email",label:"邮件",icon:Re},{path:"/security",label:"安全防护",icon:Te},{path:"/system",label:"系统配置",icon:Se},{path:"/settings",label:"设置",icon:Oe}],M=H(()=>t.path);function B(g){return g?.badgeKey&&g.badgeKey==="feedbacks"?Number(f.value||0):0}async function se(){try{await te.confirm("确定退出管理员登录吗?","退出登录",{confirmButtonText:"退出",cancelButtonText:"取消",type:"warning"})}catch{return}try{await y.post("/logout")}finally{window.location.href="/yuyx"}}async function $(g){await n.push(g),S.value=!1}return(g,r)=>{const F=m("el-icon"),U=m("el-badge"),q=m("el-menu-item"),j=m("el-menu"),oe=m("el-aside"),K=m("el-button"),re=m("el-header"),ce=m("RouterView"),le=m("el-skeleton"),ie=m("el-card"),ue=m("el-main"),G=m("el-container"),de=m("el-drawer");return _(),w(G,{class:"layout-root"},{default:o(()=>[v.value?W("",!0):(_(),w(oe,{key:0,width:"220px",class:"layout-aside"},{default:o(()=>[r[2]||(r[2]=u("div",{class:"brand"},[u("div",{class:"brand-title"},"后台管理"),u("div",{class:"brand-sub app-muted"},"知识管理平台")],-1)),d(j,{"default-active":M.value,class:"aside-menu",router:"",onSelect:$},{default:o(()=>[(_(),V(z,null,Y(I,c=>d(q,{key:c.path,index:c.path},{default:o(()=>[d(F,null,{default:o(()=>[(_(),w(Q(c.icon)))]),_:2},1024),B(c)>0?(_(),w(U,{key:0,value:B(c),max:99,class:"menu-badge"},{default:o(()=>[u("span",ze,L(c.label),1)]),_:2},1032,["value"])):(_(),V("span",Ye,L(c.label),1))]),_:2},1032,["index"])),64))]),_:1},8,["default-active"])]),_:1})),d(G,null,{default:o(()=>[d(re,{class:"layout-header"},{default:o(()=>[u("div",Qe,[v.value?(_(),w(K,{key:0,text:"",class:"header-menu-btn",onClick:r[0]||(r[0]=c=>S.value=!0)},{default:o(()=>[...r[3]||(r[3]=[X(" 菜单 ",-1)])]),_:1})):W("",!0),r[4]||(r[4]=u("div",{class:"header-title"},"后台管理系统",-1))]),u("div",Xe,[u("div",Je,[r[5]||(r[5]=u("span",{class:"app-muted"},"管理员",-1)),u("strong",null,L(s.value||"-"),1)]),d(K,{type:"primary",plain:"",class:"logout-btn",onClick:se},{default:o(()=>[...r[6]||(r[6]=[X("退出",-1)])]),_:1})])]),_:1}),d(ue,{class:"layout-main"},{default:o(()=>[u("div",Ze,[(_(),w(pe,null,{default:o(()=>[d(ce)]),fallback:o(()=>[d(ie,{shadow:"never","body-style":{padding:"16px"},class:"fallback-card"},{default:o(()=>[d(le,{rows:5,animated:""})]),_:1})]),_:1}))])]),_:1})]),_:1}),d(de,{modelValue:S.value,"onUpdate:modelValue":r[1]||(r[1]=c=>S.value=c),size:"min(82vw, 280px)",direction:"ltr","with-header":!1},{default:o(()=>[r[7]||(r[7]=u("div",{class:"drawer-brand"},[u("div",{class:"brand-title"},"后台管理"),u("div",{class:"brand-sub app-muted"},"知识管理平台")],-1)),d(j,{"default-active":M.value,class:"aside-menu",router:"",onSelect:$},{default:o(()=>[(_(),V(z,null,Y(I,c=>d(q,{key:c.path,index:c.path},{default:o(()=>[d(F,null,{default:o(()=>[(_(),w(Q(c.icon)))]),_:2},1024),B(c)>0?(_(),w(U,{key:0,value:B(c),max:99,class:"menu-badge"},{default:o(()=>[u("span",et,L(c.label),1)]),_:2},1032,["value"])):(_(),V("span",tt,L(c.label),1))]),_:2},1032,["index"])),64))]),_:1},8,["default-active"])]),_:1},8,["modelValue"])]),_:1})}}},ot=ae(st,[["__scopeId","data-v-ce83ce8c"]]),rt=()=>E(()=>import("./ReportPage-BITaX3hh.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11]),import.meta.url),ct=()=>E(()=>import("./UsersPage-BqAhpZky.js"),__vite__mapDeps([12,13,1,2,3,4,10,14]),import.meta.url),lt=()=>E(()=>import("./FeedbacksPage-BXNEKJpt.js"),__vite__mapDeps([15,8,2,9,1,3,4,10,16]),import.meta.url),it=()=>E(()=>import("./LogsPage-xSURzHTt.js"),__vite__mapDeps([17,13,6,1,2,3,4,10,18]),import.meta.url),ut=()=>E(()=>import("./AnnouncementsPage-fRPrWBwG.js"),__vite__mapDeps([19,1,2,3,4,10,20]),import.meta.url),dt=()=>E(()=>import("./EmailPage-CFpw2E0J.js"),__vite__mapDeps([21,5,8,2,9,1,3,4,10,22]),import.meta.url),mt=()=>E(()=>import("./SecurityPage-CQuWzZ_p.js"),__vite__mapDeps([23,8,2,9,1,3,4,10,24]),import.meta.url),ft=()=>E(()=>import("./SystemPage-DE9Z1xG-.js"),__vite__mapDeps([25,7,1,2,3,4,10,26]),import.meta.url),pt=()=>E(()=>import("./SettingsPage-Ct3Jh1K3.js"),__vite__mapDeps([27,1,2,3,4,10,28]),import.meta.url),_t=[{path:"/",component:ot,children:[{path:"",redirect:"/reports"},{path:"/pending",redirect:"/reports"},{path:"/stats",redirect:"/reports"},{path:"/reports",name:"reports",component:rt},{path:"/users",name:"users",component:ct},{path:"/feedbacks",name:"feedbacks",component:lt},{path:"/logs",name:"logs",component:it},{path:"/announcements",name:"announcements",component:ut},{path:"/email",name:"email",component:dt},{path:"/security",name:"security",component:mt},{path:"/system",name:"system",component:ft},{path:"/settings",name:"settings",component:pt}]}],ht=ge({history:be(),routes:_t});_e(Ve).use(ht).use(Le,{locale:Ae}).mount("#app");export{ae as _,y as a,wt as b,kt as c,Pt as d,He as f,Et as r};
+const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./ReportPage-JOjMl_cz.js","./vendor-element-CJoVtPsD.js","./vendor-sLgkZK1v.js","./vendor-vue-CWkOjFoA.js","./vendor-element-C68yOrAy.css","./email-D8pS1Pbx.js","./tasks-BrCJEcYb.js","./system-DO-Wv0rM.js","./MetricGrid-DBgW22G4.js","./MetricGrid-yP_dkP6X.css","./vendor-axios-B9ygI19o.js","./ReportPage-DlF2eaTa.css","./UsersPage-CZk3ZLBk.js","./users-Czwk10aS.js","./UsersPage-Cow_LicY.css","./FeedbacksPage-C-dXtI0j.js","./FeedbacksPage-mrXjCiV2.css","./LogsPage-BzvZ_jRl.js","./LogsPage-D1bozCEo.css","./AnnouncementsPage-BZKzU8qv.js","./AnnouncementsPage-DOwZaaOu.css","./EmailPage-BW-iqQTt.js","./EmailPage-BmPCDPYC.css","./SecurityPage-DlbTJ4QF.js","./SecurityPage-DN76ndc_.css","./SystemPage-BdmQOog3.js","./SystemPage-DMyNBO3N.css","./SettingsPage-cWvS8Esv.js","./SettingsPage-DaB8PeRL.css"])))=>i.map(i=>d[i]);
+import{aW as m,K as w,A as _,r as O,c as H,v as me,a1 as fe,L as o,N as W,S as d,B as u,z as V,R as z,ak as Y,O as Q,Q as L,P as X,by as pe,p as D,bu as _e}from"./vendor-sLgkZK1v.js";import{u as he,p as ye,q as ge,r as be}from"./vendor-vue-CWkOjFoA.js";import{E as te,f as ne,g as ve,u as we,h as Ee,j as ke,k as Pe,m as Re,n as Te,t as Se,o as Oe,p as Le,z as Ae}from"./vendor-element-CJoVtPsD.js";import{a as xe}from"./vendor-axios-B9ygI19o.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))a(s);new MutationObserver(s=>{for(const l of s)if(l.type==="childList")for(const i of l.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&a(i)}).observe(document,{childList:!0,subtree:!0});function n(s){const l={};return s.integrity&&(l.integrity=s.integrity),s.referrerPolicy&&(l.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?l.credentials="include":s.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function a(s){if(s.ep)return;s.ep=!0;const l=n(s);fetch(s.href,l)}})();const ae=(e,t)=>{const n=e.__vccOpts||e;for(const[a,s]of t)n[a]=s;return n},Ne={};function Be(e,t){const n=m("RouterView");return _(),w(n)}const Ve=ae(Ne,[["render",Be]]),Ce="modulepreload",De=function(e,t){return new URL(e,t).href},J={},E=function(t,n,a){let s=Promise.resolve();if(n&&n.length>0){let T=function(p){return Promise.all(p.map(b=>Promise.resolve(b).then(P=>({status:"fulfilled",value:P}),P=>({status:"rejected",reason:P}))))};const i=document.getElementsByTagName("link"),f=document.querySelector("meta[property=csp-nonce]"),k=f?.nonce||f?.getAttribute("nonce");s=T(n.map(p=>{if(p=De(p,a),p in J)return;J[p]=!0;const b=p.endsWith(".css"),P=b?'[rel="stylesheet"]':"";if(a)for(let R=i.length-1;R>=0;R--){const v=i[R];if(v.href===p&&(!b||v.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${p}"]${P}`))return;const h=document.createElement("link");if(h.rel=b?"stylesheet":Ce,b||(h.as="script"),h.crossOrigin="",h.href=p,k&&h.setAttribute("nonce",k),document.head.appendChild(h),b)return new Promise((R,v)=>{h.addEventListener("load",R),h.addEventListener("error",()=>v(new Error(`Unable to preload CSS for ${p}`)))})}))}function l(i){const f=new Event("vite:preloadError",{cancelable:!0});if(f.payload=i,window.dispatchEvent(f),!f.defaultPrevented)throw i}return s.then(i=>{for(const f of i||[])f.status==="rejected"&&l(f.reason);return t().catch(l)})};let Z="",ee=0;const Ie=new Set([408,425,429,500,502,503,504]),Me=1,$e=300;function A(e,t,n=1500){const a=Date.now();e===Z&&a-ee=Me)return!1;const a=String(e?.code||"");if(a==="ECONNABORTED"||a==="ERR_NETWORK")return!0;const s=Number(e?.response?.status||0);return Ie.has(s)}function je(e){return new Promise(t=>{window.setTimeout(t,Math.max(0,Number(e||0)))})}async function Ke(e,t){const n=e?.config||{},a=Number(n.__retry_count||0);n.__retry_count=a+1;const s=$e*(a+1);return await je(s),t.request(n)}const y=xe.create({baseURL:"/yuyx/api",timeout:3e4,withCredentials:!0});let x=null;async function Ge(){return x||(x=te.prompt("请输入管理员密码进行二次确认","安全确认",{inputType:"password",inputPlaceholder:"管理员密码",confirmButtonText:"确认",cancelButtonText:"取消",inputValidator:e=>!!String(e||"").trim(),inputErrorMessage:"密码不能为空"}).then(async e=>{const t=String(e.value||"").trim();await y.post("/admin/reauth",{password:t}),ne.success("已通过安全确认")}).finally(()=>{x=null}),x)}y.interceptors.request.use(e=>{const t=String(e?.method||"GET").toUpperCase();if(!["GET","HEAD","OPTIONS"].includes(t)){const n=Fe("csrf_token");n&&(e.headers=e.headers||{},e.headers["X-CSRF-Token"]=n)}return e});y.interceptors.response.use(e=>e,async e=>{const t=e?.response?.status,n=e?.response?.data,a=n?.error||n?.message||e?.message||"请求失败";if(n?.code==="reauth_required"&&e?.config&&!e.config.__reauth_retry)try{return e.config.__reauth_retry=!0,await Ge(),y.request(e.config)}catch{return Promise.reject(e)}return qe(e)?Ke(e,y):(t===401?(A("401",a,3e3),(window.location?.pathname||"").startsWith("/yuyx")||(window.location.href="/yuyx")):t===403?A("403",a,5e3):t?A(`http:${t}:${a}`,a):e?.code==="ECONNABORTED"?A("timeout","请求超时",3e3):A(`net:${a}`,a,3e3),Promise.reject(e))});async function wt(e=""){const{data:t}=await y.get("/feedbacks",{params:e?{status:e}:{}});return t}async function He(){const{data:e}=await y.get("/feedbacks",{params:{limit:1,offset:0}});return e?.stats}async function Et(e,t){const{data:n}=await y.post(`/feedbacks/${e}/reply`,{reply:t});return n}async function kt(e){const{data:t}=await y.post(`/feedbacks/${e}/close`);return t}async function Pt(e){const{data:t}=await y.delete(`/feedbacks/${e}`);return t}async function We(){const{data:e}=await y.get("/stats");return e}const ze={class:"menu-label"},Ye={key:1,class:"menu-label"},Qe={class:"header-left"},Xe={class:"header-right"},Je={class:"admin-name"},Ze={class:"main-shell"},et={class:"menu-label"},tt={key:1,class:"menu-label"},nt=6e4,at=18e4,st={__name:"AdminLayout",setup(e){const t=he(),n=ye(),a=O({}),s=H(()=>a.value?.admin_username||"");async function l(){try{a.value=await We()}finally{}}const i=O(!1),f=O(0);let k=null;async function T(g=null){if(g&&typeof g=="object"){Object.prototype.hasOwnProperty.call(g,"pendingFeedbacks")&&(f.value=Number(g.pendingFeedbacks||0));return}if(!i.value){i.value=!0;try{const r=await He();f.value=Number(r?.pending||0)}finally{i.value=!1}}}function p(){return typeof document>"u"?!1:document.visibilityState==="hidden"}function b(){return p()?at:nt}function P(){k&&(window.clearTimeout(k),k=null)}function h(){P(),k=window.setTimeout(async()=>{k=null,await T().catch(()=>{}),h()},b())}function R(){h()}D("refreshStats",l),D("adminStats",a),D("refreshNavBadges",T);const v=O(!1),S=O(!1);let N;function C(){v.value=!!N?.matches,v.value||(S.value=!1)}me(async()=>{N=window.matchMedia("(max-width: 768px)"),N.addEventListener?.("change",C),C(),await l(),await T(),h(),window.addEventListener("visibilitychange",R)}),fe(()=>{N?.removeEventListener?.("change",C),P(),window.removeEventListener("visibilitychange",R)});const I=[{path:"/reports",label:"报表",icon:ve},{path:"/users",label:"用户",icon:we},{path:"/feedbacks",label:"反馈",icon:Ee,badgeKey:"feedbacks"},{path:"/logs",label:"任务日志",icon:ke},{path:"/announcements",label:"公告",icon:Pe},{path:"/email",label:"邮件",icon:Re},{path:"/security",label:"安全防护",icon:Te},{path:"/system",label:"系统配置",icon:Se},{path:"/settings",label:"设置",icon:Oe}],M=H(()=>t.path);function B(g){return g?.badgeKey&&g.badgeKey==="feedbacks"?Number(f.value||0):0}async function se(){try{await te.confirm("确定退出管理员登录吗?","退出登录",{confirmButtonText:"退出",cancelButtonText:"取消",type:"warning"})}catch{return}try{await y.post("/logout")}finally{window.location.href="/yuyx"}}async function $(g){await n.push(g),S.value=!1}return(g,r)=>{const F=m("el-icon"),U=m("el-badge"),q=m("el-menu-item"),j=m("el-menu"),oe=m("el-aside"),K=m("el-button"),re=m("el-header"),ce=m("RouterView"),le=m("el-skeleton"),ie=m("el-card"),ue=m("el-main"),G=m("el-container"),de=m("el-drawer");return _(),w(G,{class:"layout-root"},{default:o(()=>[v.value?W("",!0):(_(),w(oe,{key:0,width:"220px",class:"layout-aside"},{default:o(()=>[r[2]||(r[2]=u("div",{class:"brand"},[u("div",{class:"brand-title"},"后台管理"),u("div",{class:"brand-sub app-muted"},"知识管理平台")],-1)),d(j,{"default-active":M.value,class:"aside-menu",router:"",onSelect:$},{default:o(()=>[(_(),V(z,null,Y(I,c=>d(q,{key:c.path,index:c.path},{default:o(()=>[d(F,null,{default:o(()=>[(_(),w(Q(c.icon)))]),_:2},1024),B(c)>0?(_(),w(U,{key:0,value:B(c),max:99,class:"menu-badge"},{default:o(()=>[u("span",ze,L(c.label),1)]),_:2},1032,["value"])):(_(),V("span",Ye,L(c.label),1))]),_:2},1032,["index"])),64))]),_:1},8,["default-active"])]),_:1})),d(G,null,{default:o(()=>[d(re,{class:"layout-header"},{default:o(()=>[u("div",Qe,[v.value?(_(),w(K,{key:0,text:"",class:"header-menu-btn",onClick:r[0]||(r[0]=c=>S.value=!0)},{default:o(()=>[...r[3]||(r[3]=[X(" 菜单 ",-1)])]),_:1})):W("",!0),r[4]||(r[4]=u("div",{class:"header-title"},"后台管理系统",-1))]),u("div",Xe,[u("div",Je,[r[5]||(r[5]=u("span",{class:"app-muted"},"管理员",-1)),u("strong",null,L(s.value||"-"),1)]),d(K,{type:"primary",plain:"",class:"logout-btn",onClick:se},{default:o(()=>[...r[6]||(r[6]=[X("退出",-1)])]),_:1})])]),_:1}),d(ue,{class:"layout-main"},{default:o(()=>[u("div",Ze,[(_(),w(pe,null,{default:o(()=>[d(ce)]),fallback:o(()=>[d(ie,{shadow:"never","body-style":{padding:"16px"},class:"fallback-card"},{default:o(()=>[d(le,{rows:5,animated:""})]),_:1})]),_:1}))])]),_:1})]),_:1}),d(de,{modelValue:S.value,"onUpdate:modelValue":r[1]||(r[1]=c=>S.value=c),size:"min(82vw, 280px)",direction:"ltr","with-header":!1},{default:o(()=>[r[7]||(r[7]=u("div",{class:"drawer-brand"},[u("div",{class:"brand-title"},"后台管理"),u("div",{class:"brand-sub app-muted"},"知识管理平台")],-1)),d(j,{"default-active":M.value,class:"aside-menu",router:"",onSelect:$},{default:o(()=>[(_(),V(z,null,Y(I,c=>d(q,{key:c.path,index:c.path},{default:o(()=>[d(F,null,{default:o(()=>[(_(),w(Q(c.icon)))]),_:2},1024),B(c)>0?(_(),w(U,{key:0,value:B(c),max:99,class:"menu-badge"},{default:o(()=>[u("span",et,L(c.label),1)]),_:2},1032,["value"])):(_(),V("span",tt,L(c.label),1))]),_:2},1032,["index"])),64))]),_:1},8,["default-active"])]),_:1},8,["modelValue"])]),_:1})}}},ot=ae(st,[["__scopeId","data-v-ce83ce8c"]]),rt=()=>E(()=>import("./ReportPage-JOjMl_cz.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11]),import.meta.url),ct=()=>E(()=>import("./UsersPage-CZk3ZLBk.js"),__vite__mapDeps([12,13,1,2,3,4,10,14]),import.meta.url),lt=()=>E(()=>import("./FeedbacksPage-C-dXtI0j.js"),__vite__mapDeps([15,8,2,9,1,3,4,10,16]),import.meta.url),it=()=>E(()=>import("./LogsPage-BzvZ_jRl.js"),__vite__mapDeps([17,13,6,1,2,3,4,10,18]),import.meta.url),ut=()=>E(()=>import("./AnnouncementsPage-BZKzU8qv.js"),__vite__mapDeps([19,1,2,3,4,10,20]),import.meta.url),dt=()=>E(()=>import("./EmailPage-BW-iqQTt.js"),__vite__mapDeps([21,5,8,2,9,1,3,4,10,22]),import.meta.url),mt=()=>E(()=>import("./SecurityPage-DlbTJ4QF.js"),__vite__mapDeps([23,8,2,9,1,3,4,10,24]),import.meta.url),ft=()=>E(()=>import("./SystemPage-BdmQOog3.js"),__vite__mapDeps([25,7,1,2,3,4,10,26]),import.meta.url),pt=()=>E(()=>import("./SettingsPage-cWvS8Esv.js"),__vite__mapDeps([27,1,2,3,4,10,28]),import.meta.url),_t=[{path:"/",component:ot,children:[{path:"",redirect:"/reports"},{path:"/pending",redirect:"/reports"},{path:"/stats",redirect:"/reports"},{path:"/reports",name:"reports",component:rt},{path:"/users",name:"users",component:ct},{path:"/feedbacks",name:"feedbacks",component:lt},{path:"/logs",name:"logs",component:it},{path:"/announcements",name:"announcements",component:ut},{path:"/email",name:"email",component:dt},{path:"/security",name:"security",component:mt},{path:"/system",name:"system",component:ft},{path:"/settings",name:"settings",component:pt}]}],ht=ge({history:be(),routes:_t});_e(Ve).use(ht).use(Le,{locale:Ae}).mount("#app");export{ae as _,y as a,wt as b,kt as c,Pt as d,He as f,Et as r};
diff --git a/static/admin/assets/system-1p1K-j-4.js b/static/admin/assets/system-DO-Wv0rM.js
similarity index 68%
rename from static/admin/assets/system-1p1K-j-4.js
rename to static/admin/assets/system-DO-Wv0rM.js
index c15a5af..a69811b 100644
--- a/static/admin/assets/system-1p1K-j-4.js
+++ b/static/admin/assets/system-DO-Wv0rM.js
@@ -1 +1 @@
-import{a}from"./index-bXG7gEdM.js";async function o(){const{data:t}=await a.get("/system/config");return t}async function e(t){const{data:n}=await a.post("/system/config",t);return n}export{o as f,e as u};
+import{a}from"./index-ZQnpYe51.js";async function o(){const{data:t}=await a.get("/system/config");return t}async function e(t){const{data:n}=await a.post("/system/config",t);return n}export{o as f,e as u};
diff --git a/static/admin/assets/tasks-Y3hOqi3L.js b/static/admin/assets/tasks-BrCJEcYb.js
similarity index 89%
rename from static/admin/assets/tasks-Y3hOqi3L.js
rename to static/admin/assets/tasks-BrCJEcYb.js
index 18f34ca..0923075 100644
--- a/static/admin/assets/tasks-Y3hOqi3L.js
+++ b/static/admin/assets/tasks-BrCJEcYb.js
@@ -1 +1 @@
-import{a}from"./index-bXG7gEdM.js";async function e(){const{data:t}=await a.get("/server/info");return t}async function c(){const{data:t}=await a.get("/docker_stats");return t}async function r(){const{data:t}=await a.get("/request_metrics");return t}async function o(){const{data:t}=await a.get("/task/stats");return t}async function i(){const{data:t}=await a.get("/task/running");return t}async function u(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function f(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{i as a,e as b,c,r as d,u as e,o as f,f as g};
+import{a}from"./index-ZQnpYe51.js";async function e(){const{data:t}=await a.get("/server/info");return t}async function c(){const{data:t}=await a.get("/docker_stats");return t}async function r(){const{data:t}=await a.get("/request_metrics");return t}async function o(){const{data:t}=await a.get("/task/stats");return t}async function i(){const{data:t}=await a.get("/task/running");return t}async function u(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function f(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{i as a,e as b,c,r as d,u as e,o as f,f as g};
diff --git a/static/admin/assets/users-kt1Evatk.js b/static/admin/assets/users-Czwk10aS.js
similarity index 90%
rename from static/admin/assets/users-kt1Evatk.js
rename to static/admin/assets/users-Czwk10aS.js
index 7aa2e90..f6106a1 100644
--- a/static/admin/assets/users-kt1Evatk.js
+++ b/static/admin/assets/users-Czwk10aS.js
@@ -1 +1 @@
-import{a as t}from"./index-bXG7gEdM.js";async function n(){const{data:s}=await t.get("/users");return s}async function o(s){const{data:a}=await t.post(`/users/${s}/approve`);return a}async function c(s){const{data:a}=await t.post(`/users/${s}/reject`);return a}async function i(s){const{data:a}=await t.delete(`/users/${s}`);return a}async function u(s,a){const{data:e}=await t.post(`/users/${s}/vip`,{days:a});return e}async function p(s){const{data:a}=await t.delete(`/users/${s}/vip`);return a}async function d(s,a){const{data:e}=await t.post(`/users/${s}/reset_password`,{new_password:a});return e}export{o as a,p as b,d as c,i as d,n as f,c as r,u as s};
+import{a as t}from"./index-ZQnpYe51.js";async function n(){const{data:s}=await t.get("/users");return s}async function o(s){const{data:a}=await t.post(`/users/${s}/approve`);return a}async function c(s){const{data:a}=await t.post(`/users/${s}/reject`);return a}async function i(s){const{data:a}=await t.delete(`/users/${s}`);return a}async function u(s,a){const{data:e}=await t.post(`/users/${s}/vip`,{days:a});return e}async function p(s){const{data:a}=await t.delete(`/users/${s}/vip`);return a}async function d(s,a){const{data:e}=await t.post(`/users/${s}/reset_password`,{new_password:a});return e}export{o as a,p as b,d as c,i as d,n as f,c as r,u as s};
diff --git a/static/admin/index.html b/static/admin/index.html
index c722cf5..f7a163c 100644
--- a/static/admin/index.html
+++ b/static/admin/index.html
@@ -5,7 +5,7 @@
后台管理 - 知识管理平台
-
+