diff --git a/admin-frontend/src/layouts/AdminLayout.vue b/admin-frontend/src/layouts/AdminLayout.vue index c453108..c83b792 100644 --- a/admin-frontend/src/layouts/AdminLayout.vue +++ b/admin-frontend/src/layouts/AdminLayout.vue @@ -17,6 +17,7 @@ import { import { api } from '../api/client' import { fetchFeedbackStats } from '../api/feedbacks' import { fetchSystemStats } from '../api/stats' +import { clearCachedKdocsStatus, preloadKdocsStatus } from '../utils/kdocsStatusCache' const route = useRoute() const router = useRouter() @@ -102,6 +103,9 @@ onMounted(async () => { mediaQuery.addEventListener?.('change', syncIsMobile) syncIsMobile() + // 后台登录后预加载金山文档登录状态,系统配置页可直接复用缓存。 + void preloadKdocsStatus({ maxAgeMs: 60_000, silent: true }).catch(() => {}) + await refreshStats() await refreshNavBadges() scheduleBadgePolling() @@ -160,6 +164,7 @@ async function logout() { try { await api.post('/logout') } finally { + clearCachedKdocsStatus() window.location.href = '/yuyx' } } diff --git a/admin-frontend/src/pages/SystemPage.vue b/admin-frontend/src/pages/SystemPage.vue index 2461120..0de2b02 100644 --- a/admin-frontend/src/pages/SystemPage.vue +++ b/admin-frontend/src/pages/SystemPage.vue @@ -5,6 +5,7 @@ import { ElMessage, ElMessageBox } from 'element-plus' import { fetchSystemConfig, updateSystemConfig } from '../api/system' import { fetchKdocsQr, fetchKdocsStatus, clearKdocsLogin } from '../api/kdocs' import { fetchProxyConfig, testProxy, updateProxyConfig } from '../api/proxy' +import { getCachedKdocsStatus, preloadKdocsStatus, updateCachedKdocsStatus } from '../utils/kdocsStatusCache' const loading = ref(false) @@ -33,14 +34,15 @@ const kdocsRowEnd = ref(0) const kdocsAdminNotifyEnabled = ref(false) const kdocsAdminNotifyEmail = ref('') -const kdocsStatus = ref({}) +const initialKdocsStatus = getCachedKdocsStatus({ maxAgeMs: 10 * 60 * 1000 }) +const kdocsStatus = ref(initialKdocsStatus || {}) const kdocsQrOpen = ref(false) const kdocsQrImage = ref('') const kdocsPolling = ref(false) const kdocsStatusLoading = ref(false) const kdocsQrLoading = ref(false) const kdocsClearLoading = ref(false) -const kdocsSilentRefreshing = ref(false) +const kdocsSilentRefreshing = ref(!initialKdocsStatus) const kdocsActionHint = ref('') let kdocsPollingTimer = null @@ -116,7 +118,13 @@ async function loadAll() { loading.value = false } - // 金山登录状态改为静默异步获取,避免阻塞系统配置首屏渲染 + const cachedStatus = getCachedKdocsStatus({ maxAgeMs: 10 * 60 * 1000 }) + if (cachedStatus) { + kdocsStatus.value = cachedStatus + kdocsSilentRefreshing.value = false + } + + // 静默刷新金山登录状态,确保状态持续更新且不阻塞首屏。 void refreshKdocsStatusSilently() } @@ -124,14 +132,12 @@ async function refreshKdocsStatusSilently() { if (kdocsSilentRefreshing.value || kdocsStatusLoading.value) return kdocsSilentRefreshing.value = true try { - const status = await fetchKdocsStatus( - {}, - { - __silent: true, - __no_retry: true, - timeout: 8000, - }, - ) + const status = await preloadKdocsStatus({ + force: false, + maxAgeMs: 60_000, + silent: true, + live: 0, + }) kdocsStatus.value = status || {} } catch { // silent mode @@ -255,7 +261,9 @@ async function refreshKdocsStatus() { kdocsStatusLoading.value = true setKdocsHint('正在刷新状态') try { - kdocsStatus.value = await fetchKdocsStatus({ live: 1 }) + const status = await fetchKdocsStatus({ live: 1 }) + kdocsStatus.value = status || {} + updateCachedKdocsStatus(kdocsStatus.value) setKdocsHint('状态已刷新') } catch { setKdocsHint('刷新失败,请稍后重试') @@ -267,7 +275,8 @@ async function refreshKdocsStatus() { async function pollKdocsStatus() { try { const status = await fetchKdocsStatus({ live: 1 }) - kdocsStatus.value = status + kdocsStatus.value = status || {} + updateCachedKdocsStatus(kdocsStatus.value) const loggedIn = status?.logged_in === true || status?.last_login_ok === true if (loggedIn) { ElMessage.success('扫码成功,已登录') @@ -332,6 +341,12 @@ async function onClearKdocsLogin() { await clearKdocsLogin() kdocsQrOpen.value = false kdocsQrImage.value = '' + kdocsStatus.value = updateCachedKdocsStatus({ + ...(kdocsStatus.value || {}), + logged_in: false, + last_login_ok: false, + login_required: true, + }) ElMessage.success('登录态已清除') setKdocsHint('登录态已清除') await refreshKdocsStatus() diff --git a/admin-frontend/src/utils/kdocsStatusCache.js b/admin-frontend/src/utils/kdocsStatusCache.js new file mode 100644 index 0000000..837735e --- /dev/null +++ b/admin-frontend/src/utils/kdocsStatusCache.js @@ -0,0 +1,121 @@ +import { fetchKdocsStatus } from '../api/kdocs' + +const CACHE_KEY = 'admin:kdocs:status:v1' +const DEFAULT_MAX_AGE_MS = 5 * 60 * 1000 + +let memoryStatus = null +let memoryUpdatedAt = 0 +let inflightPromise = null + +function nowTs() { + return Date.now() +} + +function normalizeStatus(raw) { + if (!raw || typeof raw !== 'object') return {} + return raw +} + +function readSessionCache() { + try { + const raw = window.sessionStorage.getItem(CACHE_KEY) + if (!raw) return null + const parsed = JSON.parse(raw) + if (!parsed || typeof parsed !== 'object') return null + const updatedAt = Number(parsed.updated_at || 0) + const status = normalizeStatus(parsed.status) + if (!updatedAt) return null + return { status, updatedAt } + } catch { + return null + } +} + +function writeSessionCache(status, updatedAt) { + try { + window.sessionStorage.setItem( + CACHE_KEY, + JSON.stringify({ + status: normalizeStatus(status), + updated_at: Number(updatedAt || nowTs()), + }), + ) + } catch { + // ignore + } +} + +function hydrateFromSessionIfNeeded() { + if (memoryStatus !== null) return + const cached = readSessionCache() + if (!cached) return + memoryStatus = cached.status + memoryUpdatedAt = cached.updatedAt +} + +function commitStatus(status) { + memoryStatus = normalizeStatus(status) + memoryUpdatedAt = nowTs() + writeSessionCache(memoryStatus, memoryUpdatedAt) + return memoryStatus +} + +function isFresh(maxAgeMs) { + if (memoryStatus === null || !memoryUpdatedAt) return false + const ageLimit = Number(maxAgeMs) + if (!Number.isFinite(ageLimit) || ageLimit < 0) return true + return nowTs() - memoryUpdatedAt <= ageLimit +} + +export function getCachedKdocsStatus(options = {}) { + hydrateFromSessionIfNeeded() + const maxAgeMs = options.maxAgeMs ?? DEFAULT_MAX_AGE_MS + if (!isFresh(maxAgeMs)) return null + return normalizeStatus(memoryStatus) +} + +export function updateCachedKdocsStatus(status) { + return commitStatus(status) +} + +export function clearCachedKdocsStatus() { + memoryStatus = null + memoryUpdatedAt = 0 + inflightPromise = null + try { + window.sessionStorage.removeItem(CACHE_KEY) + } catch { + // ignore + } +} + +export async function preloadKdocsStatus(options = {}) { + const { + force = false, + maxAgeMs = DEFAULT_MAX_AGE_MS, + silent = true, + live = 0, + } = options + + if (!force) { + const cached = getCachedKdocsStatus({ maxAgeMs }) + if (cached) return cached + } + + if (inflightPromise) return inflightPromise + + const params = live ? { live: 1 } : {} + const requestConfig = { + __silent: Boolean(silent), + __no_retry: true, + timeout: 8000, + } + + inflightPromise = fetchKdocsStatus(params, requestConfig) + .then((status) => commitStatus(status || {})) + .finally(() => { + inflightPromise = null + }) + + return inflightPromise +} diff --git a/static/admin/.vite/manifest.json b/static/admin/.vite/manifest.json index 4d9db7a..3e56410 100644 --- a/static/admin/.vite/manifest.json +++ b/static/admin/.vite/manifest.json @@ -1,6 +1,6 @@ { - "_MetricGrid-BW8H4wTM.js": { - "file": "assets/MetricGrid-BW8H4wTM.js", + "_MetricGrid-BnihYB_8.js": { + "file": "assets/MetricGrid-BnihYB_8.js", "name": "MetricGrid", "imports": [ "index.html", @@ -14,29 +14,29 @@ "file": "assets/MetricGrid-yP_dkP6X.css", "src": "_MetricGrid-yP_dkP6X.css" }, - "_email-CZFN9gLR.js": { - "file": "assets/email-CZFN9gLR.js", + "_email-px7YBG2O.js": { + "file": "assets/email-px7YBG2O.js", "name": "email", "imports": [ "index.html" ] }, - "_system-CiDlQnoe.js": { - "file": "assets/system-CiDlQnoe.js", + "_system-ZDPnxnIu.js": { + "file": "assets/system-ZDPnxnIu.js", "name": "system", "imports": [ "index.html" ] }, - "_tasks-DqqATNe_.js": { - "file": "assets/tasks-DqqATNe_.js", + "_tasks-Bep0SUyu.js": { + "file": "assets/tasks-Bep0SUyu.js", "name": "tasks", "imports": [ "index.html" ] }, - "_users-BowyvQzr.js": { - "file": "assets/users-BowyvQzr.js", + "_users-te9ySk34.js": { + "file": "assets/users-te9ySk34.js", "name": "users", "imports": [ "index.html" @@ -73,7 +73,7 @@ "name": "vendor-vue" }, "index.html": { - "file": "assets/index-BNslg8wp.js", + "file": "assets/index-C1f9ticl.js", "name": "index", "src": "index.html", "isEntry": true, @@ -95,11 +95,11 @@ "src/pages/SettingsPage.vue" ], "css": [ - "assets/index-CVq5QfHO.css" + "assets/index-Dh0BbqTX.css" ] }, "src/pages/AnnouncementsPage.vue": { - "file": "assets/AnnouncementsPage-C0u6p8a8.js", + "file": "assets/AnnouncementsPage-C6UgwLIT.js", "name": "AnnouncementsPage", "src": "src/pages/AnnouncementsPage.vue", "isDynamicEntry": true, @@ -115,14 +115,14 @@ ] }, "src/pages/EmailPage.vue": { - "file": "assets/EmailPage-DbZ8F1_b.js", + "file": "assets/EmailPage-CATruPK6.js", "name": "EmailPage", "src": "src/pages/EmailPage.vue", "isDynamicEntry": true, "imports": [ - "_email-CZFN9gLR.js", + "_email-px7YBG2O.js", "index.html", - "_MetricGrid-BW8H4wTM.js", + "_MetricGrid-BnihYB_8.js", "_vendor-element-B5S5pUKo.js", "_vendor-vue-CVxSw_oJ.js", "_vendor-axios-B9ygI19o.js", @@ -133,13 +133,13 @@ ] }, "src/pages/FeedbacksPage.vue": { - "file": "assets/FeedbacksPage-Dwzul2Z8.js", + "file": "assets/FeedbacksPage-BAnFKHSL.js", "name": "FeedbacksPage", "src": "src/pages/FeedbacksPage.vue", "isDynamicEntry": true, "imports": [ "index.html", - "_MetricGrid-BW8H4wTM.js", + "_MetricGrid-BnihYB_8.js", "_vendor-element-B5S5pUKo.js", "_vendor-vue-CVxSw_oJ.js", "_vendor-axios-B9ygI19o.js", @@ -150,13 +150,13 @@ ] }, "src/pages/LogsPage.vue": { - "file": "assets/LogsPage-Bs3Ge-t3.js", + "file": "assets/LogsPage-DFPeq0bL.js", "name": "LogsPage", "src": "src/pages/LogsPage.vue", "isDynamicEntry": true, "imports": [ - "_users-BowyvQzr.js", - "_tasks-DqqATNe_.js", + "_users-te9ySk34.js", + "_tasks-Bep0SUyu.js", "index.html", "_vendor-element-B5S5pUKo.js", "_vendor-vue-CVxSw_oJ.js", @@ -168,17 +168,17 @@ ] }, "src/pages/ReportPage.vue": { - "file": "assets/ReportPage-CqUGYZcC.js", + "file": "assets/ReportPage-fzVH-d9u.js", "name": "ReportPage", "src": "src/pages/ReportPage.vue", "isDynamicEntry": true, "imports": [ "_vendor-element-B5S5pUKo.js", "index.html", - "_email-CZFN9gLR.js", - "_tasks-DqqATNe_.js", - "_system-CiDlQnoe.js", - "_MetricGrid-BW8H4wTM.js", + "_email-px7YBG2O.js", + "_tasks-Bep0SUyu.js", + "_system-ZDPnxnIu.js", + "_MetricGrid-BnihYB_8.js", "_vendor-vue-CVxSw_oJ.js", "_vendor-misc-BeoNyvBp.js", "_vendor-axios-B9ygI19o.js" @@ -188,13 +188,13 @@ ] }, "src/pages/SecurityPage.vue": { - "file": "assets/SecurityPage-BARcvvk8.js", + "file": "assets/SecurityPage-xwMQfhuh.js", "name": "SecurityPage", "src": "src/pages/SecurityPage.vue", "isDynamicEntry": true, "imports": [ "index.html", - "_MetricGrid-BW8H4wTM.js", + "_MetricGrid-BnihYB_8.js", "_vendor-element-B5S5pUKo.js", "_vendor-vue-CVxSw_oJ.js", "_vendor-axios-B9ygI19o.js", @@ -205,7 +205,7 @@ ] }, "src/pages/SettingsPage.vue": { - "file": "assets/SettingsPage-Bblx2O3L.js", + "file": "assets/SettingsPage-DRqlQLxJ.js", "name": "SettingsPage", "src": "src/pages/SettingsPage.vue", "isDynamicEntry": true, @@ -221,12 +221,12 @@ ] }, "src/pages/SystemPage.vue": { - "file": "assets/SystemPage-BVr94jQh.js", + "file": "assets/SystemPage-D3eBPCNe.js", "name": "SystemPage", "src": "src/pages/SystemPage.vue", "isDynamicEntry": true, "imports": [ - "_system-CiDlQnoe.js", + "_system-ZDPnxnIu.js", "index.html", "_vendor-element-B5S5pUKo.js", "_vendor-vue-CVxSw_oJ.js", @@ -234,16 +234,16 @@ "_vendor-misc-BeoNyvBp.js" ], "css": [ - "assets/SystemPage-CfMGkvmW.css" + "assets/SystemPage-BhhEz4Qz.css" ] }, "src/pages/UsersPage.vue": { - "file": "assets/UsersPage-CwfHMchI.js", + "file": "assets/UsersPage-DJZUCpfb.js", "name": "UsersPage", "src": "src/pages/UsersPage.vue", "isDynamicEntry": true, "imports": [ - "_users-BowyvQzr.js", + "_users-te9ySk34.js", "index.html", "_vendor-element-B5S5pUKo.js", "_vendor-vue-CVxSw_oJ.js", diff --git a/static/admin/assets/AnnouncementsPage-C0u6p8a8.js b/static/admin/assets/AnnouncementsPage-C6UgwLIT.js similarity index 99% rename from static/admin/assets/AnnouncementsPage-C0u6p8a8.js rename to static/admin/assets/AnnouncementsPage-C6UgwLIT.js index ec04386..74154c7 100644 --- a/static/admin/assets/AnnouncementsPage-C0u6p8a8.js +++ b/static/admin/assets/AnnouncementsPage-C6UgwLIT.js @@ -1 +1 @@ -import{p as J,a as r,E as C}from"./vendor-element-B5S5pUKo.js";import{a as w,_ as W}from"./index-BNslg8wp.js";import{r as v,o as K,aj as p,ap as O,n as h,q as f,t as u,L as n,E as t,G as A,D as x,u as Q,I as c,J as D,F as X,a8 as $}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-misc-BeoNyvBp.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),B=v(!1),I=v(!1),T=v([]);async function k(){I.value=!0;try{T.value=await Y()}catch{T.value=[]}finally{I.value=!1}}function E(){s.value="",_.value="",m.value="",g.value&&(g.value.value="")}function N(){g.value?.click()}function U(){m.value="",g.value&&(g.value.value="")}async function P(l){const e=l.target?.files?.[0];if(e){if(e.type&&!e.type.startsWith("image/")){r.error("请选择图片文件"),l.target.value="";return}B.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{B.value=!1,l.target.value=""}}}async function z(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("保存成功"),E(),await k()}catch{}}async function L(l){const e=$("div",{class:"announcement-view"},[l.content?$("div",{class:"announcement-view-text"},l.content):null,l.image_url?$("img",{class:"announcement-view-image",src:l.image_url,alt:"公告图片"}):null]);await C.alert(e,l.title||"公告",{confirmButtonText:"关闭",dangerouslyUseHTMLString:!1})}async function R(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 S(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 j(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 K(k),(l,e)=>{const d=p("el-input"),b=p("el-form-item"),i=p("el-button"),q=p("el-form"),F=p("el-card"),y=p("el-table-column"),M=p("el-tag"),G=p("el-table"),H=O("loading");return f(),h("div",le,[e[17]||(e[17]=u("div",{class:"app-page-title"},[u("h2",null,"公告管理")],-1)),n(F,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:t(()=>[e[9]||(e[9]=u("h3",{class:"section-title"},"创建公告",-1)),n(q,{"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:Q(J),loading:B.value,onClick:N},{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})):A("",!0),m.value?(f(),h("span",oe,D(m.value),1)):A("",!0),u("input",{ref_key:"imageInputRef",ref:g,class:"image-input",type:"file",accept:"image/*",onChange:P},null,544)])]),_:1})]),_:1}),m.value?(f(),h("div",ie,[u("img",{src:m.value,alt:"公告图片预览"},null,8,re)])):A("",!0),u("div",ue,[n(i,{type:"primary",onClick:e[2]||(e[2]=a=>z(!0))},{default:t(()=>[...e[6]||(e[6]=[c("发布并启用",-1)])]),_:1}),n(i,{onClick:e[3]||(e[3]=a=>z(!1))},{default:t(()=>[...e[7]||(e[7]=[c("保存但不启用",-1)])]),_:1}),n(i,{onClick:E},{default:t(()=>[...e[8]||(e[8]=[c("清空",-1)])]),_:1})]),e[10]||(e[10]=u("div",{class:"help"}," 说明:启用公告后,用户登录进入系统将弹窗提示;用户可选择“当次关闭”或“永久关闭本次公告”。 ",-1))]),_:1}),n(F,{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(G,{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},D(a.title),9,de)]),_:1}),n(y,{label:"状态",width:"120"},{default:t(({row:a})=>[n(M,{type:a.is_active?"success":"info",effect:"light"},{default:t(()=>[c(D(a.is_active?"启用":"停用"),1)]),_:2},1032,["type"])]),_:1}),n(y,{label:"图片",width:"100"},{default:t(({row:a})=>[a.image_url?(f(),x(M,{key:0,type:"success",effect:"light"},{default:t(()=>[...e[11]||(e[11]=[c("有图",-1)])]),_:1})):(f(),h("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=>L(a)},{default:t(()=>[...e[12]||(e[12]=[c("查看",-1)])]),_:1},8,["onClick"]),a.is_active?(f(),x(i,{key:0,size:"small",onClick:V=>S(a)},{default:t(()=>[...e[13]||(e[13]=[c("停用",-1)])]),_:1},8,["onClick"])):(f(),x(i,{key:1,type:"success",size:"small",onClick:V=>R(a)},{default:t(()=>[...e[14]||(e[14]=[c("启用",-1)])]),_:1},8,["onClick"])),n(i,{type:"danger",size:"small",onClick:V=>j(a)},{default:t(()=>[...e[15]||(e[15]=[c("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[H,I.value]])])]),_:1})])}}},ke=W(pe,[["__scopeId","data-v-6f55521c"]]);export{ke as default}; +import{p as J,a as r,E as C}from"./vendor-element-B5S5pUKo.js";import{a as w,_ as W}from"./index-C1f9ticl.js";import{r as v,o as K,aj as p,ap as O,n as h,q as f,t as u,L as n,E as t,G as A,D as x,u as Q,I as c,J as D,F as X,a8 as $}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-misc-BeoNyvBp.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),B=v(!1),I=v(!1),T=v([]);async function k(){I.value=!0;try{T.value=await Y()}catch{T.value=[]}finally{I.value=!1}}function E(){s.value="",_.value="",m.value="",g.value&&(g.value.value="")}function N(){g.value?.click()}function U(){m.value="",g.value&&(g.value.value="")}async function P(l){const e=l.target?.files?.[0];if(e){if(e.type&&!e.type.startsWith("image/")){r.error("请选择图片文件"),l.target.value="";return}B.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{B.value=!1,l.target.value=""}}}async function z(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("保存成功"),E(),await k()}catch{}}async function L(l){const e=$("div",{class:"announcement-view"},[l.content?$("div",{class:"announcement-view-text"},l.content):null,l.image_url?$("img",{class:"announcement-view-image",src:l.image_url,alt:"公告图片"}):null]);await C.alert(e,l.title||"公告",{confirmButtonText:"关闭",dangerouslyUseHTMLString:!1})}async function R(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 S(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 j(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 K(k),(l,e)=>{const d=p("el-input"),b=p("el-form-item"),i=p("el-button"),q=p("el-form"),F=p("el-card"),y=p("el-table-column"),M=p("el-tag"),G=p("el-table"),H=O("loading");return f(),h("div",le,[e[17]||(e[17]=u("div",{class:"app-page-title"},[u("h2",null,"公告管理")],-1)),n(F,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:t(()=>[e[9]||(e[9]=u("h3",{class:"section-title"},"创建公告",-1)),n(q,{"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:Q(J),loading:B.value,onClick:N},{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})):A("",!0),m.value?(f(),h("span",oe,D(m.value),1)):A("",!0),u("input",{ref_key:"imageInputRef",ref:g,class:"image-input",type:"file",accept:"image/*",onChange:P},null,544)])]),_:1})]),_:1}),m.value?(f(),h("div",ie,[u("img",{src:m.value,alt:"公告图片预览"},null,8,re)])):A("",!0),u("div",ue,[n(i,{type:"primary",onClick:e[2]||(e[2]=a=>z(!0))},{default:t(()=>[...e[6]||(e[6]=[c("发布并启用",-1)])]),_:1}),n(i,{onClick:e[3]||(e[3]=a=>z(!1))},{default:t(()=>[...e[7]||(e[7]=[c("保存但不启用",-1)])]),_:1}),n(i,{onClick:E},{default:t(()=>[...e[8]||(e[8]=[c("清空",-1)])]),_:1})]),e[10]||(e[10]=u("div",{class:"help"}," 说明:启用公告后,用户登录进入系统将弹窗提示;用户可选择“当次关闭”或“永久关闭本次公告”。 ",-1))]),_:1}),n(F,{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(G,{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},D(a.title),9,de)]),_:1}),n(y,{label:"状态",width:"120"},{default:t(({row:a})=>[n(M,{type:a.is_active?"success":"info",effect:"light"},{default:t(()=>[c(D(a.is_active?"启用":"停用"),1)]),_:2},1032,["type"])]),_:1}),n(y,{label:"图片",width:"100"},{default:t(({row:a})=>[a.image_url?(f(),x(M,{key:0,type:"success",effect:"light"},{default:t(()=>[...e[11]||(e[11]=[c("有图",-1)])]),_:1})):(f(),h("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=>L(a)},{default:t(()=>[...e[12]||(e[12]=[c("查看",-1)])]),_:1},8,["onClick"]),a.is_active?(f(),x(i,{key:0,size:"small",onClick:V=>S(a)},{default:t(()=>[...e[13]||(e[13]=[c("停用",-1)])]),_:1},8,["onClick"])):(f(),x(i,{key:1,type:"success",size:"small",onClick:V=>R(a)},{default:t(()=>[...e[14]||(e[14]=[c("启用",-1)])]),_:1},8,["onClick"])),n(i,{type:"danger",size:"small",onClick:V=>j(a)},{default:t(()=>[...e[15]||(e[15]=[c("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[H,I.value]])])]),_:1})])}}},ke=W(pe,[["__scopeId","data-v-6f55521c"]]);export{ke as default}; diff --git a/static/admin/assets/EmailPage-DbZ8F1_b.js b/static/admin/assets/EmailPage-CATruPK6.js similarity index 99% rename from static/admin/assets/EmailPage-DbZ8F1_b.js rename to static/admin/assets/EmailPage-CATruPK6.js index 4ff6fee..b7ecf9f 100644 --- a/static/admin/assets/EmailPage-DbZ8F1_b.js +++ b/static/admin/assets/EmailPage-CATruPK6.js @@ -1 +1 @@ -import{a as De,c as He,b as Ne,f as Fe,u as Ie}from"./email-CZFN9gLR.js";import{a as U,_ as ze}from"./index-BNslg8wp.js";import{M as me}from"./MetricGrid-BW8H4wTM.js";import{E as B,a as d}from"./vendor-element-B5S5pUKo.js";import{r as m,S as pe,R as Qe,c as G,o as je,aj as c,ap as Ge,n as x,q as f,t as u,F as K,L as t,D as M,E as n,I as p,J as _,G as P,K as te,a3 as ce}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function Ke(){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 Je(b){const{data:v}=await U.delete(`/smtp/configs/${b}`);return v}async function We(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 Ne();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 Ie({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)}Qe(()=>{L&&window.clearTimeout(L),L=null});const O=m(!1),R=m([]),S=m(!1),k=m(!1),J=m(!1),H=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}),N=[{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"}]}],q=m("custom"),V=G(()=>N.find(s=>s.key===q.value)||N[0]),be=G(()=>k.value&&J.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=N.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,J.value=!1,H.value=!1,q.value="custom"}async function $(){O.value=!0;try{R.value=await Ke()}catch{R.value=[]}finally{O.value=!1}}function ge(){k.value=!1,se(),q.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,J.value=!!s.has_password,H.value=!!s.is_primary,q.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 $()}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 We(a.id,s);e?.success?(d.success("测试成功,邮件已发送"),await $()):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 $()}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 $()}catch{}}}async function xe(){if(!(!k.value||!a.id)){try{await B.confirm("确定删除该SMTP配置吗?此操作不可恢复。","删除配置",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const s=await Je(a.id);if(!s?.success){d.error(s?.error||"删除失败");return}d.success("已删除"),S.value=!1,await $()}catch{}}}const A=m(!1),h=m({}),W=m(!1),F=m(""),I=m(""),z=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=G(()=>[{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=G(()=>[{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 Fe()}catch{h.value={}}finally{A.value=!1}}async function D(s=1){W.value=!0;try{const e={page:s,page_size:fe};F.value&&(e.type=F.value),I.value&&(e.status=I.value);const i=await De(e);X.value=i?.logs||[],Y.value=i?.total||0,z.value=i?.page||s,Z.value=i?.total_pages||1}catch{X.value=[],Y.value=0,Z.value=1}finally{W.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 He(s);if(!e?.success){d.error(e?.error||"清理失败");return}d.success(`已清理 ${e.deleted} 条日志`),await D(1)}catch{}}async function Le(){await Promise.all([ae(),$(),Me(),D(1)])}return je(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"),Q=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"),qe=c("el-pagination"),$e=c("el-link"),le=c("el-input-number"),Ae=c("el-dialog"),j=Ge("loading");return f(),x("div",Ze,[e[43]||(e[43]=u("div",{class:"app-page-title"},[u("h2",null,"邮件配置")],-1)),K((f(),M(Q,{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})),[[j,v.value]]),t(Q,{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,[K((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"])),[[j,O.value]])])]),_:1}),K((f(),M(Q,{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})),[[j,A.value]]),t(Q,{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:F.value,"onUpdate:modelValue":e[6]||(e[6]=l=>F.value=l),style:{width:"140px"},onChange:e[7]||(e[7]=l=>D(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:I.value,"onUpdate:modelValue":e[8]||(e[8]=l=>I.value=l),style:{width:"120px"},onChange:e[9]||(e[9]=l=>D(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,[K((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"])),[[j,W.value]])]),u("div",ml,[t(qe,{"current-page":z.value,"onUpdate:currentPage":e[10]||(e[10]=l=>z.value=l),"page-size":fe,total:Y.value,layout:"prev, pager, next, ->, total",onCurrentChange:D},null,8,["current-page","total"]),u("div",pl,"第 "+_(z.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&&H.value?(f(),M(T,{key:0,type:"warning",plain:"",onClick:Te},{default:n(()=>[...e[37]||(e[37]=[p("取消主配置",-1)])]),_:1})):P("",!0),k.value&&!H.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:q.value,"onUpdate:modelValue":e[13]||(e[13]=l=>q.value=l),placeholder:"选择常用邮箱模板",style:{width:"100%"},onChange:ye},{default:n(()=>[(f(),x(te,null,ce(N,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($e,{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=ze(gl,[["__scopeId","data-v-4f511165"]]);export{Pl as default}; +import{a as De,c as He,b as Ne,f as Fe,u as Ie}from"./email-px7YBG2O.js";import{a as U,_ as ze}from"./index-C1f9ticl.js";import{M as me}from"./MetricGrid-BnihYB_8.js";import{E as B,a as d}from"./vendor-element-B5S5pUKo.js";import{r as m,S as pe,R as Qe,c as G,o as je,aj as c,ap as Ge,n as x,q as f,t as u,F as K,L as t,D as M,E as n,I as p,J as _,G as P,K as te,a3 as ce}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function Ke(){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 Je(b){const{data:v}=await U.delete(`/smtp/configs/${b}`);return v}async function We(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 Ne();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 Ie({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)}Qe(()=>{L&&window.clearTimeout(L),L=null});const O=m(!1),R=m([]),S=m(!1),k=m(!1),J=m(!1),H=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}),N=[{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"}]}],q=m("custom"),V=G(()=>N.find(s=>s.key===q.value)||N[0]),be=G(()=>k.value&&J.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=N.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,J.value=!1,H.value=!1,q.value="custom"}async function $(){O.value=!0;try{R.value=await Ke()}catch{R.value=[]}finally{O.value=!1}}function ge(){k.value=!1,se(),q.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,J.value=!!s.has_password,H.value=!!s.is_primary,q.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 $()}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 We(a.id,s);e?.success?(d.success("测试成功,邮件已发送"),await $()):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 $()}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 $()}catch{}}}async function xe(){if(!(!k.value||!a.id)){try{await B.confirm("确定删除该SMTP配置吗?此操作不可恢复。","删除配置",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const s=await Je(a.id);if(!s?.success){d.error(s?.error||"删除失败");return}d.success("已删除"),S.value=!1,await $()}catch{}}}const A=m(!1),h=m({}),W=m(!1),F=m(""),I=m(""),z=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=G(()=>[{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=G(()=>[{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 Fe()}catch{h.value={}}finally{A.value=!1}}async function D(s=1){W.value=!0;try{const e={page:s,page_size:fe};F.value&&(e.type=F.value),I.value&&(e.status=I.value);const i=await De(e);X.value=i?.logs||[],Y.value=i?.total||0,z.value=i?.page||s,Z.value=i?.total_pages||1}catch{X.value=[],Y.value=0,Z.value=1}finally{W.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 He(s);if(!e?.success){d.error(e?.error||"清理失败");return}d.success(`已清理 ${e.deleted} 条日志`),await D(1)}catch{}}async function Le(){await Promise.all([ae(),$(),Me(),D(1)])}return je(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"),Q=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"),qe=c("el-pagination"),$e=c("el-link"),le=c("el-input-number"),Ae=c("el-dialog"),j=Ge("loading");return f(),x("div",Ze,[e[43]||(e[43]=u("div",{class:"app-page-title"},[u("h2",null,"邮件配置")],-1)),K((f(),M(Q,{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})),[[j,v.value]]),t(Q,{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,[K((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"])),[[j,O.value]])])]),_:1}),K((f(),M(Q,{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})),[[j,A.value]]),t(Q,{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:F.value,"onUpdate:modelValue":e[6]||(e[6]=l=>F.value=l),style:{width:"140px"},onChange:e[7]||(e[7]=l=>D(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:I.value,"onUpdate:modelValue":e[8]||(e[8]=l=>I.value=l),style:{width:"120px"},onChange:e[9]||(e[9]=l=>D(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,[K((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"])),[[j,W.value]])]),u("div",ml,[t(qe,{"current-page":z.value,"onUpdate:currentPage":e[10]||(e[10]=l=>z.value=l),"page-size":fe,total:Y.value,layout:"prev, pager, next, ->, total",onCurrentChange:D},null,8,["current-page","total"]),u("div",pl,"第 "+_(z.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&&H.value?(f(),M(T,{key:0,type:"warning",plain:"",onClick:Te},{default:n(()=>[...e[37]||(e[37]=[p("取消主配置",-1)])]),_:1})):P("",!0),k.value&&!H.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:q.value,"onUpdate:modelValue":e[13]||(e[13]=l=>q.value=l),placeholder:"选择常用邮箱模板",style:{width:"100%"},onChange:ye},{default:n(()=>[(f(),x(te,null,ce(N,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($e,{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=ze(gl,[["__scopeId","data-v-4f511165"]]);export{Pl as default}; diff --git a/static/admin/assets/FeedbacksPage-Dwzul2Z8.js b/static/admin/assets/FeedbacksPage-BAnFKHSL.js similarity index 97% rename from static/admin/assets/FeedbacksPage-Dwzul2Z8.js rename to static/admin/assets/FeedbacksPage-BAnFKHSL.js index 6f84a64..10ff1a4 100644 --- a/static/admin/assets/FeedbacksPage-Dwzul2Z8.js +++ b/static/admin/assets/FeedbacksPage-BAnFKHSL.js @@ -1 +1 @@ -import{_ as $,b as j,r as G,d as L,e as q}from"./index-BNslg8wp.js";import{M as J}from"./MetricGrid-BW8H4wTM.js";import{E as w,a as k}from"./vendor-element-B5S5pUKo.js";import{i as K,r as f,c as O,o as R,aj as i,ap as U,n as x,q as v,t as s,L as t,E as l,K as F,a3 as A,J as d,F as H,D as Q,I as p,G as W}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.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 T=K("refreshNavBadges",null),u=f(!1),b=f(""),c=f({total:0,pending:0,replied:0,closed:0}),_=f([]),V=[{label:"全部状态",value:""},{label:"待处理",value:"pending"},{label:"已回复",value:"replied"},{label:"已关闭",value:"closed"}],M=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 j(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 T?.({pendingFeedbacks:c.value.pending||0})}async function D(n){let a;try{a=(await w.prompt("请输入回复内容","回复反馈",{inputType:"textarea",inputPlaceholder:"回复内容",confirmButtonText:"提交",cancelButtonText:"取消",inputValidator:g=>!!String(g||"").trim(),inputErrorMessage:"回复内容不能为空"})).value}catch{return}try{const m=await G(n.id,String(a||"").trim());k.success(m?.message||"回复成功"),await r()}catch{}}async function E(n){try{await w.confirm("确定要关闭这个反馈吗?","关闭反馈",{confirmButtonText:"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}try{const a=await L(n.id);k.success(a?.message||"反馈已关闭"),await r()}catch{}}async function N(n){try{await w.confirm("确定要删除这个反馈吗?此操作不可恢复!","删除反馈",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const a=await q(n.id);k.success(a?.message||"反馈已删除"),await r()}catch{}}return R(r),(n,a)=>{const m=i("el-option"),g=i("el-select"),o=i("el-table-column"),y=i("el-tooltip"),I=i("el-tag"),h=i("el-button"),z=i("el-table"),P=i("el-card"),S=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,A(V,e=>t(m,{key:e.value,label:e.label,value:e.value},null,8,["label","value"])),64))]),_:1},8,["modelValue"])])]),t(J,{items:M.value,loading:u.value,"min-width":165},null,8,["items","loading"]),t(P,{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,[H((v(),Q(z,{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(I,{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=>D(e)},{default:l(()=>[...a[3]||(a[3]=[p("回复",-1)])]),_:1},8,["onClick"]),t(h,{size:"small",onClick:C=>E(e)},{default:l(()=>[...a[4]||(a[4]=[p("关闭",-1)])]),_:1},8,["onClick"])],64)):W("",!0),t(h,{type:"danger",size:"small",onClick:C=>N(e)},{default:l(()=>[...a[5]||(a[5]=[p("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[S,u.value]])])]),_:1})])}}},fe=$(ie,[["__scopeId","data-v-910fe89b"]]);export{fe as default}; +import{_ as $,b as j,r as G,d as L,e as q}from"./index-C1f9ticl.js";import{M as J}from"./MetricGrid-BnihYB_8.js";import{E as w,a as k}from"./vendor-element-B5S5pUKo.js";import{i as K,r as f,c as O,o as R,aj as i,ap as U,n as x,q as v,t as s,L as t,E as l,K as F,a3 as A,J as d,F as H,D as Q,I as p,G as W}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.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 T=K("refreshNavBadges",null),u=f(!1),b=f(""),c=f({total:0,pending:0,replied:0,closed:0}),_=f([]),V=[{label:"全部状态",value:""},{label:"待处理",value:"pending"},{label:"已回复",value:"replied"},{label:"已关闭",value:"closed"}],M=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 j(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 T?.({pendingFeedbacks:c.value.pending||0})}async function D(n){let a;try{a=(await w.prompt("请输入回复内容","回复反馈",{inputType:"textarea",inputPlaceholder:"回复内容",confirmButtonText:"提交",cancelButtonText:"取消",inputValidator:g=>!!String(g||"").trim(),inputErrorMessage:"回复内容不能为空"})).value}catch{return}try{const m=await G(n.id,String(a||"").trim());k.success(m?.message||"回复成功"),await r()}catch{}}async function E(n){try{await w.confirm("确定要关闭这个反馈吗?","关闭反馈",{confirmButtonText:"关闭",cancelButtonText:"取消",type:"warning"})}catch{return}try{const a=await L(n.id);k.success(a?.message||"反馈已关闭"),await r()}catch{}}async function N(n){try{await w.confirm("确定要删除这个反馈吗?此操作不可恢复!","删除反馈",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{const a=await q(n.id);k.success(a?.message||"反馈已删除"),await r()}catch{}}return R(r),(n,a)=>{const m=i("el-option"),g=i("el-select"),o=i("el-table-column"),y=i("el-tooltip"),I=i("el-tag"),h=i("el-button"),z=i("el-table"),P=i("el-card"),S=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,A(V,e=>t(m,{key:e.value,label:e.label,value:e.value},null,8,["label","value"])),64))]),_:1},8,["modelValue"])])]),t(J,{items:M.value,loading:u.value,"min-width":165},null,8,["items","loading"]),t(P,{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,[H((v(),Q(z,{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(I,{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=>D(e)},{default:l(()=>[...a[3]||(a[3]=[p("回复",-1)])]),_:1},8,["onClick"]),t(h,{size:"small",onClick:C=>E(e)},{default:l(()=>[...a[4]||(a[4]=[p("关闭",-1)])]),_:1},8,["onClick"])],64)):W("",!0),t(h,{type:"danger",size:"small",onClick:C=>N(e)},{default:l(()=>[...a[5]||(a[5]=[p("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[S,u.value]])])]),_:1})])}}},fe=$(ie,[["__scopeId","data-v-910fe89b"]]);export{fe as default}; diff --git a/static/admin/assets/LogsPage-Bs3Ge-t3.js b/static/admin/assets/LogsPage-DFPeq0bL.js similarity index 98% rename from static/admin/assets/LogsPage-Bs3Ge-t3.js rename to static/admin/assets/LogsPage-DFPeq0bL.js index 284589c..df8db0f 100644 --- a/static/admin/assets/LogsPage-Bs3Ge-t3.js +++ b/static/admin/assets/LogsPage-DFPeq0bL.js @@ -1 +1 @@ -import{f as K}from"./users-BowyvQzr.js";import{g as G,h as H}from"./tasks-DqqATNe_.js";import{_ as Q}from"./index-BNslg8wp.js";import{E as N,a as X}from"./vendor-element-B5S5pUKo.js";import{r as u,c as Z,o as ee,aj as r,ap as te,n as P,q as b,t as f,L as t,E as l,K as ae,a3 as le,D as F,I as m,F as oe,J as d}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.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"},B=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)/B)));function Y(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 I(o){return o==="success"?{label:"成功",type:"success"}:o==="failed"?{label:"失败",type:"danger"}:{label:o||"-",type:"info"}}async function z(){v.value=!0;try{const o=await K();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)*B,e={limit:B,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 j(){h.value="",w.value="",V.value="",x.value="",k.value="",i.value=1,M()}async function R(){let o;try{const e=await N.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 N.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 z(),await M()}),(o,e)=>{const g=r("el-date-picker"),n=r("el-option"),U=r("el-select"),W=r("el-input"),C=r("el-button"),L=r("el-card"),c=r("el-table-column"),D=r("el-tag"),E=r("el-tooltip"),q=r("el-table"),A=r("el-pagination"),J=te("loading");return b(),P("div",re,[e[9]||(e[9]=f("div",{class:"app-page-title"},[f("h2",null,"任务日志")],-1)),t(L,{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),P(ae,null,le(T.value,a=>(b(),F(n,{key:a.id,label:a.username,value:String(a.id)},null,8,["label","value"]))),128))]),_:1},8,["modelValue","loading"]),t(W,{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:j},{default:l(()=>[...e[7]||(e[7]=[m("重置",-1)])]),_:1}),t(C,{type:"danger",plain:"",onClick:R},{default:l(()=>[...e[8]||(e[8]=[m("清理旧日志",-1)])]),_:1})])]),_:1}),t(L,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[f("div",ce,[oe((b(),F(q,{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(),F(E,{key:0,content:_(a.source).tooltip,placement:"top","show-after":300},{default:l(()=>[t(D,{type:_(a.source).type,effect:"light"},{default:l(()=>[m(d(_(a.source).label),1)]),_:2},1032,["type"])]),_:2},1032,["content"])):(b(),F(D,{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(D,{type:I(a.status).type,effect:"light"},{default:l(()=>[m(d(I(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(Y(a.duration)),1)]),_:1}),t(c,{label:"失败原因","min-width":"220"},{default:l(({row:a})=>[t(E,{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"])),[[J,s.value]])]),f("div",pe,[t(A,{"current-page":i.value,"onUpdate:currentPage":e[5]||(e[5]=a=>i.value=a),"page-size":B,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=Q(fe,[["__scopeId","data-v-8803eb08"]]);export{Ve as default}; +import{f as K}from"./users-te9ySk34.js";import{g as G,h as H}from"./tasks-Bep0SUyu.js";import{_ as Q}from"./index-C1f9ticl.js";import{E as N,a as X}from"./vendor-element-B5S5pUKo.js";import{r as u,c as Z,o as ee,aj as r,ap as te,n as P,q as b,t as f,L as t,E as l,K as ae,a3 as le,D as F,I as m,F as oe,J as d}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.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"},B=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)/B)));function Y(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 I(o){return o==="success"?{label:"成功",type:"success"}:o==="failed"?{label:"失败",type:"danger"}:{label:o||"-",type:"info"}}async function z(){v.value=!0;try{const o=await K();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)*B,e={limit:B,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 j(){h.value="",w.value="",V.value="",x.value="",k.value="",i.value=1,M()}async function R(){let o;try{const e=await N.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 N.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 z(),await M()}),(o,e)=>{const g=r("el-date-picker"),n=r("el-option"),U=r("el-select"),W=r("el-input"),C=r("el-button"),L=r("el-card"),c=r("el-table-column"),D=r("el-tag"),E=r("el-tooltip"),q=r("el-table"),A=r("el-pagination"),J=te("loading");return b(),P("div",re,[e[9]||(e[9]=f("div",{class:"app-page-title"},[f("h2",null,"任务日志")],-1)),t(L,{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),P(ae,null,le(T.value,a=>(b(),F(n,{key:a.id,label:a.username,value:String(a.id)},null,8,["label","value"]))),128))]),_:1},8,["modelValue","loading"]),t(W,{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:j},{default:l(()=>[...e[7]||(e[7]=[m("重置",-1)])]),_:1}),t(C,{type:"danger",plain:"",onClick:R},{default:l(()=>[...e[8]||(e[8]=[m("清理旧日志",-1)])]),_:1})])]),_:1}),t(L,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[f("div",ce,[oe((b(),F(q,{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(),F(E,{key:0,content:_(a.source).tooltip,placement:"top","show-after":300},{default:l(()=>[t(D,{type:_(a.source).type,effect:"light"},{default:l(()=>[m(d(_(a.source).label),1)]),_:2},1032,["type"])]),_:2},1032,["content"])):(b(),F(D,{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(D,{type:I(a.status).type,effect:"light"},{default:l(()=>[m(d(I(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(Y(a.duration)),1)]),_:1}),t(c,{label:"失败原因","min-width":"220"},{default:l(({row:a})=>[t(E,{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"])),[[J,s.value]])]),f("div",pe,[t(A,{"current-page":i.value,"onUpdate:currentPage":e[5]||(e[5]=a=>i.value=a),"page-size":B,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=Q(fe,[["__scopeId","data-v-8803eb08"]]);export{Ve as default}; diff --git a/static/admin/assets/MetricGrid-BW8H4wTM.js b/static/admin/assets/MetricGrid-BnihYB_8.js similarity index 94% rename from static/admin/assets/MetricGrid-BW8H4wTM.js rename to static/admin/assets/MetricGrid-BnihYB_8.js index 3dd472e..4412335 100644 --- a/static/admin/assets/MetricGrid-BW8H4wTM.js +++ b/static/admin/assets/MetricGrid-BnihYB_8.js @@ -1 +1 @@ -import{_}from"./index-BNslg8wp.js";import{aj as c,n as s,q as t,K as r,a3 as u,y as p,t as o,G as l,L as y,E as h,D as i,H as v,J as n,I as k,x as f}from"./vendor-vue-CVxSw_oJ.js";const b={class:"metric-top"},x={key:0,class:"metric-icon"},g={class:"metric-label"},B={class:"metric-value"},C={key:0,class:"metric-hint app-muted"},N={__name:"MetricGrid",props:{items:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},minWidth:{type:Number,default:180}},setup(a){return(V,D)=>{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(v(e.icon)))]),_:2},1024)])):l("",!0),o("div",g,n(e?.label||"-"),1)]),o("div",B,[a.loading?(t(),i(m,{key:0,rows:1,animated:""})):(t(),s(r,{key:1},[k(n(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",C,n(e?.hint||e?.sub),1)):l("",!0)],2))),128))],4)}}},w=_(N,[["__scopeId","data-v-00e217d4"]]);export{w as M}; +import{_}from"./index-C1f9ticl.js";import{aj as c,n as s,q as t,K as r,a3 as u,y as p,t as o,G as l,L as y,E as h,D as i,H as v,J as n,I as k,x as f}from"./vendor-vue-CVxSw_oJ.js";const b={class:"metric-top"},x={key:0,class:"metric-icon"},g={class:"metric-label"},B={class:"metric-value"},C={key:0,class:"metric-hint app-muted"},N={__name:"MetricGrid",props:{items:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},minWidth:{type:Number,default:180}},setup(a){return(V,D)=>{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(v(e.icon)))]),_:2},1024)])):l("",!0),o("div",g,n(e?.label||"-"),1)]),o("div",B,[a.loading?(t(),i(m,{key:0,rows:1,animated:""})):(t(),s(r,{key:1},[k(n(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",C,n(e?.hint||e?.sub),1)):l("",!0)],2))),128))],4)}}},w=_(N,[["__scopeId","data-v-00e217d4"]]);export{w as M}; diff --git a/static/admin/assets/ReportPage-CqUGYZcC.js b/static/admin/assets/ReportPage-fzVH-d9u.js similarity index 98% rename from static/admin/assets/ReportPage-CqUGYZcC.js rename to static/admin/assets/ReportPage-fzVH-d9u.js index 28d25ff..d39b07c 100644 --- a/static/admin/assets/ReportPage-CqUGYZcC.js +++ b/static/admin/assets/ReportPage-fzVH-d9u.js @@ -1 +1 @@ -import{f as tl,g as al,h as sl,k as ul,j as ol,n as nl,o as rl}from"./vendor-element-B5S5pUKo.js";import{c as il,a as cl,_ as vl,f as dl}from"./index-BNslg8wp.js";import{f as _l}from"./email-CZFN9gLR.js";import{f as ml,a as pl,b as bl,c as fl,d as hl,e as wl}from"./tasks-DqqATNe_.js";import{f as gl}from"./system-CiDlQnoe.js";import{M as kl}from"./MetricGrid-BW8H4wTM.js";import{i as de,r as m,c as a,o as yl,O as Sl,aj as R,n as S,q as k,t as s,L as u,G as D,J as d,K as _e,a3 as me,E as w,I as pe,D as ql,y as xl}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-misc-BeoNyvBp.js";import"./vendor-axios-B9ygI19o.js";const $l=il(async()=>{const{data:P}=await cl.get("/browser_pool/stats");return P},4e3);async function Tl(P={}){return $l.run(P)}const Ll={class:"page-stack"},Rl={class:"report-hero"},Ml={class:"hero-head"},Al={class:"hero-main"},Pl={class:"hero-meta app-muted"},Cl={key:0},Nl={key:1,class:"hero-dot"},Il={key:2},Dl={class:"mobile-report"},El={class:"mobile-module-head"},Wl={class:"mobile-module-title"},Ql={class:"mobile-module-desc app-muted"},Vl={class:"mobile-metrics"},zl={class:"mobile-metric-label app-muted"},Bl={class:"mobile-metric-value"},Ol={key:0,class:"module-extra-actions"},Ul={class:"request-dialog-summary app-muted"},Fl={class:"request-dialog-block"},Gl={class:"table-wrap"},jl={class:"request-dialog-block"},Hl={class:"table-wrap"},Zl={class:"request-dialog-summary app-muted"},Yl={class:"request-dialog-block"},Jl={class:"table-wrap"},Kl={class:"request-dialog-block"},Xl={class:"table-wrap"},et=3,lt=5e3,tt=2e4,at={__name:"ReportPage",setup(P){const be=de("refreshStats",null),fe=de("adminStats",null),E=m(!1),W=m(!1),T=m(""),Q=m(null),b=m(null),p=m(null),y=m(null),g=m(null),C=m(null),L=m(null),f=m(null),v=m(null),r=m(null),V=m(!1),z=m(!1);m("running");function he(){try{T.value=new Date().toLocaleString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{T.value=""}}function l(t){const e=Number(t);return Number.isFinite(e)?e:0}function we(t){const e=String(t??"").trim();if(!e)return 0;const o=e.endsWith("%")?e.slice(0,-1):e,_=Number(o);return!Number.isFinite(_)||_<0?0:_>1e3?1e3:_}function B(t){return`${Math.round(we(t))}%`}function c(t){const e=Number(t);return!Number.isFinite(e)||e<0?"-":e>=100?`${Math.round(e)}ms`:`${e.toFixed(1)}ms`}function Z(t){const e=Number(t);if(!Number.isFinite(e)||e<=0)return"-";try{return new Date(e*1e3).toLocaleTimeString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{return"-"}}function O(t){const e=Number(t);if(!Number.isFinite(e)||e<=0)return"-";try{return new Date(e*1e3).toLocaleString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{return"-"}}function ge(t){const e=String(t??"").trim();return!e||e==="manual"?"手动":e==="scheduled"?"系统定时":e==="batch"?"批量执行":e==="resumed"?"断点续跑":e.startsWith("user_scheduled:")?"用户定时":e}const Y=a(()=>{const t=fe?.value||{},e=l(b.value?.max_concurrent);return[{label:"总用户数",value:l(t.total_users),icon:tl,tone:"blue"},{label:"今日注册",value:l(t.new_users_today),icon:al,tone:"green"},{label:"近7天注册",value:l(t.new_users_7d),icon:sl,tone:"purple"},{label:"总账号数",value:l(t.total_accounts),icon:ul,tone:"cyan"},{label:"VIP用户",value:l(t.vip_users),icon:ol,tone:"orange"},{label:"运行中任务",value:l(b.value?.running_count),icon:nl,tone:"green",sub:e?`并发上限 ${e}`:""},{label:"排队任务",value:l(b.value?.queuing_count),icon:rl,tone:"purple"}]}),h=a(()=>Q.value?.today||{}),q=a(()=>Q.value?.total||{});a(()=>b.value?.running||[]);const N=a(()=>b.value?.queuing||[]),ke=a(()=>l(b.value?.running_count)),ye=a(()=>l(b.value?.queuing_count)),Se=a(()=>{const t=L.value?.workers;return Array.isArray(t)?[...t].sort((e,o)=>l(e?.worker_id)-l(o?.worker_id)):[]}),J=a(()=>l(L.value?.total_workers)),U=a(()=>Se.value.filter(t=>!!t?.has_browser).length),K=a(()=>l(L.value?.idle_workers)),X=a(()=>l(L.value?.queue_size)),F=a(()=>l(L.value?.active_workers)),ee=a(()=>{const t=l(h.value.success_tasks),e=l(h.value.failed_tasks),o=t+e;return o>0?Math.round(t/o*1e3)/10:0}),G=a(()=>l(p.value?.success_rate));a(()=>[{label:"总任务",value:l(h.value.total_tasks),tone:"blue"},{label:"成功",value:l(h.value.success_tasks),tone:"green"},{label:"失败",value:l(h.value.failed_tasks),tone:"red"},{label:"浏览内容",value:l(h.value.total_items),tone:"purple"},{label:"查看附件",value:l(h.value.total_attachments),tone:"cyan"}]),a(()=>[{label:"总任务",value:l(q.value.total_tasks),tone:"blue"},{label:"成功",value:l(q.value.success_tasks),tone:"green"},{label:"失败",value:l(q.value.failed_tasks),tone:"red"},{label:"浏览内容",value:l(q.value.total_items),tone:"purple"},{label:"查看附件",value:l(q.value.total_attachments),tone:"cyan"}]),a(()=>[{label:"总发送",value:l(p.value?.total_sent),tone:"blue"},{label:"成功",value:l(p.value?.total_success),tone:"green"},{label:"失败",value:l(p.value?.total_failed),tone:"red"},{label:"成功率",value:`${G.value}%`,tone:"purple"}]),a(()=>[{label:"注册验证",value:l(p.value?.register_sent),tone:"cyan"},{label:"密码重置",value:l(p.value?.reset_sent),tone:"orange"},{label:"邮箱绑定",value:l(p.value?.bind_sent),tone:"purple"},{label:"任务完成",value:l(p.value?.task_complete_sent),tone:"green"}]),a(()=>[{label:"总反馈",value:l(y.value?.total),tone:"blue"},{label:"待处理",value:l(y.value?.pending),tone:"orange"},{label:"已回复",value:l(y.value?.replied),tone:"green"}]),a(()=>[{label:"总 Worker",value:J.value,tone:"blue"},{label:"活跃 Worker",value:U.value,tone:"green"},{label:"空闲 Worker",value:K.value,tone:"cyan"},{label:"忙碌 Worker",value:F.value,tone:"orange"},{label:"队列",value:X.value,tone:"purple"}]);const qe=a(()=>(f.value?.schedule_enabled??0)===1),xe=a(()=>f.value?.schedule_time||"-"),$e=a(()=>f.value?.schedule_browse_type||"-"),Te=a(()=>String(f.value?.schedule_weekdays||"").trim());a(()=>{const t=Te.value;if(!t)return"";const e={1:"周一",2:"周二",3:"周三",4:"周四",5:"周五",6:"周六",7:"周日"},o=t.split(",").map(_=>_.trim()).filter(Boolean);return o.length?o.map(_=>e[Number(_)]||_).join("、"):t});const Le=a(()=>(f.value?.proxy_enabled??0)===1);a(()=>f.value?.proxy_api_url||"");const le=a(()=>l(f.value?.proxy_expire_minutes)),j=a(()=>l(f.value?.max_concurrent_global)),Re=a(()=>l(f.value?.max_concurrent_per_account)),Me=a(()=>l(f.value?.max_screenshot_concurrent)),Ae=a(()=>{const t=l(b.value?.running_count),e=l(b.value?.queuing_count),o=l(b.value?.max_concurrent);return`运行中 ${t} / 排队 ${e} / 并发上限 ${o||j.value||"-"}`}),Pe=a(()=>Y.value.map(t=>({label:t.label,value:t.sub?`${t.value}(${t.sub})`:t.value}))),Ce=a(()=>[{label:"今日总任务",value:l(h.value.total_tasks)},{label:"今日成功",value:l(h.value.success_tasks)},{label:"今日失败",value:l(h.value.failed_tasks)},{label:"今日成功率",value:`${ee.value}%`},{label:"累计任务",value:l(q.value.total_tasks)},{label:"累计成功",value:l(q.value.success_tasks)}]),Ne=a(()=>[{label:"运行中",value:ke.value},{label:"排队中",value:ye.value},{label:"并发上限",value:l(b.value?.max_concurrent)||j.value||"-"},{label:"排队首条来源",value:ge(N.value[0]?.source)},{label:"排队首条状态",value:N.value[0]?.detail_status||N.value[0]?.status||"-"},{label:"最长等待",value:N.value[0]?.elapsed_display||"-"}]),Ie=a(()=>[{label:"总发送",value:l(p.value?.total_sent)},{label:"成功",value:l(p.value?.total_success)},{label:"失败",value:l(p.value?.total_failed)},{label:"成功率",value:`${G.value}%`},{label:"注册验证",value:l(p.value?.register_sent)},{label:"重置密码",value:l(p.value?.reset_sent)}]),De=a(()=>[{label:"总反馈",value:l(y.value?.total)},{label:"待处理",value:l(y.value?.pending)},{label:"已回复",value:l(y.value?.replied)}]),Ee=a(()=>[{label:"CPU",value:B(g.value?.cpu_percent)},{label:"内存",value:B(g.value?.memory_percent)},{label:"磁盘",value:B(g.value?.disk_percent)},{label:"容器状态",value:C.value?.status||"-"},{label:"容器名",value:C.value?.container_name||"-"},{label:"容器运行",value:C.value?.uptime||"-"}]),We=a(()=>[{label:"总 Worker",value:J.value},{label:"活跃 Worker",value:U.value},{label:"忙碌 Worker",value:F.value},{label:"空闲 Worker",value:K.value},{label:"任务队列",value:X.value}]),Qe=a(()=>{const t=v.value?.top_paths;return Array.isArray(t)?t.slice(0,3):[]}),Ve=a(()=>{const t=[{label:"总请求",value:l(v.value?.total_requests)},{label:"API请求",value:l(v.value?.api_requests)},{label:"慢请求",value:l(v.value?.slow_requests)},{label:"错误请求",value:l(v.value?.error_requests)}];return Qe.value.forEach((e,o)=>{const _=String(e?.path||"-");t.push({label:`慢接口${o+1}`,value:`${_} · 峰值 ${c(e?.max_ms)}`})}),t}),ze=a(()=>{const t=c(v.value?.avg_duration_ms),e=c(v.value?.max_duration_ms),o=Z(v.value?.last_request_ts),_=c(v.value?.slow_threshold_ms);return`均值 ${t} · 峰值 ${e} · 慢阈 ${_} · 最近 ${o}`}),Be=a(()=>(Array.isArray(r.value?.top_sql)?r.value.top_sql:[]).slice(0,3)),te=a(()=>{const t=l(r.value?.window_seconds);return t<=0?24:Math.max(1,Math.round(t/3600))}),Oe=a(()=>{const t=[{label:`慢SQL(${te.value}h)`,value:l(r.value?.total_slow_queries)},{label:"去重SQL",value:l(r.value?.unique_sql)},{label:"平均耗时",value:c(r.value?.avg_duration_ms)},{label:"峰值耗时",value:c(r.value?.max_duration_ms)}];return Be.value.forEach((e,o)=>{t.push({label:`慢SQL${o+1}`,value:`${c(e?.max_ms)} · ${String(e?.sql||"-")}`})}),t}),Ue=a(()=>{const t=c(r.value?.slow_threshold_ms),e=Z(r.value?.last_slow_ts);return`窗口 ${te.value}h · 慢阈 ${t} · 最近 ${e}`}),Fe=a(()=>(Array.isArray(r.value?.top_sql)?r.value.top_sql:[]).map((e,o)=>({rank:o+1,sql:String(e?.sql||"-"),count:l(e?.count),avg_ms:c(e?.avg_ms),max_ms:c(e?.max_ms),last_seen:O(e?.last_ts),sample_params:String(e?.sample_params||"-")}))),Ge=a(()=>[...Array.isArray(r.value?.recent_slow_sql)?r.value.recent_slow_sql:[]].sort((e,o)=>Number(o?.time||0)-Number(e?.time||0)).map(e=>({time_text:O(e?.time),sql:String(e?.sql||"-"),duration_ms:c(e?.duration_ms),params:String(e?.params||"-")}))),je=a(()=>(Array.isArray(v.value?.top_paths)?v.value.top_paths:[]).map((e,o)=>({rank:o+1,path:String(e?.path||"-"),count:l(e?.count),avg_ms:c(e?.avg_ms),max_ms:c(e?.max_ms),status_5xx:l(e?.status_5xx)}))),He=a(()=>[...Array.isArray(v.value?.recent_slow)?v.value.recent_slow:[]].sort((e,o)=>Number(o?.time||0)-Number(e?.time||0)).map(e=>({time_text:O(e?.time),method:String(e?.method||"-").toUpperCase(),path:String(e?.path||"-"),status:l(e?.status),duration_ms:c(e?.duration_ms)})));function Ze(t){const e=l(t);return e>=500?"danger":e>=400?"warning":e>=300?"info":"success"}function Ye(){V.value=!0}function Je(){z.value=!0}const ae=a(()=>{const t=r.value?.slow_threshold_ms;if(t!=null)return c(t);const e=f.value?.db_slow_query_ms;return e!=null?c(e):"-"}),Ke=a(()=>[{label:"定时任务",value:qe.value?"启用":"关闭"},{label:"执行时间",value:xe.value||"-"},{label:"浏览类型",value:$e.value||"-"},{label:"代理",value:Le.value?"启用":"关闭"},{label:"代理有效期",value:le.value?`${le.value} 分钟`:"-"},{label:"全局并发",value:j.value||"-"},{label:"单账号并发",value:Re.value||"-"},{label:"截图并发",value:Me.value||"-"},{label:"慢SQL阈值",value:ae.value}]),Xe=a(()=>[{key:"overview",title:"平台概览",desc:T.value?`更新 ${T.value}`:"核心指标",tone:"blue",items:Pe.value},{key:"task",title:"任务概览",desc:l(h.value.total_tasks)>0?`今日成功率 ${ee.value}%`:"今日暂无任务",tone:"purple",items:Ce.value},{key:"queue",title:"队列监控",desc:Ae.value,tone:"blue",items:Ne.value},{key:"email",title:"邮件报表",desc:`成功率 ${G.value}%`,tone:"cyan",items:Ie.value},{key:"feedback",title:"反馈概览",desc:`待处理 ${l(y.value?.pending)} 条`,tone:"orange",items:De.value},{key:"resource",title:"系统资源",desc:g.value?.uptime?`运行 ${g.value.uptime}`:"运行状态获取中",tone:"green",items:Ee.value},{key:"request",title:"接口性能",desc:ze.value,tone:"purple",items:Ve.value},{key:"slow_sql",title:"慢SQL监控",desc:Ue.value,tone:"red",items:Oe.value},{key:"worker",title:"截图线程池",desc:`活跃 ${U.value} · 忙碌 ${F.value}`,tone:"cyan",items:We.value},{key:"config",title:"配置概览",desc:"并发 / 代理 / 定时任务",tone:"red",items:Ke.value}]);let se=1;async function ue(t={}){const e=t.showLoading??!0;if(W.value)return;const o=!!t.forceStatsSync,_=o||se%et===0;se+=1,W.value=!0,e&&(E.value=!0);try{const[n,x,I,A,i,$,re,ie,ce,ve]=await Promise.allSettled([ml(),pl(),_l(),dl(),bl(),fl(),Tl(),hl(),wl(),gl()]);n.status==="fulfilled"&&(Q.value=n.value),x.status==="fulfilled"&&(b.value=x.value),I.status==="fulfilled"&&(p.value=I.value),A.status==="fulfilled"&&(y.value=A.value),i.status==="fulfilled"&&(g.value=i.value),$.status==="fulfilled"&&(C.value=$.value),re.status==="fulfilled"&&(L.value=re.value),ie.status==="fulfilled"&&(v.value=ie.value),ce.status==="fulfilled"&&(r.value=ce.value),ve.status==="fulfilled"&&(f.value=ve.value),_&&await be?.({force:o}),he()}finally{W.value=!1,e&&(E.value=!1)}}let M=null;function el(){return typeof document>"u"?!1:document.visibilityState==="hidden"}function ll(){return el()?tt:lt}function oe(){M&&(clearTimeout(M),M=null)}function H(){oe(),M=window.setTimeout(async()=>{M=null,await ue({showLoading:!1}).catch(()=>{}),H()},ll())}function ne(){H()}return yl(()=>{ue({showLoading:!1}).catch(()=>{}).finally(()=>{H()}),window.addEventListener("visibilitychange",ne)}),Sl(()=>{oe(),window.removeEventListener("visibilitychange",ne)}),(t,e)=>{const o=R("el-button"),_=R("el-card"),n=R("el-table-column"),x=R("el-table"),I=R("el-tag"),A=R("el-dialog");return k(),S("div",Ll,[s("section",Rl,[s("div",Ml,[s("div",Al,[e[3]||(e[3]=s("h2",null,"报表中心",-1)),s("div",Pl,[T.value?(k(),S("span",Cl,"更新时间:"+d(T.value),1)):D("",!0),e[2]||(e[2]=s("span",{class:"hero-dot"},"·",-1)),s("span",null,"慢SQL阈值 "+d(ae.value),1),g.value?.uptime?(k(),S("span",Nl,"·")):D("",!0),g.value?.uptime?(k(),S("span",Il,"运行 "+d(g.value.uptime),1)):D("",!0)])])]),u(kl,{class:"hero-overview-grid",items:Y.value,loading:E.value,"min-width":165},null,8,["items","loading"])]),s("section",Dl,[(k(!0),S(_e,null,me(Xe.value,i=>(k(),ql(_,{key:i.key,shadow:"never",class:xl(["mobile-module-card",`mobile-tone-${i.tone}`]),"body-style":{padding:"12px"}},{default:w(()=>[s("div",El,[s("div",Wl,d(i.title),1),s("div",Ql,d(i.desc),1)]),s("div",Vl,[(k(!0),S(_e,null,me(i.items,$=>(k(),S("div",{key:`${i.key}-${$.label}`,class:"mobile-metric-item"},[s("div",zl,d($.label),1),s("div",Bl,d($.value),1)]))),128))]),i.key==="request"||i.key==="slow_sql"?(k(),S("div",Ol,[u(o,{size:"small",type:"primary",plain:"",onClick:$=>i.key==="request"?Ye():Je()},{default:w(()=>[pe(d(i.key==="request"?"查看慢接口详情":"查看慢SQL详情"),1)]),_:2},1032,["onClick"])])):D("",!0)]),_:2},1032,["class"]))),128))]),u(A,{modelValue:V.value,"onUpdate:modelValue":e[0]||(e[0]=i=>V.value=i),title:"慢接口详情",width:"min(1080px, 96vw)"},{default:w(()=>[s("div",Ul,[s("span",null,"总请求:"+d(l(v.value?.total_requests)),1),s("span",null,"API请求:"+d(l(v.value?.api_requests)),1),s("span",null,"慢请求:"+d(l(v.value?.slow_requests)),1),s("span",null,"错误请求:"+d(l(v.value?.error_requests)),1)]),s("div",Fl,[e[4]||(e[4]=s("div",{class:"request-dialog-title"},"慢接口排行榜",-1)),s("div",Gl,[u(x,{data:je.value,size:"small","max-height":"280"},{default:w(()=>[u(n,{prop:"rank",label:"#",width:"60"}),u(n,{prop:"path",label:"接口路径","min-width":"340","show-overflow-tooltip":""}),u(n,{prop:"count",label:"请求数",width:"100"}),u(n,{prop:"avg_ms",label:"平均耗时",width:"120"}),u(n,{prop:"max_ms",label:"峰值耗时",width:"120"}),u(n,{prop:"status_5xx",label:"5xx",width:"90"})]),_:1},8,["data"])])]),s("div",jl,[e[5]||(e[5]=s("div",{class:"request-dialog-title"},"最近慢请求",-1)),s("div",Hl,[u(x,{data:He.value,size:"small","max-height":"320"},{default:w(()=>[u(n,{prop:"time_text",label:"时间",width:"180"}),u(n,{prop:"method",label:"方法",width:"90"}),u(n,{prop:"path",label:"接口路径","min-width":"320","show-overflow-tooltip":""}),u(n,{label:"状态",width:"100"},{default:w(i=>[u(I,{size:"small",type:Ze(i.row.status)},{default:w(()=>[pe(d(i.row.status||"-"),1)]),_:2},1032,["type"])]),_:1}),u(n,{prop:"duration_ms",label:"耗时",width:"110"})]),_:1},8,["data"])])])]),_:1},8,["modelValue"]),u(A,{modelValue:z.value,"onUpdate:modelValue":e[1]||(e[1]=i=>z.value=i),title:"慢SQL详情(近24小时)",width:"min(1080px, 96vw)"},{default:w(()=>[s("div",Zl,[s("span",null,"慢SQL总数:"+d(l(r.value?.total_slow_queries)),1),s("span",null,"去重SQL:"+d(l(r.value?.unique_sql)),1),s("span",null,"平均耗时:"+d(c(r.value?.avg_duration_ms)),1),s("span",null,"峰值耗时:"+d(c(r.value?.max_duration_ms)),1),s("span",null,"慢阈值:"+d(c(r.value?.slow_threshold_ms)),1)]),s("div",Yl,[e[6]||(e[6]=s("div",{class:"request-dialog-title"},"TOP 慢SQL(按出现次数)",-1)),s("div",Jl,[u(x,{data:Fe.value,size:"small","max-height":"320"},{default:w(()=>[u(n,{prop:"rank",label:"#",width:"60"}),u(n,{prop:"sql",label:"SQL","min-width":"400","show-overflow-tooltip":""}),u(n,{prop:"count",label:"次数",width:"90"}),u(n,{prop:"avg_ms",label:"平均耗时",width:"120"}),u(n,{prop:"max_ms",label:"峰值耗时",width:"120"}),u(n,{prop:"last_seen",label:"最近出现",width:"180"}),u(n,{prop:"sample_params",label:"参数样本","min-width":"140","show-overflow-tooltip":""})]),_:1},8,["data"])])]),s("div",Kl,[e[7]||(e[7]=s("div",{class:"request-dialog-title"},"最近慢SQL",-1)),s("div",Xl,[u(x,{data:Ge.value,size:"small","max-height":"320"},{default:w(()=>[u(n,{prop:"time_text",label:"时间",width:"180"}),u(n,{prop:"sql",label:"SQL","min-width":"420","show-overflow-tooltip":""}),u(n,{prop:"duration_ms",label:"耗时",width:"110"}),u(n,{prop:"params",label:"参数","min-width":"130","show-overflow-tooltip":""})]),_:1},8,["data"])])])]),_:1},8,["modelValue"])])}}},_t=vl(at,[["__scopeId","data-v-d610e788"]]);export{_t as default}; +import{f as tl,g as al,h as sl,k as ul,j as ol,n as nl,o as rl}from"./vendor-element-B5S5pUKo.js";import{c as il,a as cl,_ as vl,f as dl}from"./index-C1f9ticl.js";import{f as _l}from"./email-px7YBG2O.js";import{f as ml,a as pl,b as bl,c as fl,d as hl,e as wl}from"./tasks-Bep0SUyu.js";import{f as gl}from"./system-ZDPnxnIu.js";import{M as kl}from"./MetricGrid-BnihYB_8.js";import{i as de,r as m,c as a,o as yl,O as Sl,aj as R,n as S,q as k,t as s,L as u,G as D,J as d,K as _e,a3 as me,E as w,I as pe,D as ql,y as xl}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-misc-BeoNyvBp.js";import"./vendor-axios-B9ygI19o.js";const $l=il(async()=>{const{data:P}=await cl.get("/browser_pool/stats");return P},4e3);async function Tl(P={}){return $l.run(P)}const Ll={class:"page-stack"},Rl={class:"report-hero"},Ml={class:"hero-head"},Al={class:"hero-main"},Pl={class:"hero-meta app-muted"},Cl={key:0},Nl={key:1,class:"hero-dot"},Il={key:2},Dl={class:"mobile-report"},El={class:"mobile-module-head"},Wl={class:"mobile-module-title"},Ql={class:"mobile-module-desc app-muted"},Vl={class:"mobile-metrics"},zl={class:"mobile-metric-label app-muted"},Bl={class:"mobile-metric-value"},Ol={key:0,class:"module-extra-actions"},Ul={class:"request-dialog-summary app-muted"},Fl={class:"request-dialog-block"},Gl={class:"table-wrap"},jl={class:"request-dialog-block"},Hl={class:"table-wrap"},Zl={class:"request-dialog-summary app-muted"},Yl={class:"request-dialog-block"},Jl={class:"table-wrap"},Kl={class:"request-dialog-block"},Xl={class:"table-wrap"},et=3,lt=5e3,tt=2e4,at={__name:"ReportPage",setup(P){const be=de("refreshStats",null),fe=de("adminStats",null),E=m(!1),W=m(!1),T=m(""),Q=m(null),b=m(null),p=m(null),y=m(null),g=m(null),C=m(null),L=m(null),f=m(null),v=m(null),r=m(null),V=m(!1),z=m(!1);m("running");function he(){try{T.value=new Date().toLocaleString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{T.value=""}}function l(t){const e=Number(t);return Number.isFinite(e)?e:0}function we(t){const e=String(t??"").trim();if(!e)return 0;const o=e.endsWith("%")?e.slice(0,-1):e,_=Number(o);return!Number.isFinite(_)||_<0?0:_>1e3?1e3:_}function B(t){return`${Math.round(we(t))}%`}function c(t){const e=Number(t);return!Number.isFinite(e)||e<0?"-":e>=100?`${Math.round(e)}ms`:`${e.toFixed(1)}ms`}function Z(t){const e=Number(t);if(!Number.isFinite(e)||e<=0)return"-";try{return new Date(e*1e3).toLocaleTimeString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{return"-"}}function O(t){const e=Number(t);if(!Number.isFinite(e)||e<=0)return"-";try{return new Date(e*1e3).toLocaleString("zh-CN",{hour12:!1,timeZone:"Asia/Shanghai"})}catch{return"-"}}function ge(t){const e=String(t??"").trim();return!e||e==="manual"?"手动":e==="scheduled"?"系统定时":e==="batch"?"批量执行":e==="resumed"?"断点续跑":e.startsWith("user_scheduled:")?"用户定时":e}const Y=a(()=>{const t=fe?.value||{},e=l(b.value?.max_concurrent);return[{label:"总用户数",value:l(t.total_users),icon:tl,tone:"blue"},{label:"今日注册",value:l(t.new_users_today),icon:al,tone:"green"},{label:"近7天注册",value:l(t.new_users_7d),icon:sl,tone:"purple"},{label:"总账号数",value:l(t.total_accounts),icon:ul,tone:"cyan"},{label:"VIP用户",value:l(t.vip_users),icon:ol,tone:"orange"},{label:"运行中任务",value:l(b.value?.running_count),icon:nl,tone:"green",sub:e?`并发上限 ${e}`:""},{label:"排队任务",value:l(b.value?.queuing_count),icon:rl,tone:"purple"}]}),h=a(()=>Q.value?.today||{}),q=a(()=>Q.value?.total||{});a(()=>b.value?.running||[]);const N=a(()=>b.value?.queuing||[]),ke=a(()=>l(b.value?.running_count)),ye=a(()=>l(b.value?.queuing_count)),Se=a(()=>{const t=L.value?.workers;return Array.isArray(t)?[...t].sort((e,o)=>l(e?.worker_id)-l(o?.worker_id)):[]}),J=a(()=>l(L.value?.total_workers)),U=a(()=>Se.value.filter(t=>!!t?.has_browser).length),K=a(()=>l(L.value?.idle_workers)),X=a(()=>l(L.value?.queue_size)),F=a(()=>l(L.value?.active_workers)),ee=a(()=>{const t=l(h.value.success_tasks),e=l(h.value.failed_tasks),o=t+e;return o>0?Math.round(t/o*1e3)/10:0}),G=a(()=>l(p.value?.success_rate));a(()=>[{label:"总任务",value:l(h.value.total_tasks),tone:"blue"},{label:"成功",value:l(h.value.success_tasks),tone:"green"},{label:"失败",value:l(h.value.failed_tasks),tone:"red"},{label:"浏览内容",value:l(h.value.total_items),tone:"purple"},{label:"查看附件",value:l(h.value.total_attachments),tone:"cyan"}]),a(()=>[{label:"总任务",value:l(q.value.total_tasks),tone:"blue"},{label:"成功",value:l(q.value.success_tasks),tone:"green"},{label:"失败",value:l(q.value.failed_tasks),tone:"red"},{label:"浏览内容",value:l(q.value.total_items),tone:"purple"},{label:"查看附件",value:l(q.value.total_attachments),tone:"cyan"}]),a(()=>[{label:"总发送",value:l(p.value?.total_sent),tone:"blue"},{label:"成功",value:l(p.value?.total_success),tone:"green"},{label:"失败",value:l(p.value?.total_failed),tone:"red"},{label:"成功率",value:`${G.value}%`,tone:"purple"}]),a(()=>[{label:"注册验证",value:l(p.value?.register_sent),tone:"cyan"},{label:"密码重置",value:l(p.value?.reset_sent),tone:"orange"},{label:"邮箱绑定",value:l(p.value?.bind_sent),tone:"purple"},{label:"任务完成",value:l(p.value?.task_complete_sent),tone:"green"}]),a(()=>[{label:"总反馈",value:l(y.value?.total),tone:"blue"},{label:"待处理",value:l(y.value?.pending),tone:"orange"},{label:"已回复",value:l(y.value?.replied),tone:"green"}]),a(()=>[{label:"总 Worker",value:J.value,tone:"blue"},{label:"活跃 Worker",value:U.value,tone:"green"},{label:"空闲 Worker",value:K.value,tone:"cyan"},{label:"忙碌 Worker",value:F.value,tone:"orange"},{label:"队列",value:X.value,tone:"purple"}]);const qe=a(()=>(f.value?.schedule_enabled??0)===1),xe=a(()=>f.value?.schedule_time||"-"),$e=a(()=>f.value?.schedule_browse_type||"-"),Te=a(()=>String(f.value?.schedule_weekdays||"").trim());a(()=>{const t=Te.value;if(!t)return"";const e={1:"周一",2:"周二",3:"周三",4:"周四",5:"周五",6:"周六",7:"周日"},o=t.split(",").map(_=>_.trim()).filter(Boolean);return o.length?o.map(_=>e[Number(_)]||_).join("、"):t});const Le=a(()=>(f.value?.proxy_enabled??0)===1);a(()=>f.value?.proxy_api_url||"");const le=a(()=>l(f.value?.proxy_expire_minutes)),j=a(()=>l(f.value?.max_concurrent_global)),Re=a(()=>l(f.value?.max_concurrent_per_account)),Me=a(()=>l(f.value?.max_screenshot_concurrent)),Ae=a(()=>{const t=l(b.value?.running_count),e=l(b.value?.queuing_count),o=l(b.value?.max_concurrent);return`运行中 ${t} / 排队 ${e} / 并发上限 ${o||j.value||"-"}`}),Pe=a(()=>Y.value.map(t=>({label:t.label,value:t.sub?`${t.value}(${t.sub})`:t.value}))),Ce=a(()=>[{label:"今日总任务",value:l(h.value.total_tasks)},{label:"今日成功",value:l(h.value.success_tasks)},{label:"今日失败",value:l(h.value.failed_tasks)},{label:"今日成功率",value:`${ee.value}%`},{label:"累计任务",value:l(q.value.total_tasks)},{label:"累计成功",value:l(q.value.success_tasks)}]),Ne=a(()=>[{label:"运行中",value:ke.value},{label:"排队中",value:ye.value},{label:"并发上限",value:l(b.value?.max_concurrent)||j.value||"-"},{label:"排队首条来源",value:ge(N.value[0]?.source)},{label:"排队首条状态",value:N.value[0]?.detail_status||N.value[0]?.status||"-"},{label:"最长等待",value:N.value[0]?.elapsed_display||"-"}]),Ie=a(()=>[{label:"总发送",value:l(p.value?.total_sent)},{label:"成功",value:l(p.value?.total_success)},{label:"失败",value:l(p.value?.total_failed)},{label:"成功率",value:`${G.value}%`},{label:"注册验证",value:l(p.value?.register_sent)},{label:"重置密码",value:l(p.value?.reset_sent)}]),De=a(()=>[{label:"总反馈",value:l(y.value?.total)},{label:"待处理",value:l(y.value?.pending)},{label:"已回复",value:l(y.value?.replied)}]),Ee=a(()=>[{label:"CPU",value:B(g.value?.cpu_percent)},{label:"内存",value:B(g.value?.memory_percent)},{label:"磁盘",value:B(g.value?.disk_percent)},{label:"容器状态",value:C.value?.status||"-"},{label:"容器名",value:C.value?.container_name||"-"},{label:"容器运行",value:C.value?.uptime||"-"}]),We=a(()=>[{label:"总 Worker",value:J.value},{label:"活跃 Worker",value:U.value},{label:"忙碌 Worker",value:F.value},{label:"空闲 Worker",value:K.value},{label:"任务队列",value:X.value}]),Qe=a(()=>{const t=v.value?.top_paths;return Array.isArray(t)?t.slice(0,3):[]}),Ve=a(()=>{const t=[{label:"总请求",value:l(v.value?.total_requests)},{label:"API请求",value:l(v.value?.api_requests)},{label:"慢请求",value:l(v.value?.slow_requests)},{label:"错误请求",value:l(v.value?.error_requests)}];return Qe.value.forEach((e,o)=>{const _=String(e?.path||"-");t.push({label:`慢接口${o+1}`,value:`${_} · 峰值 ${c(e?.max_ms)}`})}),t}),ze=a(()=>{const t=c(v.value?.avg_duration_ms),e=c(v.value?.max_duration_ms),o=Z(v.value?.last_request_ts),_=c(v.value?.slow_threshold_ms);return`均值 ${t} · 峰值 ${e} · 慢阈 ${_} · 最近 ${o}`}),Be=a(()=>(Array.isArray(r.value?.top_sql)?r.value.top_sql:[]).slice(0,3)),te=a(()=>{const t=l(r.value?.window_seconds);return t<=0?24:Math.max(1,Math.round(t/3600))}),Oe=a(()=>{const t=[{label:`慢SQL(${te.value}h)`,value:l(r.value?.total_slow_queries)},{label:"去重SQL",value:l(r.value?.unique_sql)},{label:"平均耗时",value:c(r.value?.avg_duration_ms)},{label:"峰值耗时",value:c(r.value?.max_duration_ms)}];return Be.value.forEach((e,o)=>{t.push({label:`慢SQL${o+1}`,value:`${c(e?.max_ms)} · ${String(e?.sql||"-")}`})}),t}),Ue=a(()=>{const t=c(r.value?.slow_threshold_ms),e=Z(r.value?.last_slow_ts);return`窗口 ${te.value}h · 慢阈 ${t} · 最近 ${e}`}),Fe=a(()=>(Array.isArray(r.value?.top_sql)?r.value.top_sql:[]).map((e,o)=>({rank:o+1,sql:String(e?.sql||"-"),count:l(e?.count),avg_ms:c(e?.avg_ms),max_ms:c(e?.max_ms),last_seen:O(e?.last_ts),sample_params:String(e?.sample_params||"-")}))),Ge=a(()=>[...Array.isArray(r.value?.recent_slow_sql)?r.value.recent_slow_sql:[]].sort((e,o)=>Number(o?.time||0)-Number(e?.time||0)).map(e=>({time_text:O(e?.time),sql:String(e?.sql||"-"),duration_ms:c(e?.duration_ms),params:String(e?.params||"-")}))),je=a(()=>(Array.isArray(v.value?.top_paths)?v.value.top_paths:[]).map((e,o)=>({rank:o+1,path:String(e?.path||"-"),count:l(e?.count),avg_ms:c(e?.avg_ms),max_ms:c(e?.max_ms),status_5xx:l(e?.status_5xx)}))),He=a(()=>[...Array.isArray(v.value?.recent_slow)?v.value.recent_slow:[]].sort((e,o)=>Number(o?.time||0)-Number(e?.time||0)).map(e=>({time_text:O(e?.time),method:String(e?.method||"-").toUpperCase(),path:String(e?.path||"-"),status:l(e?.status),duration_ms:c(e?.duration_ms)})));function Ze(t){const e=l(t);return e>=500?"danger":e>=400?"warning":e>=300?"info":"success"}function Ye(){V.value=!0}function Je(){z.value=!0}const ae=a(()=>{const t=r.value?.slow_threshold_ms;if(t!=null)return c(t);const e=f.value?.db_slow_query_ms;return e!=null?c(e):"-"}),Ke=a(()=>[{label:"定时任务",value:qe.value?"启用":"关闭"},{label:"执行时间",value:xe.value||"-"},{label:"浏览类型",value:$e.value||"-"},{label:"代理",value:Le.value?"启用":"关闭"},{label:"代理有效期",value:le.value?`${le.value} 分钟`:"-"},{label:"全局并发",value:j.value||"-"},{label:"单账号并发",value:Re.value||"-"},{label:"截图并发",value:Me.value||"-"},{label:"慢SQL阈值",value:ae.value}]),Xe=a(()=>[{key:"overview",title:"平台概览",desc:T.value?`更新 ${T.value}`:"核心指标",tone:"blue",items:Pe.value},{key:"task",title:"任务概览",desc:l(h.value.total_tasks)>0?`今日成功率 ${ee.value}%`:"今日暂无任务",tone:"purple",items:Ce.value},{key:"queue",title:"队列监控",desc:Ae.value,tone:"blue",items:Ne.value},{key:"email",title:"邮件报表",desc:`成功率 ${G.value}%`,tone:"cyan",items:Ie.value},{key:"feedback",title:"反馈概览",desc:`待处理 ${l(y.value?.pending)} 条`,tone:"orange",items:De.value},{key:"resource",title:"系统资源",desc:g.value?.uptime?`运行 ${g.value.uptime}`:"运行状态获取中",tone:"green",items:Ee.value},{key:"request",title:"接口性能",desc:ze.value,tone:"purple",items:Ve.value},{key:"slow_sql",title:"慢SQL监控",desc:Ue.value,tone:"red",items:Oe.value},{key:"worker",title:"截图线程池",desc:`活跃 ${U.value} · 忙碌 ${F.value}`,tone:"cyan",items:We.value},{key:"config",title:"配置概览",desc:"并发 / 代理 / 定时任务",tone:"red",items:Ke.value}]);let se=1;async function ue(t={}){const e=t.showLoading??!0;if(W.value)return;const o=!!t.forceStatsSync,_=o||se%et===0;se+=1,W.value=!0,e&&(E.value=!0);try{const[n,x,I,A,i,$,re,ie,ce,ve]=await Promise.allSettled([ml(),pl(),_l(),dl(),bl(),fl(),Tl(),hl(),wl(),gl()]);n.status==="fulfilled"&&(Q.value=n.value),x.status==="fulfilled"&&(b.value=x.value),I.status==="fulfilled"&&(p.value=I.value),A.status==="fulfilled"&&(y.value=A.value),i.status==="fulfilled"&&(g.value=i.value),$.status==="fulfilled"&&(C.value=$.value),re.status==="fulfilled"&&(L.value=re.value),ie.status==="fulfilled"&&(v.value=ie.value),ce.status==="fulfilled"&&(r.value=ce.value),ve.status==="fulfilled"&&(f.value=ve.value),_&&await be?.({force:o}),he()}finally{W.value=!1,e&&(E.value=!1)}}let M=null;function el(){return typeof document>"u"?!1:document.visibilityState==="hidden"}function ll(){return el()?tt:lt}function oe(){M&&(clearTimeout(M),M=null)}function H(){oe(),M=window.setTimeout(async()=>{M=null,await ue({showLoading:!1}).catch(()=>{}),H()},ll())}function ne(){H()}return yl(()=>{ue({showLoading:!1}).catch(()=>{}).finally(()=>{H()}),window.addEventListener("visibilitychange",ne)}),Sl(()=>{oe(),window.removeEventListener("visibilitychange",ne)}),(t,e)=>{const o=R("el-button"),_=R("el-card"),n=R("el-table-column"),x=R("el-table"),I=R("el-tag"),A=R("el-dialog");return k(),S("div",Ll,[s("section",Rl,[s("div",Ml,[s("div",Al,[e[3]||(e[3]=s("h2",null,"报表中心",-1)),s("div",Pl,[T.value?(k(),S("span",Cl,"更新时间:"+d(T.value),1)):D("",!0),e[2]||(e[2]=s("span",{class:"hero-dot"},"·",-1)),s("span",null,"慢SQL阈值 "+d(ae.value),1),g.value?.uptime?(k(),S("span",Nl,"·")):D("",!0),g.value?.uptime?(k(),S("span",Il,"运行 "+d(g.value.uptime),1)):D("",!0)])])]),u(kl,{class:"hero-overview-grid",items:Y.value,loading:E.value,"min-width":165},null,8,["items","loading"])]),s("section",Dl,[(k(!0),S(_e,null,me(Xe.value,i=>(k(),ql(_,{key:i.key,shadow:"never",class:xl(["mobile-module-card",`mobile-tone-${i.tone}`]),"body-style":{padding:"12px"}},{default:w(()=>[s("div",El,[s("div",Wl,d(i.title),1),s("div",Ql,d(i.desc),1)]),s("div",Vl,[(k(!0),S(_e,null,me(i.items,$=>(k(),S("div",{key:`${i.key}-${$.label}`,class:"mobile-metric-item"},[s("div",zl,d($.label),1),s("div",Bl,d($.value),1)]))),128))]),i.key==="request"||i.key==="slow_sql"?(k(),S("div",Ol,[u(o,{size:"small",type:"primary",plain:"",onClick:$=>i.key==="request"?Ye():Je()},{default:w(()=>[pe(d(i.key==="request"?"查看慢接口详情":"查看慢SQL详情"),1)]),_:2},1032,["onClick"])])):D("",!0)]),_:2},1032,["class"]))),128))]),u(A,{modelValue:V.value,"onUpdate:modelValue":e[0]||(e[0]=i=>V.value=i),title:"慢接口详情",width:"min(1080px, 96vw)"},{default:w(()=>[s("div",Ul,[s("span",null,"总请求:"+d(l(v.value?.total_requests)),1),s("span",null,"API请求:"+d(l(v.value?.api_requests)),1),s("span",null,"慢请求:"+d(l(v.value?.slow_requests)),1),s("span",null,"错误请求:"+d(l(v.value?.error_requests)),1)]),s("div",Fl,[e[4]||(e[4]=s("div",{class:"request-dialog-title"},"慢接口排行榜",-1)),s("div",Gl,[u(x,{data:je.value,size:"small","max-height":"280"},{default:w(()=>[u(n,{prop:"rank",label:"#",width:"60"}),u(n,{prop:"path",label:"接口路径","min-width":"340","show-overflow-tooltip":""}),u(n,{prop:"count",label:"请求数",width:"100"}),u(n,{prop:"avg_ms",label:"平均耗时",width:"120"}),u(n,{prop:"max_ms",label:"峰值耗时",width:"120"}),u(n,{prop:"status_5xx",label:"5xx",width:"90"})]),_:1},8,["data"])])]),s("div",jl,[e[5]||(e[5]=s("div",{class:"request-dialog-title"},"最近慢请求",-1)),s("div",Hl,[u(x,{data:He.value,size:"small","max-height":"320"},{default:w(()=>[u(n,{prop:"time_text",label:"时间",width:"180"}),u(n,{prop:"method",label:"方法",width:"90"}),u(n,{prop:"path",label:"接口路径","min-width":"320","show-overflow-tooltip":""}),u(n,{label:"状态",width:"100"},{default:w(i=>[u(I,{size:"small",type:Ze(i.row.status)},{default:w(()=>[pe(d(i.row.status||"-"),1)]),_:2},1032,["type"])]),_:1}),u(n,{prop:"duration_ms",label:"耗时",width:"110"})]),_:1},8,["data"])])])]),_:1},8,["modelValue"]),u(A,{modelValue:z.value,"onUpdate:modelValue":e[1]||(e[1]=i=>z.value=i),title:"慢SQL详情(近24小时)",width:"min(1080px, 96vw)"},{default:w(()=>[s("div",Zl,[s("span",null,"慢SQL总数:"+d(l(r.value?.total_slow_queries)),1),s("span",null,"去重SQL:"+d(l(r.value?.unique_sql)),1),s("span",null,"平均耗时:"+d(c(r.value?.avg_duration_ms)),1),s("span",null,"峰值耗时:"+d(c(r.value?.max_duration_ms)),1),s("span",null,"慢阈值:"+d(c(r.value?.slow_threshold_ms)),1)]),s("div",Yl,[e[6]||(e[6]=s("div",{class:"request-dialog-title"},"TOP 慢SQL(按出现次数)",-1)),s("div",Jl,[u(x,{data:Fe.value,size:"small","max-height":"320"},{default:w(()=>[u(n,{prop:"rank",label:"#",width:"60"}),u(n,{prop:"sql",label:"SQL","min-width":"400","show-overflow-tooltip":""}),u(n,{prop:"count",label:"次数",width:"90"}),u(n,{prop:"avg_ms",label:"平均耗时",width:"120"}),u(n,{prop:"max_ms",label:"峰值耗时",width:"120"}),u(n,{prop:"last_seen",label:"最近出现",width:"180"}),u(n,{prop:"sample_params",label:"参数样本","min-width":"140","show-overflow-tooltip":""})]),_:1},8,["data"])])]),s("div",Kl,[e[7]||(e[7]=s("div",{class:"request-dialog-title"},"最近慢SQL",-1)),s("div",Xl,[u(x,{data:Ge.value,size:"small","max-height":"320"},{default:w(()=>[u(n,{prop:"time_text",label:"时间",width:"180"}),u(n,{prop:"sql",label:"SQL","min-width":"420","show-overflow-tooltip":""}),u(n,{prop:"duration_ms",label:"耗时",width:"110"}),u(n,{prop:"params",label:"参数","min-width":"130","show-overflow-tooltip":""})]),_:1},8,["data"])])])]),_:1},8,["modelValue"])])}}},_t=vl(at,[["__scopeId","data-v-d610e788"]]);export{_t as default}; diff --git a/static/admin/assets/SecurityPage-BARcvvk8.js b/static/admin/assets/SecurityPage-xwMQfhuh.js similarity index 99% rename from static/admin/assets/SecurityPage-BARcvvk8.js rename to static/admin/assets/SecurityPage-xwMQfhuh.js index 2fdaf7c..abef170 100644 --- a/static/admin/assets/SecurityPage-BARcvvk8.js +++ b/static/admin/assets/SecurityPage-xwMQfhuh.js @@ -1,4 +1,4 @@ -import{a as g,_ as qe}from"./index-BNslg8wp.js";import{M as Ee}from"./MetricGrid-BW8H4wTM.js";import{E as ee,a as w}from"./vendor-element-B5S5pUKo.js";import{r as d,c as oe,o as ze,aj as v,ap as Ae,n as P,q as c,t as p,L as a,E as l,I as i,K as Ge,a3 as Ke,D as b,F as te,J as f,G as ue}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function Oe(){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 Je(){const{data:u}=await g.get("/admin/security/banned-ips");return u}async function Qe(){const{data:u}=await g.get("/admin/security/banned-users");return u}async function We(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"},Tt={class:"dialog-actions"},re=20,Pt={__name:"SecurityPage",setup(u){const m=d("threats"),V=d(!1),L=d(null),ae=d(!1),q=d([]),E=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}),z=d("ip"),k=d(!1),A=d(""),G=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 D(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 K(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=L.value?.recent_threat_events||[];for(const s of e){const y=String(s?.threat_type||"").trim();y&&n.add(y)}for(const s of q.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=L.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((E.value||0)/re)));async function N(){V.value=!0;try{L.value=await Oe()}catch{L.value=null}finally{V.value=!1}}async function O(){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);q.value=e?.items||[],E.value=e?.total||0}catch{q.value=[],E.value=0}finally{ae.value=!1}}async function F(){if(!$.value){$.value=!0;try{const[n,e]=await Promise.allSettled([Je(),Qe()]);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([N(),O(),F()])}function Se(){C.value=1,O()}function Te(){U.value="",B.value="",C.value=1,O()}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 Pe(){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 We({ip:I,reason:e,duration_hours:y,permanent:s}),w.success("IP已封禁"),S.value=!1,await Promise.allSettled([N(),F()])}catch{}finally{R.value=!1}return}const Q=String(o.value.user_id||"").trim(),r=Number.parseInt(Q,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([N(),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([N(),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([N(),F()])}catch{}}}function ge(n){const e=String(n||"").trim();e&&(m.value="risk",z.value="ip",A.value=e,J())}function ke(n){const e=String(n||"").trim();e&&(m.value="risk",z.value="user",G.value=e,ne())}async function J(){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(G.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 J()):(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 qe}from"./index-C1f9ticl.js";import{M as Ee}from"./MetricGrid-BnihYB_8.js";import{E as ee,a as w}from"./vendor-element-B5S5pUKo.js";import{r as d,c as oe,o as ze,aj as v,ap as Ae,n as P,q as c,t as p,L as a,E as l,I as i,K as Ge,a3 as Ke,D as b,F as te,J as f,G as ue}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function Oe(){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 Je(){const{data:u}=await g.get("/admin/security/banned-ips");return u}async function Qe(){const{data:u}=await g.get("/admin/security/banned-users");return u}async function We(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"},Tt={class:"dialog-actions"},re=20,Pt={__name:"SecurityPage",setup(u){const m=d("threats"),V=d(!1),L=d(null),ae=d(!1),q=d([]),E=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}),z=d("ip"),k=d(!1),A=d(""),G=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 D(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 K(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=L.value?.recent_threat_events||[];for(const s of e){const y=String(s?.threat_type||"").trim();y&&n.add(y)}for(const s of q.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=L.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((E.value||0)/re)));async function N(){V.value=!0;try{L.value=await Oe()}catch{L.value=null}finally{V.value=!1}}async function O(){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);q.value=e?.items||[],E.value=e?.total||0}catch{q.value=[],E.value=0}finally{ae.value=!1}}async function F(){if(!$.value){$.value=!0;try{const[n,e]=await Promise.allSettled([Je(),Qe()]);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([N(),O(),F()])}function Se(){C.value=1,O()}function Te(){U.value="",B.value="",C.value=1,O()}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 Pe(){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 We({ip:I,reason:e,duration_hours:y,permanent:s}),w.success("IP已封禁"),S.value=!1,await Promise.allSettled([N(),F()])}catch{}finally{R.value=!1}return}const Q=String(o.value.user_id||"").trim(),r=Number.parseInt(Q,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([N(),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([N(),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([N(),F()])}catch{}}}function ge(n){const e=String(n||"").trim();e&&(m.value="risk",z.value="ip",A.value=e,J())}function ke(n){const e=String(n||"").trim();e&&(m.value="risk",z.value="user",G.value=e,ne())}async function J(){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(G.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 J()):(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 J()}}}const ie=d(!1);async function Re(){try{await ee.confirm(`确定清理过期封禁记录,并衰减风险分吗? diff --git a/static/admin/assets/SettingsPage-Bblx2O3L.js b/static/admin/assets/SettingsPage-DRqlQLxJ.js similarity index 99% rename from static/admin/assets/SettingsPage-Bblx2O3L.js rename to static/admin/assets/SettingsPage-DRqlQLxJ.js index 1b034d3..415e6da 100644 --- a/static/admin/assets/SettingsPage-Bblx2O3L.js +++ b/static/admin/assets/SettingsPage-DRqlQLxJ.js @@ -1 +1 @@ -import{a as v,_ as L}from"./index-BNslg8wp.js";import{a as u,E as O}from"./vendor-element-B5S5pUKo.js";import{r as p,o as Y,aj as m,ap as Z,n as N,q as C,t as b,L as r,E as o,I as S,F as Q,D as K}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function W(t){const{data:e}=await v.put("/admin/username",{new_username:t});return e}async function ee(t={}){const e=String(t.currentPassword||""),n=String(t.newPassword||""),{data:l}=await v.put("/admin/password",{current_password:e,new_password:n});return l}async function te(){const{data:t}=await v.post("/logout");return t}async function ae(){const{data:t}=await v.get("/admin/passkeys");return t}async function $(t={}){const{data:e}=await v.post("/admin/passkeys/register/options",t);return e}async function ne(t={}){const{data:e}=await v.post("/admin/passkeys/register/verify",t);return e}async function se(t){const{data:e}=await v.delete(`/admin/passkeys/${t}`);return e}async function re(t={}){const{data:e}=await v.post("/admin/passkeys/client-error",t);return e}function le(t){if(!t||typeof t!="object")throw new Error("Passkey参数无效");return t.publicKey&&typeof t.publicKey=="object"?t.publicKey:t}function D(t){const e=String(t||""),n="=".repeat((4-e.length%4)%4),l=(e+n).replace(/-/g,"+").replace(/_/g,"/"),y=window.atob(l),i=new Uint8Array(y.length);for(let w=0;w({...l,id:D(l.id)}))),n}function ie(t){if(!t)return null;const e=t.response||{},n={id:t.id,rawId:h(t.rawId),type:t.type,authenticatorAttachment:t.authenticatorAttachment||void 0,response:{}};return e.clientDataJSON&&(n.response.clientDataJSON=h(e.clientDataJSON)),e.attestationObject&&(n.response.attestationObject=h(e.attestationObject)),e.authenticatorData&&(n.response.authenticatorData=h(e.authenticatorData)),e.signature&&(n.response.signature=h(e.signature)),e.userHandle?n.response.userHandle=h(e.userHandle):n.response.userHandle=null,typeof e.getTransports=="function"&&(n.response.transports=e.getTransports()||[]),n}function ue(){return typeof window<"u"&&window.isSecureContext&&!!window.PublicKeyCredential&&!!navigator.credentials}function de(){const t=String(window?.navigator?.userAgent||"");return/MiuiBrowser|XiaoMi\/MiuiBrowser/i.test(t)}function ce(t,e="Passkey操作"){const n=String(t?.name||"").trim(),l=String(t?.message||"").trim();return n==="NotAllowedError"?`${e}未完成(可能已取消、超时或设备未响应)`:n==="NotReadableError"?/credential manager/i.test(l)&&de()?"当前小米浏览器与系统凭据管理器兼容性较差,请改用系统 Chrome 或 Edge 后重试。":/credential manager/i.test(l)?"系统凭据管理器返回异常,请确认已设置系统锁屏并改用系统 Chrome/Edge 后重试。":l||`${e}失败(设备读取异常)`:n==="SecurityError"?"当前环境安全策略不满足 Passkey 要求,请确认使用 HTTPS 且证书有效。":l||`${e}失败`}async function pe(t){const e=oe(t),n=await navigator.credentials.create({publicKey:e});return ie(n)}const fe={class:"page-stack"},me=24e4,ye={__name:"SettingsPage",setup(t){const e=p(""),n=p(""),l=p(""),y=p(""),i=p(!1),w=p(!1),V=p(!1),A=p(""),k=p([]),g=p(null),_=p(0);function H(s){const a=String(s||"");return a.length<8?{ok:!1,message:"密码长度至少8位"}:a.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(a)||!/\d/.test(a)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function U(){try{await te()}catch{}finally{window.location.href="/yuyx"}}async function I(){const s=e.value.trim();if(!s){u.error("请输入新用户名");return}try{await O.confirm(`确定将管理员用户名修改为「${s}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}i.value=!0;try{await W(s),u.success("用户名修改成功,请重新登录"),e.value="",setTimeout(U,1200)}catch{}finally{i.value=!1}}async function R(){const s=n.value,a=l.value,d=y.value;if(!s){u.error("请输入当前密码");return}if(!a){u.error("请输入新密码");return}const f=H(a);if(!f.ok){u.error(f.message);return}if(a!==d){u.error("两次输入的新密码不一致");return}try{await O.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}i.value=!0;try{await ee({currentPassword:s,newPassword:a}),u.success("密码修改成功,请重新登录"),n.value="",l.value="",y.value="",setTimeout(U,1200)}catch{}finally{i.value=!1}}async function T(){w.value=!0;try{const s=await ae();k.value=Array.isArray(s?.items)?s.items:[],k.value.length<3?await M():(g.value=null,_.value=0)}catch{k.value=[],g.value=null,_.value=0}finally{w.value=!1}}function j(){return!g.value||Date.now()-Number(_.value||0)>me?null:g.value}async function M(){try{const s=await $({});g.value=s,_.value=Date.now()}catch{g.value=null,_.value=0}}async function z(){if(!ue()){u.error("当前浏览器或环境不支持Passkey(需 HTTPS)");return}if(k.value.length>=3){u.error("最多可绑定3台设备");return}V.value=!0;try{let s=j();s||(s=await $({}));const a=await pe(s?.publicKey||{});await ne({credential:a,device_name:A.value.trim()}),g.value=null,_.value=0,A.value="",u.success("Passkey设备添加成功"),await T()}catch(s){try{await re({stage:"register",source:"admin-settings",name:s?.name||"",message:s?.message||"",code:s?.code||"",user_agent:navigator.userAgent||""})}catch{}g.value=null,_.value=0,await M();const d=s?.response?.data?.error||ce(s,"Passkey注册");u.error(d)}finally{V.value=!1}}async function J(s){try{await O.confirm(`确定删除设备「${s?.device_name||"未命名设备"}」吗?`,"删除Passkey设备",{confirmButtonText:"删除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await se(s.id),u.success("设备已删除"),await T()}catch(a){const d=a?.response?.data;u.error(d?.error||"删除失败")}}return Y(()=>{T()}),(s,a)=>{const d=m("el-input"),f=m("el-form-item"),E=m("el-form"),x=m("el-button"),B=m("el-card"),F=m("el-alert"),X=m("el-empty"),P=m("el-table-column"),q=m("el-table"),G=Z("loading");return C(),N("div",fe,[a[13]||(a[13]=b("div",{class:"app-page-title"},[b("h2",null,"设置"),b("span",{class:"app-muted"},"管理员账号设置")],-1)),r(B,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[a[6]||(a[6]=b("h3",{class:"section-title"},"修改管理员用户名",-1)),r(E,{"label-width":"120px"},{default:o(()=>[r(f,{label:"新用户名"},{default:o(()=>[r(d,{modelValue:e.value,"onUpdate:modelValue":a[0]||(a[0]=c=>e.value=c),placeholder:"输入新用户名",disabled:i.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),r(x,{type:"primary",loading:i.value,onClick:I},{default:o(()=>[...a[5]||(a[5]=[S("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),r(B,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[a[8]||(a[8]=b("h3",{class:"section-title"},"修改管理员密码",-1)),r(E,{"label-width":"120px"},{default:o(()=>[r(f,{label:"当前密码"},{default:o(()=>[r(d,{modelValue:n.value,"onUpdate:modelValue":a[1]||(a[1]=c=>n.value=c),type:"password","show-password":"",placeholder:"输入当前密码",disabled:i.value},null,8,["modelValue","disabled"])]),_:1}),r(f,{label:"新密码"},{default:o(()=>[r(d,{modelValue:l.value,"onUpdate:modelValue":a[2]||(a[2]=c=>l.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:i.value},null,8,["modelValue","disabled"])]),_:1}),r(f,{label:"确认新密码"},{default:o(()=>[r(d,{modelValue:y.value,"onUpdate:modelValue":a[3]||(a[3]=c=>y.value=c),type:"password","show-password":"",placeholder:"再次输入新密码",disabled:i.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),r(x,{type:"primary",loading:i.value,onClick:R},{default:o(()=>[...a[7]||(a[7]=[S("保存密码",-1)])]),_:1},8,["loading"]),a[9]||(a[9]=b("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1}),r(B,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[a[12]||(a[12]=b("h3",{class:"section-title"},"Passkey设备",-1)),r(F,{type:"info",closable:!1,title:"最多可绑定3台设备,可用于管理员无密码登录。","show-icon":"",class:"help-alert"}),r(E,{inline:""},{default:o(()=>[r(f,{label:"设备备注"},{default:o(()=>[r(d,{modelValue:A.value,"onUpdate:modelValue":a[4]||(a[4]=c=>A.value=c),placeholder:"例如:值班iPhone / 办公Mac",maxlength:"40","show-word-limit":""},null,8,["modelValue"])]),_:1}),r(f,null,{default:o(()=>[r(x,{type:"primary",loading:V.value,onClick:z},{default:o(()=>[...a[10]||(a[10]=[S("添加Passkey设备",-1)])]),_:1},8,["loading"])]),_:1})]),_:1}),Q((C(),N("div",null,[k.value.length===0?(C(),K(X,{key:0,description:"暂无Passkey设备"})):(C(),K(q,{key:1,data:k.value,size:"small",style:{width:"100%"}},{default:o(()=>[r(P,{prop:"device_name",label:"设备备注","min-width":"160"}),r(P,{prop:"credential_id_preview",label:"凭据ID","min-width":"180"}),r(P,{prop:"last_used_at",label:"最近使用","min-width":"140"}),r(P,{prop:"created_at",label:"创建时间","min-width":"140"}),r(P,{label:"操作",width:"100",fixed:"right"},{default:o(({row:c})=>[r(x,{type:"danger",text:"",onClick:we=>J(c)},{default:o(()=>[...a[11]||(a[11]=[S("删除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"]))])),[[G,w.value]])]),_:1})])}}},he=L(ye,[["__scopeId","data-v-fb202365"]]);export{he as default}; +import{a as v,_ as L}from"./index-C1f9ticl.js";import{a as u,E as O}from"./vendor-element-B5S5pUKo.js";import{r as p,o as Y,aj as m,ap as Z,n as N,q as C,t as b,L as r,E as o,I as S,F as Q,D as K}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function W(t){const{data:e}=await v.put("/admin/username",{new_username:t});return e}async function ee(t={}){const e=String(t.currentPassword||""),n=String(t.newPassword||""),{data:l}=await v.put("/admin/password",{current_password:e,new_password:n});return l}async function te(){const{data:t}=await v.post("/logout");return t}async function ae(){const{data:t}=await v.get("/admin/passkeys");return t}async function $(t={}){const{data:e}=await v.post("/admin/passkeys/register/options",t);return e}async function ne(t={}){const{data:e}=await v.post("/admin/passkeys/register/verify",t);return e}async function se(t){const{data:e}=await v.delete(`/admin/passkeys/${t}`);return e}async function re(t={}){const{data:e}=await v.post("/admin/passkeys/client-error",t);return e}function le(t){if(!t||typeof t!="object")throw new Error("Passkey参数无效");return t.publicKey&&typeof t.publicKey=="object"?t.publicKey:t}function D(t){const e=String(t||""),n="=".repeat((4-e.length%4)%4),l=(e+n).replace(/-/g,"+").replace(/_/g,"/"),y=window.atob(l),i=new Uint8Array(y.length);for(let w=0;w({...l,id:D(l.id)}))),n}function ie(t){if(!t)return null;const e=t.response||{},n={id:t.id,rawId:h(t.rawId),type:t.type,authenticatorAttachment:t.authenticatorAttachment||void 0,response:{}};return e.clientDataJSON&&(n.response.clientDataJSON=h(e.clientDataJSON)),e.attestationObject&&(n.response.attestationObject=h(e.attestationObject)),e.authenticatorData&&(n.response.authenticatorData=h(e.authenticatorData)),e.signature&&(n.response.signature=h(e.signature)),e.userHandle?n.response.userHandle=h(e.userHandle):n.response.userHandle=null,typeof e.getTransports=="function"&&(n.response.transports=e.getTransports()||[]),n}function ue(){return typeof window<"u"&&window.isSecureContext&&!!window.PublicKeyCredential&&!!navigator.credentials}function de(){const t=String(window?.navigator?.userAgent||"");return/MiuiBrowser|XiaoMi\/MiuiBrowser/i.test(t)}function ce(t,e="Passkey操作"){const n=String(t?.name||"").trim(),l=String(t?.message||"").trim();return n==="NotAllowedError"?`${e}未完成(可能已取消、超时或设备未响应)`:n==="NotReadableError"?/credential manager/i.test(l)&&de()?"当前小米浏览器与系统凭据管理器兼容性较差,请改用系统 Chrome 或 Edge 后重试。":/credential manager/i.test(l)?"系统凭据管理器返回异常,请确认已设置系统锁屏并改用系统 Chrome/Edge 后重试。":l||`${e}失败(设备读取异常)`:n==="SecurityError"?"当前环境安全策略不满足 Passkey 要求,请确认使用 HTTPS 且证书有效。":l||`${e}失败`}async function pe(t){const e=oe(t),n=await navigator.credentials.create({publicKey:e});return ie(n)}const fe={class:"page-stack"},me=24e4,ye={__name:"SettingsPage",setup(t){const e=p(""),n=p(""),l=p(""),y=p(""),i=p(!1),w=p(!1),V=p(!1),A=p(""),k=p([]),g=p(null),_=p(0);function H(s){const a=String(s||"");return a.length<8?{ok:!1,message:"密码长度至少8位"}:a.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(a)||!/\d/.test(a)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function U(){try{await te()}catch{}finally{window.location.href="/yuyx"}}async function I(){const s=e.value.trim();if(!s){u.error("请输入新用户名");return}try{await O.confirm(`确定将管理员用户名修改为「${s}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}i.value=!0;try{await W(s),u.success("用户名修改成功,请重新登录"),e.value="",setTimeout(U,1200)}catch{}finally{i.value=!1}}async function R(){const s=n.value,a=l.value,d=y.value;if(!s){u.error("请输入当前密码");return}if(!a){u.error("请输入新密码");return}const f=H(a);if(!f.ok){u.error(f.message);return}if(a!==d){u.error("两次输入的新密码不一致");return}try{await O.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}i.value=!0;try{await ee({currentPassword:s,newPassword:a}),u.success("密码修改成功,请重新登录"),n.value="",l.value="",y.value="",setTimeout(U,1200)}catch{}finally{i.value=!1}}async function T(){w.value=!0;try{const s=await ae();k.value=Array.isArray(s?.items)?s.items:[],k.value.length<3?await M():(g.value=null,_.value=0)}catch{k.value=[],g.value=null,_.value=0}finally{w.value=!1}}function j(){return!g.value||Date.now()-Number(_.value||0)>me?null:g.value}async function M(){try{const s=await $({});g.value=s,_.value=Date.now()}catch{g.value=null,_.value=0}}async function z(){if(!ue()){u.error("当前浏览器或环境不支持Passkey(需 HTTPS)");return}if(k.value.length>=3){u.error("最多可绑定3台设备");return}V.value=!0;try{let s=j();s||(s=await $({}));const a=await pe(s?.publicKey||{});await ne({credential:a,device_name:A.value.trim()}),g.value=null,_.value=0,A.value="",u.success("Passkey设备添加成功"),await T()}catch(s){try{await re({stage:"register",source:"admin-settings",name:s?.name||"",message:s?.message||"",code:s?.code||"",user_agent:navigator.userAgent||""})}catch{}g.value=null,_.value=0,await M();const d=s?.response?.data?.error||ce(s,"Passkey注册");u.error(d)}finally{V.value=!1}}async function J(s){try{await O.confirm(`确定删除设备「${s?.device_name||"未命名设备"}」吗?`,"删除Passkey设备",{confirmButtonText:"删除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await se(s.id),u.success("设备已删除"),await T()}catch(a){const d=a?.response?.data;u.error(d?.error||"删除失败")}}return Y(()=>{T()}),(s,a)=>{const d=m("el-input"),f=m("el-form-item"),E=m("el-form"),x=m("el-button"),B=m("el-card"),F=m("el-alert"),X=m("el-empty"),P=m("el-table-column"),q=m("el-table"),G=Z("loading");return C(),N("div",fe,[a[13]||(a[13]=b("div",{class:"app-page-title"},[b("h2",null,"设置"),b("span",{class:"app-muted"},"管理员账号设置")],-1)),r(B,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[a[6]||(a[6]=b("h3",{class:"section-title"},"修改管理员用户名",-1)),r(E,{"label-width":"120px"},{default:o(()=>[r(f,{label:"新用户名"},{default:o(()=>[r(d,{modelValue:e.value,"onUpdate:modelValue":a[0]||(a[0]=c=>e.value=c),placeholder:"输入新用户名",disabled:i.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),r(x,{type:"primary",loading:i.value,onClick:I},{default:o(()=>[...a[5]||(a[5]=[S("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),r(B,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[a[8]||(a[8]=b("h3",{class:"section-title"},"修改管理员密码",-1)),r(E,{"label-width":"120px"},{default:o(()=>[r(f,{label:"当前密码"},{default:o(()=>[r(d,{modelValue:n.value,"onUpdate:modelValue":a[1]||(a[1]=c=>n.value=c),type:"password","show-password":"",placeholder:"输入当前密码",disabled:i.value},null,8,["modelValue","disabled"])]),_:1}),r(f,{label:"新密码"},{default:o(()=>[r(d,{modelValue:l.value,"onUpdate:modelValue":a[2]||(a[2]=c=>l.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:i.value},null,8,["modelValue","disabled"])]),_:1}),r(f,{label:"确认新密码"},{default:o(()=>[r(d,{modelValue:y.value,"onUpdate:modelValue":a[3]||(a[3]=c=>y.value=c),type:"password","show-password":"",placeholder:"再次输入新密码",disabled:i.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),r(x,{type:"primary",loading:i.value,onClick:R},{default:o(()=>[...a[7]||(a[7]=[S("保存密码",-1)])]),_:1},8,["loading"]),a[9]||(a[9]=b("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1}),r(B,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[a[12]||(a[12]=b("h3",{class:"section-title"},"Passkey设备",-1)),r(F,{type:"info",closable:!1,title:"最多可绑定3台设备,可用于管理员无密码登录。","show-icon":"",class:"help-alert"}),r(E,{inline:""},{default:o(()=>[r(f,{label:"设备备注"},{default:o(()=>[r(d,{modelValue:A.value,"onUpdate:modelValue":a[4]||(a[4]=c=>A.value=c),placeholder:"例如:值班iPhone / 办公Mac",maxlength:"40","show-word-limit":""},null,8,["modelValue"])]),_:1}),r(f,null,{default:o(()=>[r(x,{type:"primary",loading:V.value,onClick:z},{default:o(()=>[...a[10]||(a[10]=[S("添加Passkey设备",-1)])]),_:1},8,["loading"])]),_:1})]),_:1}),Q((C(),N("div",null,[k.value.length===0?(C(),K(X,{key:0,description:"暂无Passkey设备"})):(C(),K(q,{key:1,data:k.value,size:"small",style:{width:"100%"}},{default:o(()=>[r(P,{prop:"device_name",label:"设备备注","min-width":"160"}),r(P,{prop:"credential_id_preview",label:"凭据ID","min-width":"180"}),r(P,{prop:"last_used_at",label:"最近使用","min-width":"140"}),r(P,{prop:"created_at",label:"创建时间","min-width":"140"}),r(P,{label:"操作",width:"100",fixed:"right"},{default:o(({row:c})=>[r(x,{type:"danger",text:"",onClick:we=>J(c)},{default:o(()=>[...a[11]||(a[11]=[S("删除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"]))])),[[G,w.value]])]),_:1})])}}},he=L(ye,[["__scopeId","data-v-fb202365"]]);export{he as default}; diff --git a/static/admin/assets/SystemPage-BVr94jQh.js b/static/admin/assets/SystemPage-BVr94jQh.js deleted file mode 100644 index 3341146..0000000 --- a/static/admin/assets/SystemPage-BVr94jQh.js +++ /dev/null @@ -1,6 +0,0 @@ -import{f as Ce,u as ne}from"./system-CiDlQnoe.js";import{a as C,_ as Se}from"./index-BNslg8wp.js";import{E as me,a as m}from"./vendor-element-B5S5pUKo.js";import{r as n,c as le,l as Pe,R as Ie,o as Ae,aj as p,ap as Ne,F as De,q as P,n as I,t as o,L as a,E as s,I as f,G as ae,y as Ke,J as te}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function ue(r={},c={}){const{data:v}=await C.get("/kdocs/status",{params:r,...c});return v}async function Ee(r={}){const c={force:!0,...r},{data:v}=await C.post("/kdocs/qr",c);return v}async function Le(){const{data:r}=await C.post("/kdocs/clear-login",{});return r}async function Qe(){const{data:r}=await C.get("/proxy/config");return r}async function Te(r){const{data:c}=await C.post("/proxy/config",r);return c}async function qe(r){const{data:c}=await C.post("/proxy/test",r);return c}const Be={class:"page-stack"},$e={class:"config-grid"},Re={class:"row-actions"},Me={class:"row-actions"},Fe={class:"row-actions"},he={class:"section-head"},ze={class:"status-inline app-muted"},He={key:0,class:"status-dots","aria-hidden":"true"},Ge={class:"kdocs-inline"},Oe={class:"kdocs-range"},je={class:"row-actions"},Je={key:0,class:"help"},We={key:1,class:"help"},Xe={class:"kdocs-qr"},Ye=["src"],Ze={__name:"SystemPage",setup(r){const c=n(!1),v=n(2),A=n(1),N=n(3),D=n(120),S=n(!1),_=n(""),K=n(3),E=n(!1),L=n(10),Q=n(7),T=n(!1),q=n(""),B=n(""),$=n(""),R=n(0),M=n("A"),F=n("D"),h=n(0),z=n(0),H=n(!1),G=n(""),y=n({}),k=n(!1),b=n(""),oe=n(!1),x=n(!1),w=n(!1),U=n(!1),O=n(!1),j=n("");let J=null;const de=le(()=>x.value||w.value||U.value),se=le(()=>O.value||x.value||oe.value),W=le(()=>{if(se.value)return"检测中";const l=y.value||{};return l?.logged_in===!0||l?.last_login_ok===!0?"已登录":l?.logged_in===!1||l?.last_login_ok===!1||l?.login_required===!0?"未登录":l?.last_error?"异常":"未知"}),ce=le(()=>se.value?"is-checking":W.value==="已登录"?"is-online":W.value==="未登录"?"is-offline":W.value==="异常"?"is-error":"is-unknown");function d(l){if(!l){j.value="";return}const e=new Date().toLocaleTimeString("zh-CN",{hour12:!1});j.value=`${l} (${e})`}async function ve(){c.value=!0;try{const[l,e]=await Promise.all([Ce(),Qe()]);v.value=l.max_concurrent_global??2,A.value=l.max_concurrent_per_account??1,N.value=l.max_screenshot_concurrent??3,D.value=l.db_slow_query_ms??120,E.value=(l.auto_approve_enabled??0)===1,L.value=l.auto_approve_hourly_limit??10,Q.value=l.auto_approve_vip_days??7,S.value=(e.proxy_enabled??0)===1,_.value=e.proxy_api_url||"",K.value=e.proxy_expire_minutes??3,T.value=(l.kdocs_enabled??0)===1,q.value=l.kdocs_doc_url||"",B.value=l.kdocs_default_unit||"",$.value=l.kdocs_sheet_name||"",R.value=l.kdocs_sheet_index??0,M.value=(l.kdocs_unit_column||"A").toUpperCase(),F.value=(l.kdocs_image_column||"D").toUpperCase(),h.value=l.kdocs_row_start??0,z.value=l.kdocs_row_end??0,H.value=(l.kdocs_admin_notify_enabled??0)===1,G.value=l.kdocs_admin_notify_email||""}catch{}finally{c.value=!1}pe()}async function pe(){if(!(O.value||x.value)){O.value=!0;try{const l=await ue({},{__silent:!0,__no_retry:!0,timeout:8e3});y.value=l||{}}catch{}finally{O.value=!1}}}async function fe(){const l={max_concurrent_global:Number(v.value),max_concurrent_per_account:Number(A.value),max_screenshot_concurrent:Number(N.value),db_slow_query_ms:Number(D.value)};try{await me.confirm(`确定更新并发配置吗? - -全局并发数: ${l.max_concurrent_global} -单账号并发数: ${l.max_concurrent_per_account} -截图并发数: ${l.max_screenshot_concurrent} -慢 SQL 阈值: ${l.db_slow_query_ms}ms`,"保存并发配置",{confirmButtonText:"保存",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await ne(l);m.success(e?.message||"并发配置已更新")}catch{}}async function _e(){if(S.value&&!_.value.trim()){m.error("启用代理时,API地址不能为空");return}const l={proxy_enabled:S.value?1:0,proxy_api_url:_.value.trim(),proxy_expire_minutes:Number(K.value)||3};try{const e=await Te(l);m.success(e?.message||"代理配置已更新")}catch{}}async function ye(){if(!_.value.trim()){m.error("请先输入代理API地址");return}try{const l=await qe({api_url:_.value.trim()});await me.alert(l?.message||"测试完成","代理测试",{confirmButtonText:"知道了"})}catch{}}async function ge(){const l=Number(L.value),e=Number(Q.value);if(!Number.isFinite(l)||l<1){m.error("每小时注册限制必须大于0");return}if(!Number.isFinite(e)||e<0){m.error("VIP天数不能为负数");return}const i={auto_approve_enabled:E.value?1:0,auto_approve_hourly_limit:l,auto_approve_vip_days:e};try{const u=await ne(i);m.success(u?.message||"注册设置已保存")}catch{}}async function Ve(){const l={kdocs_enabled:T.value?1:0,kdocs_doc_url:q.value.trim(),kdocs_default_unit:B.value.trim(),kdocs_sheet_name:$.value.trim(),kdocs_sheet_index:Number(R.value)||0,kdocs_unit_column:M.value.trim().toUpperCase(),kdocs_image_column:F.value.trim().toUpperCase(),kdocs_row_start:Number(h.value)||0,kdocs_row_end:Number(z.value)||0,kdocs_admin_notify_enabled:H.value?1:0,kdocs_admin_notify_email:G.value.trim()};try{const e=await ne(l);m.success(e?.message||"表格配置已更新")}catch{}}async function re(){if(!x.value){x.value=!0,d("正在刷新状态");try{y.value=await ue({live:1}),d("状态已刷新")}catch{d("刷新失败,请稍后重试")}finally{x.value=!1}}}async function ie(){try{const l=await ue({live:1});y.value=l,(l?.logged_in===!0||l?.last_login_ok===!0)&&(m.success("扫码成功,已登录"),d("扫码成功,已登录"),k.value=!1,X())}catch{}}function ke(){X(),oe.value=!0,d("扫码检测中"),ie(),J=setInterval(ie,2e3)}function X(){J&&(clearInterval(J),J=null),oe.value=!1}async function be(){if(!w.value){w.value=!0,d("正在获取二维码");try{b.value="";const l=await Ee();if(b.value=l?.qr_image||"",!b.value){if(l?.logged_in){m.success("当前已登录,无需扫码"),d("当前已登录,无需扫码"),await re();return}m.warning("未获取到二维码"),d("未获取到二维码");return}d("二维码已获取"),k.value=!0}catch{d("获取二维码失败")}finally{w.value=!1}}}async function xe(){if(!U.value){U.value=!0,d("正在清除登录态");try{await Le(),k.value=!1,b.value="",m.success("登录态已清除"),d("登录态已清除"),await re()}catch{d("清除登录态失败")}finally{U.value=!1}}}return Pe(k,l=>{l?ke():X()}),Ie(()=>{X()}),Ae(ve),(l,e)=>{const i=p("el-input-number"),u=p("el-form-item"),Y=p("el-form"),g=p("el-button"),Z=p("el-card"),ee=p("el-switch"),V=p("el-input"),we=p("el-dialog"),Ue=Ne("loading");return De((P(),I("div",Be,[e[50]||(e[50]=o("div",{class:"app-page-title"},[o("h2",null,"系统配置")],-1)),o("div",$e,[a(Z,{shadow:"never","body-style":{padding:"16px"},class:"card section-card"},{default:s(()=>[e[27]||(e[27]=o("h3",{class:"section-title"},"并发配置",-1)),e[28]||(e[28]=o("div",{class:"section-sub app-muted"},"控制任务与截图的并发资源上限",-1)),a(Y,{"label-width":"122px"},{default:s(()=>[a(u,{label:"全局最大并发数"},{default:s(()=>[a(i,{modelValue:v.value,"onUpdate:modelValue":e[0]||(e[0]=t=>v.value=t),min:1,max:200},null,8,["modelValue"]),e[22]||(e[22]=o("div",{class:"help"},"同时最多运行账号数(浏览任务 API 执行,资源占用较低)。",-1))]),_:1}),a(u,{label:"单账号最大并发数"},{default:s(()=>[a(i,{modelValue:A.value,"onUpdate:modelValue":e[1]||(e[1]=t=>A.value=t),min:1,max:50},null,8,["modelValue"]),e[23]||(e[23]=o("div",{class:"help"},"建议保持为 1,避免同账号任务抢占。",-1))]),_:1}),a(u,{label:"截图最大并发数"},{default:s(()=>[a(i,{modelValue:N.value,"onUpdate:modelValue":e[2]||(e[2]=t=>N.value=t),min:1,max:50},null,8,["modelValue"]),e[24]||(e[24]=o("div",{class:"help"},"截图资源占用较低,可按机器性能逐步提高。",-1))]),_:1}),a(u,{label:"慢 SQL 阈值(ms)"},{default:s(()=>[a(i,{modelValue:D.value,"onUpdate:modelValue":e[3]||(e[3]=t=>D.value=t),min:0,max:6e4},null,8,["modelValue"]),e[25]||(e[25]=o("div",{class:"help"},"低于该阈值不会计入慢 SQL(0 表示关闭慢 SQL 采样)。",-1))]),_:1})]),_:1}),o("div",Re,[a(g,{type:"primary",onClick:fe},{default:s(()=>[...e[26]||(e[26]=[f("保存并发配置",-1)])]),_:1})])]),_:1}),a(Z,{shadow:"never","body-style":{padding:"16px"},class:"card section-card"},{default:s(()=>[e[33]||(e[33]=o("h3",{class:"section-title"},"代理设置",-1)),e[34]||(e[34]=o("div",{class:"section-sub app-muted"},"用于任务出网代理与连接有效期管理",-1)),a(Y,{"label-width":"122px"},{default:s(()=>[a(u,{label:"启用 IP 代理"},{default:s(()=>[a(ee,{modelValue:S.value,"onUpdate:modelValue":e[4]||(e[4]=t=>S.value=t)},null,8,["modelValue"]),e[29]||(e[29]=o("div",{class:"help"},"开启后,浏览任务通过代理访问,失败自动重试。",-1))]),_:1}),a(u,{label:"代理 API 地址"},{default:s(()=>[a(V,{modelValue:_.value,"onUpdate:modelValue":e[5]||(e[5]=t=>_.value=t),placeholder:"http://api.xxx/Tools/IP.ashx?..."},null,8,["modelValue"]),e[30]||(e[30]=o("div",{class:"help"},"API 应返回 `IP:PORT`(例:123.45.67.89:8888)。",-1))]),_:1}),a(u,{label:"有效期(分钟)"},{default:s(()=>[a(i,{modelValue:K.value,"onUpdate:modelValue":e[6]||(e[6]=t=>K.value=t),min:1,max:60},null,8,["modelValue"])]),_:1})]),_:1}),o("div",Me,[a(g,{type:"primary",onClick:_e},{default:s(()=>[...e[31]||(e[31]=[f("保存代理配置",-1)])]),_:1}),a(g,{onClick:ye},{default:s(()=>[...e[32]||(e[32]=[f("测试代理",-1)])]),_:1})])]),_:1}),a(Z,{shadow:"never","body-style":{padding:"16px"},class:"card section-card"},{default:s(()=>[e[37]||(e[37]=o("h3",{class:"section-title"},"注册设置",-1)),e[38]||(e[38]=o("div",{class:"section-sub app-muted"},"控制注册节流与新用户赠送 VIP",-1)),a(Y,{"label-width":"122px"},{default:s(()=>[a(u,{label:"注册赠送 VIP"},{default:s(()=>[a(ee,{modelValue:E.value,"onUpdate:modelValue":e[7]||(e[7]=t=>E.value=t)},null,8,["modelValue"]),e[35]||(e[35]=o("div",{class:"help"},"开启后,新用户注册成功自动赠送下方设定的 VIP 天数。",-1))]),_:1}),a(u,{label:"每小时注册限制"},{default:s(()=>[a(i,{modelValue:L.value,"onUpdate:modelValue":e[8]||(e[8]=t=>L.value=t),min:1,max:1e4},null,8,["modelValue"])]),_:1}),a(u,{label:"赠送 VIP 天数"},{default:s(()=>[a(i,{modelValue:Q.value,"onUpdate:modelValue":e[9]||(e[9]=t=>Q.value=t),min:0,max:999999},null,8,["modelValue"])]),_:1})]),_:1}),o("div",Fe,[a(g,{type:"primary",onClick:ge},{default:s(()=>[...e[36]||(e[36]=[f("保存注册设置",-1)])]),_:1})])]),_:1})]),a(Z,{shadow:"never","body-style":{padding:"16px"},class:"card kdocs-card"},{default:s(()=>[o("div",he,[e[41]||(e[41]=o("h3",{class:"section-title"},"金山文档上传",-1)),o("div",ze,[e[40]||(e[40]=o("span",null,"登录状态:",-1)),o("span",{class:Ke(["status-chip",ce.value])},[f(te(W.value)+" ",1),se.value?(P(),I("span",He,[...e[39]||(e[39]=[o("i",null,null,-1),o("i",null,null,-1),o("i",null,null,-1)])])):ae("",!0)],2),o("span",null,"· 待上传 "+te(y.value.queue_size||0),1)])]),a(Y,{"label-width":"118px",class:"kdocs-form"},{default:s(()=>[a(u,{label:"启用上传"},{default:s(()=>[a(ee,{modelValue:T.value,"onUpdate:modelValue":e[10]||(e[10]=t=>T.value=t)},null,8,["modelValue"]),e[42]||(e[42]=o("div",{class:"help"},"表格结构变化时可先关闭,避免错误上传。",-1))]),_:1}),a(u,{label:"文档链接"},{default:s(()=>[a(V,{modelValue:q.value,"onUpdate:modelValue":e[11]||(e[11]=t=>q.value=t),placeholder:"https://kdocs.cn/..."},null,8,["modelValue"])]),_:1}),a(u,{label:"默认县区"},{default:s(()=>[a(V,{modelValue:B.value,"onUpdate:modelValue":e[12]||(e[12]=t=>B.value=t),placeholder:"如:道县(用户可覆盖)"},null,8,["modelValue"])]),_:1}),a(u,{label:"Sheet 名称"},{default:s(()=>[a(V,{modelValue:$.value,"onUpdate:modelValue":e[13]||(e[13]=t=>$.value=t),placeholder:"留空使用第一个 Sheet"},null,8,["modelValue"])]),_:1}),a(u,{label:"Sheet 序号"},{default:s(()=>[a(i,{modelValue:R.value,"onUpdate:modelValue":e[14]||(e[14]=t=>R.value=t),min:0,max:50},null,8,["modelValue"]),e[43]||(e[43]=o("div",{class:"help"},"0 表示第一个 Sheet。",-1))]),_:1}),a(u,{label:"列配置"},{default:s(()=>[o("div",Ge,[a(V,{modelValue:M.value,"onUpdate:modelValue":e[15]||(e[15]=t=>M.value=t),placeholder:"县区列,如 A"},null,8,["modelValue"]),a(V,{modelValue:F.value,"onUpdate:modelValue":e[16]||(e[16]=t=>F.value=t),placeholder:"图片列,如 D"},null,8,["modelValue"])])]),_:1}),a(u,{label:"有效行范围"},{default:s(()=>[o("div",Oe,[a(i,{modelValue:h.value,"onUpdate:modelValue":e[17]||(e[17]=t=>h.value=t),min:0,max:1e4,placeholder:"起始行",style:{width:"140px"}},null,8,["modelValue"]),e[44]||(e[44]=o("span",{class:"app-muted"},"至",-1)),a(i,{modelValue:z.value,"onUpdate:modelValue":e[18]||(e[18]=t=>z.value=t),min:0,max:1e4,placeholder:"结束行",style:{width:"140px"}},null,8,["modelValue"])]),e[45]||(e[45]=o("div",{class:"help"},"用于限制上传区间(如 50-100),0 表示不限制。",-1))]),_:1}),a(u,{label:"管理员通知"},{default:s(()=>[a(ee,{modelValue:H.value,"onUpdate:modelValue":e[19]||(e[19]=t=>H.value=t)},null,8,["modelValue"])]),_:1}),a(u,{label:"通知邮箱"},{default:s(()=>[a(V,{modelValue:G.value,"onUpdate:modelValue":e[20]||(e[20]=t=>G.value=t),placeholder:"admin@example.com"},null,8,["modelValue"])]),_:1})]),_:1}),o("div",je,[a(g,{type:"primary",onClick:Ve},{default:s(()=>[...e[46]||(e[46]=[f("保存表格配置",-1)])]),_:1}),a(g,{type:"success",plain:"",loading:w.value,disabled:de.value&&!w.value,onClick:be},{default:s(()=>[...e[47]||(e[47]=[f(" 获取二维码 ",-1)])]),_:1},8,["loading","disabled"]),a(g,{type:"danger",plain:"",loading:U.value,disabled:de.value&&!U.value,onClick:xe},{default:s(()=>[...e[48]||(e[48]=[f(" 清除登录 ",-1)])]),_:1},8,["loading","disabled"])]),y.value.last_error?(P(),I("div",Je,"最近错误:"+te(y.value.last_error),1)):ae("",!0),j.value?(P(),I("div",We,"操作提示:"+te(j.value),1)):ae("",!0)]),_:1}),a(we,{modelValue:k.value,"onUpdate:modelValue":e[21]||(e[21]=t=>k.value=t),title:"扫码登录",width:"min(420px, 92vw)"},{default:s(()=>[o("div",Xe,[b.value?(P(),I("img",{key:0,src:`data:image/png;base64,${b.value}`,alt:"KDocs QR"},null,8,Ye)):ae("",!0),e[49]||(e[49]=o("div",{class:"help"},"请使用管理员微信扫码登录。",-1))])]),_:1},8,["modelValue"])])),[[Ue,c.value]])}}},nl=Se(Ze,[["__scopeId","data-v-cef111cd"]]);export{nl as default}; diff --git a/static/admin/assets/SystemPage-BhhEz4Qz.css b/static/admin/assets/SystemPage-BhhEz4Qz.css new file mode 100644 index 0000000..041cecc --- /dev/null +++ b/static/admin/assets/SystemPage-BhhEz4Qz.css @@ -0,0 +1 @@ +.page-stack[data-v-ca313705]{display:flex;flex-direction:column;gap:14px;min-width:0}.config-grid[data-v-ca313705]{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.card[data-v-ca313705]{border-radius:var(--app-radius);border:1px solid var(--app-border);background:var(--app-card-bg);box-shadow:var(--app-shadow-soft)}.section-card[data-v-ca313705]{min-width:0}.section-title[data-v-ca313705]{margin:0;font-size:15px;font-weight:800;letter-spacing:.2px}.section-sub[data-v-ca313705]{margin-top:6px;margin-bottom:10px;font-size:12px}.section-head[data-v-ca313705]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:10px}.status-inline[data-v-ca313705]{font-size:12px;display:inline-flex;align-items:center;gap:6px}.status-chip[data-v-ca313705]{display:inline-flex;align-items:center;min-height:22px;padding:0 8px;border-radius:999px;font-size:12px;font-weight:700;border:1px solid transparent}.status-chip.is-checking[data-v-ca313705]{color:#1d4ed8;background:#dbeafe;border-color:#93c5fd}.status-chip.is-online[data-v-ca313705]{color:#065f46;background:#d1fae5;border-color:#6ee7b7}.status-chip.is-offline[data-v-ca313705]{color:#92400e;background:#fef3c7;border-color:#fcd34d}.status-chip.is-error[data-v-ca313705]{color:#991b1b;background:#fee2e2;border-color:#fca5a5}.status-chip.is-unknown[data-v-ca313705]{color:#374151;background:#f3f4f6;border-color:#d1d5db}.status-dots[data-v-ca313705]{display:inline-flex;align-items:center;gap:3px;margin-left:3px}.status-dots i[data-v-ca313705]{width:4px;height:4px;border-radius:50%;background:currentColor;opacity:.25;animation:dotPulse-ca313705 1.2s infinite ease-in-out}.status-dots i[data-v-ca313705]:nth-child(2){animation-delay:.2s}.status-dots i[data-v-ca313705]:nth-child(3){animation-delay:.4s}@keyframes dotPulse-ca313705{0%,80%,to{opacity:.25;transform:translateY(0)}40%{opacity:1;transform:translateY(-1px)}}.kdocs-form[data-v-ca313705]{margin-top:6px}.kdocs-inline[data-v-ca313705]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;width:100%}.kdocs-range[data-v-ca313705]{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.kdocs-qr[data-v-ca313705]{display:flex;flex-direction:column;align-items:center;gap:12px}.kdocs-qr img[data-v-ca313705]{width:260px;max-width:100%;border:1px solid var(--app-border);border-radius:8px;padding:8px;background:#fff}.help[data-v-ca313705]{margin-top:6px;font-size:12px;color:var(--app-muted)}.row-actions[data-v-ca313705]{display:flex;flex-wrap:wrap;gap:10px}@media(max-width:1200px){.config-grid[data-v-ca313705]{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:768px){.config-grid[data-v-ca313705],.kdocs-inline[data-v-ca313705]{grid-template-columns:1fr}.kdocs-range[data-v-ca313705]{align-items:stretch}} diff --git a/static/admin/assets/SystemPage-CfMGkvmW.css b/static/admin/assets/SystemPage-CfMGkvmW.css deleted file mode 100644 index f492608..0000000 --- a/static/admin/assets/SystemPage-CfMGkvmW.css +++ /dev/null @@ -1 +0,0 @@ -.page-stack[data-v-cef111cd]{display:flex;flex-direction:column;gap:14px;min-width:0}.config-grid[data-v-cef111cd]{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.card[data-v-cef111cd]{border-radius:var(--app-radius);border:1px solid var(--app-border);background:var(--app-card-bg);box-shadow:var(--app-shadow-soft)}.section-card[data-v-cef111cd]{min-width:0}.section-title[data-v-cef111cd]{margin:0;font-size:15px;font-weight:800;letter-spacing:.2px}.section-sub[data-v-cef111cd]{margin-top:6px;margin-bottom:10px;font-size:12px}.section-head[data-v-cef111cd]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:10px}.status-inline[data-v-cef111cd]{font-size:12px;display:inline-flex;align-items:center;gap:6px}.status-chip[data-v-cef111cd]{display:inline-flex;align-items:center;min-height:22px;padding:0 8px;border-radius:999px;font-size:12px;font-weight:700;border:1px solid transparent}.status-chip.is-checking[data-v-cef111cd]{color:#1d4ed8;background:#dbeafe;border-color:#93c5fd}.status-chip.is-online[data-v-cef111cd]{color:#065f46;background:#d1fae5;border-color:#6ee7b7}.status-chip.is-offline[data-v-cef111cd]{color:#92400e;background:#fef3c7;border-color:#fcd34d}.status-chip.is-error[data-v-cef111cd]{color:#991b1b;background:#fee2e2;border-color:#fca5a5}.status-chip.is-unknown[data-v-cef111cd]{color:#374151;background:#f3f4f6;border-color:#d1d5db}.status-dots[data-v-cef111cd]{display:inline-flex;align-items:center;gap:3px;margin-left:3px}.status-dots i[data-v-cef111cd]{width:4px;height:4px;border-radius:50%;background:currentColor;opacity:.25;animation:dotPulse-cef111cd 1.2s infinite ease-in-out}.status-dots i[data-v-cef111cd]:nth-child(2){animation-delay:.2s}.status-dots i[data-v-cef111cd]:nth-child(3){animation-delay:.4s}@keyframes dotPulse-cef111cd{0%,80%,to{opacity:.25;transform:translateY(0)}40%{opacity:1;transform:translateY(-1px)}}.kdocs-form[data-v-cef111cd]{margin-top:6px}.kdocs-inline[data-v-cef111cd]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;width:100%}.kdocs-range[data-v-cef111cd]{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.kdocs-qr[data-v-cef111cd]{display:flex;flex-direction:column;align-items:center;gap:12px}.kdocs-qr img[data-v-cef111cd]{width:260px;max-width:100%;border:1px solid var(--app-border);border-radius:8px;padding:8px;background:#fff}.help[data-v-cef111cd]{margin-top:6px;font-size:12px;color:var(--app-muted)}.row-actions[data-v-cef111cd]{display:flex;flex-wrap:wrap;gap:10px}@media(max-width:1200px){.config-grid[data-v-cef111cd]{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:768px){.config-grid[data-v-cef111cd],.kdocs-inline[data-v-cef111cd]{grid-template-columns:1fr}.kdocs-range[data-v-cef111cd]{align-items:stretch}} diff --git a/static/admin/assets/SystemPage-D3eBPCNe.js b/static/admin/assets/SystemPage-D3eBPCNe.js new file mode 100644 index 0000000..d1d1c0e --- /dev/null +++ b/static/admin/assets/SystemPage-D3eBPCNe.js @@ -0,0 +1,6 @@ +import{f as Ae,u as se}from"./system-ZDPnxnIu.js";import{a as ne,_ as Ie,g as ve,h as Ne,i as Ke,u as ue,j as ce,p as De}from"./index-C1f9ticl.js";import{E as pe,a as m}from"./vendor-element-B5S5pUKo.js";import{r as u,c as ee,l as Ee,R as Le,o as Qe,aj as v,ap as Te,F as qe,q as U,n as P,t as o,L as l,E as s,I as c,G as le,y as Be,J as ae}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function Me(){const{data:g}=await ne.get("/proxy/config");return g}async function he(g){const{data:p}=await ne.post("/proxy/config",g);return p}async function $e(g){const{data:p}=await ne.post("/proxy/test",g);return p}const Re={class:"page-stack"},Fe={class:"config-grid"},ze={class:"row-actions"},He={class:"row-actions"},je={class:"row-actions"},Ge={class:"section-head"},Oe={class:"status-inline app-muted"},Je={key:0,class:"status-dots","aria-hidden":"true"},We={class:"kdocs-inline"},Xe={class:"kdocs-range"},Ye={class:"row-actions"},Ze={key:0,class:"help"},el={key:1,class:"help"},ll={class:"kdocs-qr"},al=["src"],tl={__name:"SystemPage",setup(g){const p=u(!1),A=u(2),I=u(1),N=u(3),K=u(120),S=u(!1),f=u(""),D=u(3),E=u(!1),L=u(10),Q=u(7),T=u(!1),q=u(""),B=u(""),M=u(""),h=u(0),$=u("A"),R=u("D"),F=u(0),z=u(0),H=u(!1),j=u(""),de=ve({maxAgeMs:600*1e3}),r=u(de||{}),V=u(!1),x=u(""),te=u(!1),k=u(!1),b=u(!1),w=u(!1),C=u(!de),G=u("");let O=null;const ie=ee(()=>k.value||b.value||w.value),oe=ee(()=>C.value||k.value||te.value),J=ee(()=>{if(oe.value)return"检测中";const t=r.value||{};return t?.logged_in===!0||t?.last_login_ok===!0?"已登录":t?.logged_in===!1||t?.last_login_ok===!1||t?.login_required===!0?"未登录":t?.last_error?"异常":"未知"}),fe=ee(()=>oe.value?"is-checking":J.value==="已登录"?"is-online":J.value==="未登录"?"is-offline":J.value==="异常"?"is-error":"is-unknown");function i(t){if(!t){G.value="";return}const e=new Date().toLocaleTimeString("zh-CN",{hour12:!1});G.value=`${t} (${e})`}async function _e(){p.value=!0;try{const[e,d]=await Promise.all([Ae(),Me()]);A.value=e.max_concurrent_global??2,I.value=e.max_concurrent_per_account??1,N.value=e.max_screenshot_concurrent??3,K.value=e.db_slow_query_ms??120,E.value=(e.auto_approve_enabled??0)===1,L.value=e.auto_approve_hourly_limit??10,Q.value=e.auto_approve_vip_days??7,S.value=(d.proxy_enabled??0)===1,f.value=d.proxy_api_url||"",D.value=d.proxy_expire_minutes??3,T.value=(e.kdocs_enabled??0)===1,q.value=e.kdocs_doc_url||"",B.value=e.kdocs_default_unit||"",M.value=e.kdocs_sheet_name||"",h.value=e.kdocs_sheet_index??0,$.value=(e.kdocs_unit_column||"A").toUpperCase(),R.value=(e.kdocs_image_column||"D").toUpperCase(),F.value=e.kdocs_row_start??0,z.value=e.kdocs_row_end??0,H.value=(e.kdocs_admin_notify_enabled??0)===1,j.value=e.kdocs_admin_notify_email||""}catch{}finally{p.value=!1}const t=ve({maxAgeMs:600*1e3});t&&(r.value=t,C.value=!1),ye()}async function ye(){if(!(C.value||k.value)){C.value=!0;try{const t=await De({force:!1,maxAgeMs:6e4,silent:!0,live:0});r.value=t||{}}catch{}finally{C.value=!1}}}async function ge(){const t={max_concurrent_global:Number(A.value),max_concurrent_per_account:Number(I.value),max_screenshot_concurrent:Number(N.value),db_slow_query_ms:Number(K.value)};try{await pe.confirm(`确定更新并发配置吗? + +全局并发数: ${t.max_concurrent_global} +单账号并发数: ${t.max_concurrent_per_account} +截图并发数: ${t.max_screenshot_concurrent} +慢 SQL 阈值: ${t.db_slow_query_ms}ms`,"保存并发配置",{confirmButtonText:"保存",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await se(t);m.success(e?.message||"并发配置已更新")}catch{}}async function Ve(){if(S.value&&!f.value.trim()){m.error("启用代理时,API地址不能为空");return}const t={proxy_enabled:S.value?1:0,proxy_api_url:f.value.trim(),proxy_expire_minutes:Number(D.value)||3};try{const e=await he(t);m.success(e?.message||"代理配置已更新")}catch{}}async function xe(){if(!f.value.trim()){m.error("请先输入代理API地址");return}try{const t=await $e({api_url:f.value.trim()});await pe.alert(t?.message||"测试完成","代理测试",{confirmButtonText:"知道了"})}catch{}}async function ke(){const t=Number(L.value),e=Number(Q.value);if(!Number.isFinite(t)||t<1){m.error("每小时注册限制必须大于0");return}if(!Number.isFinite(e)||e<0){m.error("VIP天数不能为负数");return}const d={auto_approve_enabled:E.value?1:0,auto_approve_hourly_limit:t,auto_approve_vip_days:e};try{const n=await se(d);m.success(n?.message||"注册设置已保存")}catch{}}async function be(){const t={kdocs_enabled:T.value?1:0,kdocs_doc_url:q.value.trim(),kdocs_default_unit:B.value.trim(),kdocs_sheet_name:M.value.trim(),kdocs_sheet_index:Number(h.value)||0,kdocs_unit_column:$.value.trim().toUpperCase(),kdocs_image_column:R.value.trim().toUpperCase(),kdocs_row_start:Number(F.value)||0,kdocs_row_end:Number(z.value)||0,kdocs_admin_notify_enabled:H.value?1:0,kdocs_admin_notify_email:j.value.trim()};try{const e=await se(t);m.success(e?.message||"表格配置已更新")}catch{}}async function re(){if(!k.value){k.value=!0,i("正在刷新状态");try{const t=await ce({live:1});r.value=t||{},ue(r.value),i("状态已刷新")}catch{i("刷新失败,请稍后重试")}finally{k.value=!1}}}async function me(){try{const t=await ce({live:1});r.value=t||{},ue(r.value),(t?.logged_in===!0||t?.last_login_ok===!0)&&(m.success("扫码成功,已登录"),i("扫码成功,已登录"),V.value=!1,W())}catch{}}function we(){W(),te.value=!0,i("扫码检测中"),me(),O=setInterval(me,2e3)}function W(){O&&(clearInterval(O),O=null),te.value=!1}async function Se(){if(!b.value){b.value=!0,i("正在获取二维码");try{x.value="";const t=await Ne();if(x.value=t?.qr_image||"",!x.value){if(t?.logged_in){m.success("当前已登录,无需扫码"),i("当前已登录,无需扫码"),await re();return}m.warning("未获取到二维码"),i("未获取到二维码");return}i("二维码已获取"),V.value=!0}catch{i("获取二维码失败")}finally{b.value=!1}}}async function Ce(){if(!w.value){w.value=!0,i("正在清除登录态");try{await Ke(),V.value=!1,x.value="",r.value=ue({...r.value||{},logged_in:!1,last_login_ok:!1,login_required:!0}),m.success("登录态已清除"),i("登录态已清除"),await re()}catch{i("清除登录态失败")}finally{w.value=!1}}}return Ee(V,t=>{t?we():W()}),Le(()=>{W()}),Qe(_e),(t,e)=>{const d=v("el-input-number"),n=v("el-form-item"),X=v("el-form"),_=v("el-button"),Y=v("el-card"),Z=v("el-switch"),y=v("el-input"),Ue=v("el-dialog"),Pe=Te("loading");return qe((U(),P("div",Re,[e[50]||(e[50]=o("div",{class:"app-page-title"},[o("h2",null,"系统配置")],-1)),o("div",Fe,[l(Y,{shadow:"never","body-style":{padding:"16px"},class:"card section-card"},{default:s(()=>[e[27]||(e[27]=o("h3",{class:"section-title"},"并发配置",-1)),e[28]||(e[28]=o("div",{class:"section-sub app-muted"},"控制任务与截图的并发资源上限",-1)),l(X,{"label-width":"122px"},{default:s(()=>[l(n,{label:"全局最大并发数"},{default:s(()=>[l(d,{modelValue:A.value,"onUpdate:modelValue":e[0]||(e[0]=a=>A.value=a),min:1,max:200},null,8,["modelValue"]),e[22]||(e[22]=o("div",{class:"help"},"同时最多运行账号数(浏览任务 API 执行,资源占用较低)。",-1))]),_:1}),l(n,{label:"单账号最大并发数"},{default:s(()=>[l(d,{modelValue:I.value,"onUpdate:modelValue":e[1]||(e[1]=a=>I.value=a),min:1,max:50},null,8,["modelValue"]),e[23]||(e[23]=o("div",{class:"help"},"建议保持为 1,避免同账号任务抢占。",-1))]),_:1}),l(n,{label:"截图最大并发数"},{default:s(()=>[l(d,{modelValue:N.value,"onUpdate:modelValue":e[2]||(e[2]=a=>N.value=a),min:1,max:50},null,8,["modelValue"]),e[24]||(e[24]=o("div",{class:"help"},"截图资源占用较低,可按机器性能逐步提高。",-1))]),_:1}),l(n,{label:"慢 SQL 阈值(ms)"},{default:s(()=>[l(d,{modelValue:K.value,"onUpdate:modelValue":e[3]||(e[3]=a=>K.value=a),min:0,max:6e4},null,8,["modelValue"]),e[25]||(e[25]=o("div",{class:"help"},"低于该阈值不会计入慢 SQL(0 表示关闭慢 SQL 采样)。",-1))]),_:1})]),_:1}),o("div",ze,[l(_,{type:"primary",onClick:ge},{default:s(()=>[...e[26]||(e[26]=[c("保存并发配置",-1)])]),_:1})])]),_:1}),l(Y,{shadow:"never","body-style":{padding:"16px"},class:"card section-card"},{default:s(()=>[e[33]||(e[33]=o("h3",{class:"section-title"},"代理设置",-1)),e[34]||(e[34]=o("div",{class:"section-sub app-muted"},"用于任务出网代理与连接有效期管理",-1)),l(X,{"label-width":"122px"},{default:s(()=>[l(n,{label:"启用 IP 代理"},{default:s(()=>[l(Z,{modelValue:S.value,"onUpdate:modelValue":e[4]||(e[4]=a=>S.value=a)},null,8,["modelValue"]),e[29]||(e[29]=o("div",{class:"help"},"开启后,浏览任务通过代理访问,失败自动重试。",-1))]),_:1}),l(n,{label:"代理 API 地址"},{default:s(()=>[l(y,{modelValue:f.value,"onUpdate:modelValue":e[5]||(e[5]=a=>f.value=a),placeholder:"http://api.xxx/Tools/IP.ashx?..."},null,8,["modelValue"]),e[30]||(e[30]=o("div",{class:"help"},"API 应返回 `IP:PORT`(例:123.45.67.89:8888)。",-1))]),_:1}),l(n,{label:"有效期(分钟)"},{default:s(()=>[l(d,{modelValue:D.value,"onUpdate:modelValue":e[6]||(e[6]=a=>D.value=a),min:1,max:60},null,8,["modelValue"])]),_:1})]),_:1}),o("div",He,[l(_,{type:"primary",onClick:Ve},{default:s(()=>[...e[31]||(e[31]=[c("保存代理配置",-1)])]),_:1}),l(_,{onClick:xe},{default:s(()=>[...e[32]||(e[32]=[c("测试代理",-1)])]),_:1})])]),_:1}),l(Y,{shadow:"never","body-style":{padding:"16px"},class:"card section-card"},{default:s(()=>[e[37]||(e[37]=o("h3",{class:"section-title"},"注册设置",-1)),e[38]||(e[38]=o("div",{class:"section-sub app-muted"},"控制注册节流与新用户赠送 VIP",-1)),l(X,{"label-width":"122px"},{default:s(()=>[l(n,{label:"注册赠送 VIP"},{default:s(()=>[l(Z,{modelValue:E.value,"onUpdate:modelValue":e[7]||(e[7]=a=>E.value=a)},null,8,["modelValue"]),e[35]||(e[35]=o("div",{class:"help"},"开启后,新用户注册成功自动赠送下方设定的 VIP 天数。",-1))]),_:1}),l(n,{label:"每小时注册限制"},{default:s(()=>[l(d,{modelValue:L.value,"onUpdate:modelValue":e[8]||(e[8]=a=>L.value=a),min:1,max:1e4},null,8,["modelValue"])]),_:1}),l(n,{label:"赠送 VIP 天数"},{default:s(()=>[l(d,{modelValue:Q.value,"onUpdate:modelValue":e[9]||(e[9]=a=>Q.value=a),min:0,max:999999},null,8,["modelValue"])]),_:1})]),_:1}),o("div",je,[l(_,{type:"primary",onClick:ke},{default:s(()=>[...e[36]||(e[36]=[c("保存注册设置",-1)])]),_:1})])]),_:1})]),l(Y,{shadow:"never","body-style":{padding:"16px"},class:"card kdocs-card"},{default:s(()=>[o("div",Ge,[e[41]||(e[41]=o("h3",{class:"section-title"},"金山文档上传",-1)),o("div",Oe,[e[40]||(e[40]=o("span",null,"登录状态:",-1)),o("span",{class:Be(["status-chip",fe.value])},[c(ae(J.value)+" ",1),oe.value?(U(),P("span",Je,[...e[39]||(e[39]=[o("i",null,null,-1),o("i",null,null,-1),o("i",null,null,-1)])])):le("",!0)],2),o("span",null,"· 待上传 "+ae(r.value.queue_size||0),1)])]),l(X,{"label-width":"118px",class:"kdocs-form"},{default:s(()=>[l(n,{label:"启用上传"},{default:s(()=>[l(Z,{modelValue:T.value,"onUpdate:modelValue":e[10]||(e[10]=a=>T.value=a)},null,8,["modelValue"]),e[42]||(e[42]=o("div",{class:"help"},"表格结构变化时可先关闭,避免错误上传。",-1))]),_:1}),l(n,{label:"文档链接"},{default:s(()=>[l(y,{modelValue:q.value,"onUpdate:modelValue":e[11]||(e[11]=a=>q.value=a),placeholder:"https://kdocs.cn/..."},null,8,["modelValue"])]),_:1}),l(n,{label:"默认县区"},{default:s(()=>[l(y,{modelValue:B.value,"onUpdate:modelValue":e[12]||(e[12]=a=>B.value=a),placeholder:"如:道县(用户可覆盖)"},null,8,["modelValue"])]),_:1}),l(n,{label:"Sheet 名称"},{default:s(()=>[l(y,{modelValue:M.value,"onUpdate:modelValue":e[13]||(e[13]=a=>M.value=a),placeholder:"留空使用第一个 Sheet"},null,8,["modelValue"])]),_:1}),l(n,{label:"Sheet 序号"},{default:s(()=>[l(d,{modelValue:h.value,"onUpdate:modelValue":e[14]||(e[14]=a=>h.value=a),min:0,max:50},null,8,["modelValue"]),e[43]||(e[43]=o("div",{class:"help"},"0 表示第一个 Sheet。",-1))]),_:1}),l(n,{label:"列配置"},{default:s(()=>[o("div",We,[l(y,{modelValue:$.value,"onUpdate:modelValue":e[15]||(e[15]=a=>$.value=a),placeholder:"县区列,如 A"},null,8,["modelValue"]),l(y,{modelValue:R.value,"onUpdate:modelValue":e[16]||(e[16]=a=>R.value=a),placeholder:"图片列,如 D"},null,8,["modelValue"])])]),_:1}),l(n,{label:"有效行范围"},{default:s(()=>[o("div",Xe,[l(d,{modelValue:F.value,"onUpdate:modelValue":e[17]||(e[17]=a=>F.value=a),min:0,max:1e4,placeholder:"起始行",style:{width:"140px"}},null,8,["modelValue"]),e[44]||(e[44]=o("span",{class:"app-muted"},"至",-1)),l(d,{modelValue:z.value,"onUpdate:modelValue":e[18]||(e[18]=a=>z.value=a),min:0,max:1e4,placeholder:"结束行",style:{width:"140px"}},null,8,["modelValue"])]),e[45]||(e[45]=o("div",{class:"help"},"用于限制上传区间(如 50-100),0 表示不限制。",-1))]),_:1}),l(n,{label:"管理员通知"},{default:s(()=>[l(Z,{modelValue:H.value,"onUpdate:modelValue":e[19]||(e[19]=a=>H.value=a)},null,8,["modelValue"])]),_:1}),l(n,{label:"通知邮箱"},{default:s(()=>[l(y,{modelValue:j.value,"onUpdate:modelValue":e[20]||(e[20]=a=>j.value=a),placeholder:"admin@example.com"},null,8,["modelValue"])]),_:1})]),_:1}),o("div",Ye,[l(_,{type:"primary",onClick:be},{default:s(()=>[...e[46]||(e[46]=[c("保存表格配置",-1)])]),_:1}),l(_,{type:"success",plain:"",loading:b.value,disabled:ie.value&&!b.value,onClick:Se},{default:s(()=>[...e[47]||(e[47]=[c(" 获取二维码 ",-1)])]),_:1},8,["loading","disabled"]),l(_,{type:"danger",plain:"",loading:w.value,disabled:ie.value&&!w.value,onClick:Ce},{default:s(()=>[...e[48]||(e[48]=[c(" 清除登录 ",-1)])]),_:1},8,["loading","disabled"])]),r.value.last_error?(U(),P("div",Ze,"最近错误:"+ae(r.value.last_error),1)):le("",!0),G.value?(U(),P("div",el,"操作提示:"+ae(G.value),1)):le("",!0)]),_:1}),l(Ue,{modelValue:V.value,"onUpdate:modelValue":e[21]||(e[21]=a=>V.value=a),title:"扫码登录",width:"min(420px, 92vw)"},{default:s(()=>[o("div",ll,[x.value?(U(),P("img",{key:0,src:`data:image/png;base64,${x.value}`,alt:"KDocs QR"},null,8,al)):le("",!0),e[49]||(e[49]=o("div",{class:"help"},"请使用管理员微信扫码登录。",-1))])]),_:1},8,["modelValue"])])),[[Pe,p.value]])}}},rl=Ie(tl,[["__scopeId","data-v-ca313705"]]);export{rl as default}; diff --git a/static/admin/assets/UsersPage-CwfHMchI.js b/static/admin/assets/UsersPage-DJZUCpfb.js similarity index 99% rename from static/admin/assets/UsersPage-CwfHMchI.js rename to static/admin/assets/UsersPage-DJZUCpfb.js index fdc95f5..3f6cf4a 100644 --- a/static/admin/assets/UsersPage-CwfHMchI.js +++ b/static/admin/assets/UsersPage-DJZUCpfb.js @@ -1 +1 @@ -import{a as W,r as Z,s as F,b as G,c as J,d as H,f as K}from"./users-BowyvQzr.js";import{_ as O}from"./index-BNslg8wp.js";import{E as v,a as h}from"./vendor-element-B5S5pUKo.js";import{i as Q,r as P,o as X,aj as _,ap as Y,n as V,q as r,t as y,L as l,E as s,F as ee,D as f,G as p,J as b,I as c}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.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 o=a.includes("T")?a:a.replace(" ","T");o=o.replace(/\.(\d{3})\d+/,".$1"),/([zZ]|[+-]\d{2}:\d{2})$/.test(o)||(o=`${o}+08:00`);const u=new Date(o);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 o=/[a-zA-Z]/.test(a),x=/\d/.test(a);return!o||!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"},oe={key:0,class:"app-muted"},le={class:"actions"},ce={__name:"UsersPage",setup(g){const a=Q("refreshStats",null),o=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(){o.value=!0;try{x.value=await K()}catch{x.value=[]}finally{o.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 W(n.id),h.success("用户已启用"),await w(),await a?.({force:!0})}catch{}}async function S(n){try{await v.confirm(`确定禁用用户「${n.username}」吗?禁用后用户将无法登录。`,"禁用用户",{confirmButtonText:"禁用",cancelButtonText:"取消",type:"warning"})}catch{return}try{await Z(n.id),h.success("用户已禁用"),await w(),await a?.({force:!0})}catch{}}async function E(n){try{await v.confirm(`确定删除用户「${n.username}」吗?此操作将删除该用户的所有数据,不可恢复!`,"删除用户",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{await H(n.id),h.success("用户已删除"),await w(),await a?.({force:!0})}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 F(n.id,e);h.success(d?.message||"VIP设置成功"),await w(),await a?.({force:!0})}catch{}}async function N(n){try{await v.confirm(`确定移除用户「${n.username}」的 VIP 吗?`,"移除VIP",{confirmButtonText:"移除",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await G(n.id);h.success(e?.message||"VIP已移除"),await w(),await a?.({force:!0})}catch{}}async function M(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 J(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"),L=_("el-dropdown"),A=_("el-table"),R=_("el-card"),q=Y("loading");return r(),V("div",te,[e[11]||(e[11]=y("div",{class:"app-page-title"},[y("h2",null,"用户")],-1)),l(R,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:s(()=>[y("div",ne,[ee((r(),f(A,{data:x.value,style:{width:"100%"}},{default:s(()=>[l(i,{prop:"id",label:"ID",width:"80"}),l(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})):p("",!0)]),t.email?(r(),V("div",ie,b(t.email),1)):p("",!0),C(t)?(r(),V("div",re,b(C(t)),1)):p("",!0)])]),_:1}),l(i,{label:"状态",width:"120"},{default:s(({row:t})=>[l(d,{type:B(t.status).type,effect:"light"},{default:s(()=>[c(b(B(t.status).label),1)]),_:2},1032,["type"])]),_:1}),l(i,{label:"时间","min-width":"220"},{default:s(({row:t})=>[y("div",null,b(t.created_at),1),t.vip_expire_time?(r(),V("div",oe,"VIP到期: "+b(t.vip_expire_time),1)):p("",!0)]),_:1}),l(i,{label:"操作",width:"280",fixed:"right"},{default:s(({row:t})=>[y("div",le,[t.status==="rejected"?(r(),f(k,{key:0,type:"success",size:"small",onClick:m=>z(t)},{default:s(()=>[...e[1]||(e[1]=[c("启用",-1)])]),_:1},8,["onClick"])):(r(),f(k,{key:1,type:"warning",size:"small",onClick:m=>S(t)},{default:s(()=>[...e[2]||(e[2]=[c("禁用",-1)])]),_:1},8,["onClick"])),l(L,{trigger:"click"},{dropdown:s(()=>[l(j,null,{default:s(()=>[u(t)?p("",!0):(r(),f(T,{key:0,onClick:m=>$(t,7)},{default:s(()=>[...e[4]||(e[4]=[c("开通一周",-1)])]),_:1},8,["onClick"])),u(t)?p("",!0):(r(),f(T,{key:1,onClick:m=>$(t,30)},{default:s(()=>[...e[5]||(e[5]=[c("开通一月",-1)])]),_:1},8,["onClick"])),u(t)?p("",!0):(r(),f(T,{key:2,onClick:m=>$(t,365)},{default:s(()=>[...e[6]||(e[6]=[c("开通一年",-1)])]),_:1},8,["onClick"])),u(t)?p("",!0):(r(),f(T,{key:3,onClick:m=>$(t,999999)},{default:s(()=>[...e[7]||(e[7]=[c("永久VIP",-1)])]),_:1},8,["onClick"])),u(t)?(r(),f(T,{key:4,onClick:m=>N(t)},{default:s(()=>[...e[8]||(e[8]=[c("移除VIP",-1)])]),_:1},8,["onClick"])):p("",!0)]),_:2},1024)]),default:s(()=>[l(k,{size:"small"},{default:s(()=>[...e[3]||(e[3]=[c("VIP",-1)])]),_:1})]),_:2},1024),l(k,{size:"small",onClick:m=>M(t)},{default:s(()=>[...e[9]||(e[9]=[c("重置密码",-1)])]),_:1},8,["onClick"]),l(k,{type:"danger",size:"small",onClick:m=>E(t)},{default:s(()=>[...e[10]||(e[10]=[c("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[q,o.value]])])]),_:1})])}}},ye=O(ce,[["__scopeId","data-v-11f4f270"]]);export{ye as default}; +import{a as W,r as Z,s as F,b as G,c as J,d as H,f as K}from"./users-te9ySk34.js";import{_ as O}from"./index-C1f9ticl.js";import{E as v,a as h}from"./vendor-element-B5S5pUKo.js";import{i as Q,r as P,o as X,aj as _,ap as Y,n as V,q as r,t as y,L as l,E as s,F as ee,D as f,G as p,J as b,I as c}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.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 o=a.includes("T")?a:a.replace(" ","T");o=o.replace(/\.(\d{3})\d+/,".$1"),/([zZ]|[+-]\d{2}:\d{2})$/.test(o)||(o=`${o}+08:00`);const u=new Date(o);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 o=/[a-zA-Z]/.test(a),x=/\d/.test(a);return!o||!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"},oe={key:0,class:"app-muted"},le={class:"actions"},ce={__name:"UsersPage",setup(g){const a=Q("refreshStats",null),o=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(){o.value=!0;try{x.value=await K()}catch{x.value=[]}finally{o.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 W(n.id),h.success("用户已启用"),await w(),await a?.({force:!0})}catch{}}async function S(n){try{await v.confirm(`确定禁用用户「${n.username}」吗?禁用后用户将无法登录。`,"禁用用户",{confirmButtonText:"禁用",cancelButtonText:"取消",type:"warning"})}catch{return}try{await Z(n.id),h.success("用户已禁用"),await w(),await a?.({force:!0})}catch{}}async function E(n){try{await v.confirm(`确定删除用户「${n.username}」吗?此操作将删除该用户的所有数据,不可恢复!`,"删除用户",{confirmButtonText:"删除",cancelButtonText:"取消",type:"error"})}catch{return}try{await H(n.id),h.success("用户已删除"),await w(),await a?.({force:!0})}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 F(n.id,e);h.success(d?.message||"VIP设置成功"),await w(),await a?.({force:!0})}catch{}}async function N(n){try{await v.confirm(`确定移除用户「${n.username}」的 VIP 吗?`,"移除VIP",{confirmButtonText:"移除",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await G(n.id);h.success(e?.message||"VIP已移除"),await w(),await a?.({force:!0})}catch{}}async function M(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 J(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"),L=_("el-dropdown"),A=_("el-table"),R=_("el-card"),q=Y("loading");return r(),V("div",te,[e[11]||(e[11]=y("div",{class:"app-page-title"},[y("h2",null,"用户")],-1)),l(R,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:s(()=>[y("div",ne,[ee((r(),f(A,{data:x.value,style:{width:"100%"}},{default:s(()=>[l(i,{prop:"id",label:"ID",width:"80"}),l(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})):p("",!0)]),t.email?(r(),V("div",ie,b(t.email),1)):p("",!0),C(t)?(r(),V("div",re,b(C(t)),1)):p("",!0)])]),_:1}),l(i,{label:"状态",width:"120"},{default:s(({row:t})=>[l(d,{type:B(t.status).type,effect:"light"},{default:s(()=>[c(b(B(t.status).label),1)]),_:2},1032,["type"])]),_:1}),l(i,{label:"时间","min-width":"220"},{default:s(({row:t})=>[y("div",null,b(t.created_at),1),t.vip_expire_time?(r(),V("div",oe,"VIP到期: "+b(t.vip_expire_time),1)):p("",!0)]),_:1}),l(i,{label:"操作",width:"280",fixed:"right"},{default:s(({row:t})=>[y("div",le,[t.status==="rejected"?(r(),f(k,{key:0,type:"success",size:"small",onClick:m=>z(t)},{default:s(()=>[...e[1]||(e[1]=[c("启用",-1)])]),_:1},8,["onClick"])):(r(),f(k,{key:1,type:"warning",size:"small",onClick:m=>S(t)},{default:s(()=>[...e[2]||(e[2]=[c("禁用",-1)])]),_:1},8,["onClick"])),l(L,{trigger:"click"},{dropdown:s(()=>[l(j,null,{default:s(()=>[u(t)?p("",!0):(r(),f(T,{key:0,onClick:m=>$(t,7)},{default:s(()=>[...e[4]||(e[4]=[c("开通一周",-1)])]),_:1},8,["onClick"])),u(t)?p("",!0):(r(),f(T,{key:1,onClick:m=>$(t,30)},{default:s(()=>[...e[5]||(e[5]=[c("开通一月",-1)])]),_:1},8,["onClick"])),u(t)?p("",!0):(r(),f(T,{key:2,onClick:m=>$(t,365)},{default:s(()=>[...e[6]||(e[6]=[c("开通一年",-1)])]),_:1},8,["onClick"])),u(t)?p("",!0):(r(),f(T,{key:3,onClick:m=>$(t,999999)},{default:s(()=>[...e[7]||(e[7]=[c("永久VIP",-1)])]),_:1},8,["onClick"])),u(t)?(r(),f(T,{key:4,onClick:m=>N(t)},{default:s(()=>[...e[8]||(e[8]=[c("移除VIP",-1)])]),_:1},8,["onClick"])):p("",!0)]),_:2},1024)]),default:s(()=>[l(k,{size:"small"},{default:s(()=>[...e[3]||(e[3]=[c("VIP",-1)])]),_:1})]),_:2},1024),l(k,{size:"small",onClick:m=>M(t)},{default:s(()=>[...e[9]||(e[9]=[c("重置密码",-1)])]),_:1},8,["onClick"]),l(k,{type:"danger",size:"small",onClick:m=>E(t)},{default:s(()=>[...e[10]||(e[10]=[c("删除",-1)])]),_:1},8,["onClick"])])]),_:1})]),_:1},8,["data"])),[[q,o.value]])])]),_:1})])}}},ye=O(ce,[["__scopeId","data-v-11f4f270"]]);export{ye as default}; diff --git a/static/admin/assets/email-CZFN9gLR.js b/static/admin/assets/email-px7YBG2O.js similarity index 88% rename from static/admin/assets/email-CZFN9gLR.js rename to static/admin/assets/email-px7YBG2O.js index 76c2843..d7e7ca4 100644 --- a/static/admin/assets/email-CZFN9gLR.js +++ b/static/admin/assets/email-px7YBG2O.js @@ -1 +1 @@ -import{c as s,a as e}from"./index-BNslg8wp.js";const n=s(async()=>{const{data:a}=await e.get("/email/stats");return a},1e4);async function i(){const{data:a}=await e.get("/email/settings");return a}async function r(a){const{data:t}=await e.post("/email/settings",a);return n.clear(),t}async function o(a={}){return n.run(a)}async function l(a){const{data:t}=await e.get("/email/logs",{params:a});return t}async function u(a){const{data:t}=await e.post("/email/logs/cleanup",{days:a});return n.clear(),t}export{l as a,i as b,u as c,o as f,r as u}; +import{c as s,a as e}from"./index-C1f9ticl.js";const n=s(async()=>{const{data:a}=await e.get("/email/stats");return a},1e4);async function i(){const{data:a}=await e.get("/email/settings");return a}async function r(a){const{data:t}=await e.post("/email/settings",a);return n.clear(),t}async function o(a={}){return n.run(a)}async function l(a){const{data:t}=await e.get("/email/logs",{params:a});return t}async function u(a){const{data:t}=await e.post("/email/logs/cleanup",{days:a});return n.clear(),t}export{l as a,i as b,u as c,o as f,r as u}; diff --git a/static/admin/assets/index-BNslg8wp.js b/static/admin/assets/index-BNslg8wp.js deleted file mode 100644 index 0a18d2c..0000000 --- a/static/admin/assets/index-BNslg8wp.js +++ /dev/null @@ -1,2 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./ReportPage-CqUGYZcC.js","./vendor-element-B5S5pUKo.js","./vendor-vue-CVxSw_oJ.js","./vendor-misc-BeoNyvBp.js","./vendor-element-C68yOrAy.css","./email-CZFN9gLR.js","./tasks-DqqATNe_.js","./system-CiDlQnoe.js","./MetricGrid-BW8H4wTM.js","./MetricGrid-yP_dkP6X.css","./vendor-axios-B9ygI19o.js","./ReportPage-BCQBCnjY.css","./UsersPage-CwfHMchI.js","./users-BowyvQzr.js","./UsersPage-BNDnhJe0.css","./FeedbacksPage-Dwzul2Z8.js","./FeedbacksPage-mrXjCiV2.css","./LogsPage-Bs3Ge-t3.js","./LogsPage-D1bozCEo.css","./AnnouncementsPage-C0u6p8a8.js","./AnnouncementsPage-DOwZaaOu.css","./EmailPage-DbZ8F1_b.js","./EmailPage-BmPCDPYC.css","./SecurityPage-BARcvvk8.js","./SecurityPage-DN76ndc_.css","./SystemPage-BVr94jQh.js","./SystemPage-CfMGkvmW.css","./SettingsPage-Bblx2O3L.js","./SettingsPage-BAa-Qu3q.css"])))=>i.map(i=>d[i]); -import{aj as _,D as S,q as h,ax as _e,ay as he,r as A,c as Y,o as ye,R as ge,E as c,G as z,L as p,t as m,n as D,K as W,a3 as X,H as J,J as O,I as Q,az as be,p as M,aA as ve,aB as we,as as Ee}from"./vendor-vue-CVxSw_oJ.js";import{E as ne,a as ae,d as Se,u as Te,c as ke,l as Pe,b as Re,m as Le,e as Ae,t as Oe,s as xe,i as Be,z as Ne}from"./vendor-element-B5S5pUKo.js";import{a as Ce}from"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))s(n);new MutationObserver(n=>{for(const o of n)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&s(i)}).observe(document,{childList:!0,subtree:!0});function a(n){const o={};return n.integrity&&(o.integrity=n.integrity),n.referrerPolicy&&(o.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?o.credentials="include":n.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function s(n){if(n.ep)return;n.ep=!0;const o=a(n);fetch(n.href,o)}})();const se=(e,t)=>{const a=e.__vccOpts||e;for(const[s,n]of t)a[s]=n;return a},De={};function Ve(e,t){const a=_("RouterView");return h(),S(a)}const Me=se(De,[["render",Ve]]),Ie="modulepreload",$e=function(e,t){return new URL(e,t).href},Z={},T=function(t,a,s){let n=Promise.resolve();if(a&&a.length>0){let k=function(f){return Promise.all(f.map(y=>Promise.resolve(y).then(w=>({status:"fulfilled",value:w}),w=>({status:"rejected",reason:w}))))};const i=document.getElementsByTagName("link"),d=document.querySelector("meta[property=csp-nonce]"),v=d?.nonce||d?.getAttribute("nonce");n=k(a.map(f=>{if(f=$e(f,s),f in Z)return;Z[f]=!0;const y=f.endsWith(".css"),w=y?'[rel="stylesheet"]':"";if(s)for(let P=i.length-1;P>=0;P--){const E=i[P];if(E.href===f&&(!y||E.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${f}"]${w}`))return;const g=document.createElement("link");if(g.rel=y?"stylesheet":Ie,y||(g.as="script"),g.crossOrigin="",g.href=f,v&&g.setAttribute("nonce",v),document.head.appendChild(g),y)return new Promise((P,E)=>{g.addEventListener("load",P),g.addEventListener("error",()=>E(new Error(`Unable to preload CSS for ${f}`)))})}))}function o(i){const d=new Event("vite:preloadError",{cancelable:!0});if(d.payload=i,window.dispatchEvent(d),!d.defaultPrevented)throw i}return n.then(i=>{for(const d of i||[])d.status==="rejected"&&o(d.reason);return t().catch(o)})};let ee="",te=0;const Fe=new Set([408,425,429,500,502,503,504]),Ue=1,qe=300;function x(e,t,a=1500){const s=Date.now();e===ee&&s-te=Ue)return!1;const s=String(e?.code||"");if(s==="ECONNABORTED"||s==="ERR_NETWORK")return!0;const n=Number(e?.response?.status||0);return Fe.has(n)}function He(e){return new Promise(t=>{window.setTimeout(t,Math.max(0,Number(e||0)))})}async function Ye(e,t){const a=e?.config||{},s=Number(a.__retry_count||0);a.__retry_count=s+1;const n=qe*(s+1);return await He(n),t.request(a)}const b=Ce.create({baseURL:"/yuyx/api",timeout:3e4,withCredentials:!0});let B=null;async function ze(){return B||(B=ne.prompt("请输入管理员密码进行二次确认","安全确认",{inputType:"password",inputPlaceholder:"管理员密码",confirmButtonText:"确认",cancelButtonText:"取消",inputValidator:e=>!!String(e||"").trim(),inputErrorMessage:"密码不能为空"}).then(async e=>{const t=String(e.value||"").trim();await b.post("/admin/reauth",{password:t}),ae.success("已通过安全确认")}).finally(()=>{B=null}),B)}b.interceptors.request.use(e=>{const t=String(e?.method||"GET").toUpperCase();if(!["GET","HEAD","OPTIONS"].includes(t)){const a=Ge("csrf_token");a&&(e.headers=e.headers||{},e.headers["X-CSRF-Token"]=a)}return e});b.interceptors.response.use(e=>e,async e=>{const t=e?.response?.status,a=e?.response?.data,s=a?.error||a?.message||e?.message||"请求失败",n=!!e?.config?.__silent;if(a?.code==="reauth_required"&&e?.config&&!e.config.__reauth_retry)try{return e.config.__reauth_retry=!0,await ze(),b.request(e.config)}catch{return Promise.reject(e)}return je(e)?Ye(e,b):(t===401?(n||x("401",s,3e3),(window.location?.pathname||"").startsWith("/yuyx")||(window.location.href="/yuyx")):t===403?n||x("403",s,5e3):t?n||x(`http:${t}:${s}`,s):e?.code==="ECONNABORTED"?n||x("timeout","请求超时",3e3):n||x(`net:${s}`,s,3e3),Promise.reject(e))});function oe(e,t=0){let a=!1,s=null,n=0,o=null;async function i(v={}){const k=!!v?.force,f=Date.now();return!k&&a&&fe()).then(y=>{s=y,a=!0;const w=Math.max(0,Number(t)||0);return n=Date.now()+w,y}).finally(()=>{o=null})),o)}function d(){a=!1,s=null,n=0,o=null}return{run:i,clear:d}}const We=1e4,re=oe(async()=>{const{data:e}=await b.get("/feedbacks",{params:{limit:1,offset:0}});return e?.stats},We);async function Rt(e=""){const{data:t}=await b.get("/feedbacks",{params:e?{status:e}:{}});return t}async function Xe(e={}){return re.run(e)}function I(){re.clear()}async function Lt(e,t){const{data:a}=await b.post(`/feedbacks/${e}/reply`,{reply:t});return I(),a}async function At(e){const{data:t}=await b.post(`/feedbacks/${e}/close`);return I(),t}async function Ot(e){const{data:t}=await b.delete(`/feedbacks/${e}`);return I(),t}const Je=15e3,Qe=oe(async()=>{const{data:e}=await b.get("/stats");return e},Je);async function Ze(e={}){return Qe.run(e)}const et={class:"menu-label"},tt={key:1,class:"menu-label"},nt={class:"header-left"},at={class:"header-right"},st={class:"admin-name"},ot={class:"main-shell"},rt={class:"menu-label"},ct={key:1,class:"menu-label"},lt=6e4,it=18e4,ut={__name:"AdminLayout",setup(e){const t=_e(),a=he(),s=A({}),n=Y(()=>s.value?.admin_username||"");async function o(u={}){s.value=await Ze(u)}const i=A(!1),d=A(0);let v=null;async function k(u=null){if(u&&typeof u=="object"){Object.prototype.hasOwnProperty.call(u,"pendingFeedbacks")&&(d.value=Number(u.pendingFeedbacks||0));return}if(!i.value){i.value=!0;try{const r=await Xe();d.value=Number(r?.pending||0)}finally{i.value=!1}}}function f(){return typeof document>"u"?!1:document.visibilityState==="hidden"}function y(){return f()?it:lt}function w(){v&&(window.clearTimeout(v),v=null)}function g(){w(),v=window.setTimeout(async()=>{v=null,await k().catch(()=>{}),g()},y())}function P(){g()}M("refreshStats",o),M("adminStats",s),M("refreshNavBadges",k);const E=A(!1),R=A(!1);let N;function V(){E.value=!!N?.matches,E.value||(R.value=!1)}ye(async()=>{N=window.matchMedia("(max-width: 768px)"),N.addEventListener?.("change",V),V(),await o(),await k(),g(),window.addEventListener("visibilitychange",P)}),ge(()=>{N?.removeEventListener?.("change",V),w(),window.removeEventListener("visibilitychange",P)});const $=[{path:"/reports",label:"报表",icon:Se},{path:"/users",label:"用户",icon:Te},{path:"/feedbacks",label:"反馈",icon:ke,badgeKey:"feedbacks"},{path:"/logs",label:"任务日志",icon:Pe},{path:"/announcements",label:"公告",icon:Re},{path:"/email",label:"邮件",icon:Le},{path:"/security",label:"安全防护",icon:Ae},{path:"/system",label:"系统配置",icon:Oe},{path:"/settings",label:"设置",icon:xe}],F=Y(()=>t.path);function C(u){return u?.badgeKey&&u.badgeKey==="feedbacks"?Number(d.value||0):0}async function ce(){let u=!1;try{await ne.confirm("确定退出管理员登录吗?","退出登录",{confirmButtonText:"退出",cancelButtonText:"取消",type:"warning"}),u=!0}catch(r){const L=String(r||"").toLowerCase();if(L==="cancel"||L==="close")return;try{u=window.confirm("确定退出管理员登录吗?")}catch{u=!1}}if(u)try{await b.post("/logout")}finally{window.location.href="/yuyx"}}async function U(u){await a.push(u),R.value=!1}return(u,r)=>{const L=_("el-icon"),q=_("el-badge"),G=_("el-menu-item"),K=_("el-menu"),le=_("el-aside"),j=_("el-button"),ie=_("el-header"),ue=_("RouterView"),de=_("el-skeleton"),fe=_("el-card"),me=_("el-main"),H=_("el-container"),pe=_("el-drawer");return h(),S(H,{class:"layout-root"},{default:c(()=>[E.value?z("",!0):(h(),S(le,{key:0,width:"220px",class:"layout-aside"},{default:c(()=>[r[2]||(r[2]=m("div",{class:"brand"},[m("div",{class:"brand-title"},"后台管理"),m("div",{class:"brand-sub app-muted"},"知识管理平台")],-1)),p(K,{"default-active":F.value,class:"aside-menu",router:"",onSelect:U},{default:c(()=>[(h(),D(W,null,X($,l=>p(G,{key:l.path,index:l.path},{default:c(()=>[p(L,null,{default:c(()=>[(h(),S(J(l.icon)))]),_:2},1024),C(l)>0?(h(),S(q,{key:0,value:C(l),max:99,class:"menu-badge"},{default:c(()=>[m("span",et,O(l.label),1)]),_:2},1032,["value"])):(h(),D("span",tt,O(l.label),1))]),_:2},1032,["index"])),64))]),_:1},8,["default-active"])]),_:1})),p(H,null,{default:c(()=>[p(ie,{class:"layout-header"},{default:c(()=>[m("div",nt,[E.value?(h(),S(j,{key:0,text:"",class:"header-menu-btn",onClick:r[0]||(r[0]=l=>R.value=!0)},{default:c(()=>[...r[3]||(r[3]=[Q(" 菜单 ",-1)])]),_:1})):z("",!0),r[4]||(r[4]=m("div",{class:"header-title"},"后台管理系统",-1))]),m("div",at,[m("div",st,[r[5]||(r[5]=m("span",{class:"app-muted"},"管理员",-1)),m("strong",null,O(n.value||"-"),1)]),p(j,{type:"primary",plain:"",class:"logout-btn",onClick:ce},{default:c(()=>[...r[6]||(r[6]=[Q("退出",-1)])]),_:1})])]),_:1}),p(me,{class:"layout-main"},{default:c(()=>[m("div",ot,[(h(),S(be,null,{default:c(()=>[p(ue)]),fallback:c(()=>[p(fe,{shadow:"never","body-style":{padding:"16px"},class:"fallback-card"},{default:c(()=>[p(de,{rows:5,animated:""})]),_:1})]),_:1}))])]),_:1})]),_:1}),p(pe,{modelValue:R.value,"onUpdate:modelValue":r[1]||(r[1]=l=>R.value=l),size:"min(82vw, 280px)",direction:"ltr","with-header":!1},{default:c(()=>[r[7]||(r[7]=m("div",{class:"drawer-brand"},[m("div",{class:"brand-title"},"后台管理"),m("div",{class:"brand-sub app-muted"},"知识管理平台")],-1)),p(K,{"default-active":F.value,class:"aside-menu",router:"",onSelect:U},{default:c(()=>[(h(),D(W,null,X($,l=>p(G,{key:l.path,index:l.path},{default:c(()=>[p(L,null,{default:c(()=>[(h(),S(J(l.icon)))]),_:2},1024),C(l)>0?(h(),S(q,{key:0,value:C(l),max:99,class:"menu-badge"},{default:c(()=>[m("span",rt,O(l.label),1)]),_:2},1032,["value"])):(h(),D("span",ct,O(l.label),1))]),_:2},1032,["index"])),64))]),_:1},8,["default-active"])]),_:1},8,["modelValue"])]),_:1})}}},dt=se(ut,[["__scopeId","data-v-93b2fed7"]]),ft=()=>T(()=>import("./ReportPage-CqUGYZcC.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11]),import.meta.url),mt=()=>T(()=>import("./UsersPage-CwfHMchI.js"),__vite__mapDeps([12,13,1,2,3,4,10,14]),import.meta.url),pt=()=>T(()=>import("./FeedbacksPage-Dwzul2Z8.js"),__vite__mapDeps([15,8,2,9,1,3,4,10,16]),import.meta.url),_t=()=>T(()=>import("./LogsPage-Bs3Ge-t3.js"),__vite__mapDeps([17,13,6,1,2,3,4,10,18]),import.meta.url),ht=()=>T(()=>import("./AnnouncementsPage-C0u6p8a8.js"),__vite__mapDeps([19,1,2,3,4,10,20]),import.meta.url),yt=()=>T(()=>import("./EmailPage-DbZ8F1_b.js"),__vite__mapDeps([21,5,8,2,9,1,3,4,10,22]),import.meta.url),gt=()=>T(()=>import("./SecurityPage-BARcvvk8.js"),__vite__mapDeps([23,8,2,9,1,3,4,10,24]),import.meta.url),bt=()=>T(()=>import("./SystemPage-BVr94jQh.js"),__vite__mapDeps([25,7,1,2,3,4,10,26]),import.meta.url),vt=()=>T(()=>import("./SettingsPage-Bblx2O3L.js"),__vite__mapDeps([27,1,2,3,4,10,28]),import.meta.url),wt=[{path:"/",component:dt,children:[{path:"",redirect:"/reports"},{path:"/pending",redirect:"/reports"},{path:"/stats",redirect:"/reports"},{path:"/reports",name:"reports",component:ft},{path:"/users",name:"users",component:mt},{path:"/feedbacks",name:"feedbacks",component:pt},{path:"/logs",name:"logs",component:_t},{path:"/announcements",name:"announcements",component:ht},{path:"/email",name:"email",component:yt},{path:"/security",name:"security",component:gt},{path:"/system",name:"system",component:bt},{path:"/settings",name:"settings",component:vt}]}],Et=ve({history:we(),routes:wt});Ee(Me).use(Et).use(Be,{locale:Ne}).mount("#app");export{se as _,b as a,Rt as b,oe as c,At as d,Ot as e,Xe as f,Lt as r}; diff --git a/static/admin/assets/index-C1f9ticl.js b/static/admin/assets/index-C1f9ticl.js new file mode 100644 index 0000000..40d8c53 --- /dev/null +++ b/static/admin/assets/index-C1f9ticl.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./ReportPage-fzVH-d9u.js","./vendor-element-B5S5pUKo.js","./vendor-vue-CVxSw_oJ.js","./vendor-misc-BeoNyvBp.js","./vendor-element-C68yOrAy.css","./email-px7YBG2O.js","./tasks-Bep0SUyu.js","./system-ZDPnxnIu.js","./MetricGrid-BnihYB_8.js","./MetricGrid-yP_dkP6X.css","./vendor-axios-B9ygI19o.js","./ReportPage-BCQBCnjY.css","./UsersPage-DJZUCpfb.js","./users-te9ySk34.js","./UsersPage-BNDnhJe0.css","./FeedbacksPage-BAnFKHSL.js","./FeedbacksPage-mrXjCiV2.css","./LogsPage-DFPeq0bL.js","./LogsPage-D1bozCEo.css","./AnnouncementsPage-C6UgwLIT.js","./AnnouncementsPage-DOwZaaOu.css","./EmailPage-CATruPK6.js","./EmailPage-BmPCDPYC.css","./SecurityPage-xwMQfhuh.js","./SecurityPage-DN76ndc_.css","./SystemPage-D3eBPCNe.js","./SystemPage-BhhEz4Qz.css","./SettingsPage-DRqlQLxJ.js","./SettingsPage-BAa-Qu3q.css"])))=>i.map(i=>d[i]); +import{aj as h,D as S,q as y,ax as Se,ay as ke,r as O,c as Z,o as Te,R as Ae,E as i,G as ee,L as _,t as m,n as I,K as te,a3 as ne,H as ae,J as C,I as se,az as Pe,p as $,aA as Re,aB as Le,as as xe}from"./vendor-vue-CVxSw_oJ.js";import{E as ie,a as le,d as Ne,u as Oe,c as Ce,l as Be,b as De,m as Me,e as Ve,t as Ie,s as Fe,i as Ke,z as $e}from"./vendor-element-B5S5pUKo.js";import{a as qe}from"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))s(a);new MutationObserver(a=>{for(const o of a)if(o.type==="childList")for(const l of o.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&s(l)}).observe(document,{childList:!0,subtree:!0});function n(a){const o={};return a.integrity&&(o.integrity=a.integrity),a.referrerPolicy&&(o.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?o.credentials="include":a.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function s(a){if(a.ep)return;a.ep=!0;const o=n(a);fetch(a.href,o)}})();const ue=(e,t)=>{const n=e.__vccOpts||e;for(const[s,a]of t)n[s]=a;return n},Ue={};function Ge(e,t){const n=h("RouterView");return y(),S(n)}const je=ue(Ue,[["render",Ge]]),He="modulepreload",Ye=function(e,t){return new URL(e,t).href},oe={},k=function(t,n,s){let a=Promise.resolve();if(n&&n.length>0){let T=function(f){return Promise.all(f.map(g=>Promise.resolve(g).then(w=>({status:"fulfilled",value:w}),w=>({status:"rejected",reason:w}))))};const l=document.getElementsByTagName("link"),r=document.querySelector("meta[property=csp-nonce]"),v=r?.nonce||r?.getAttribute("nonce");a=T(n.map(f=>{if(f=Ye(f,s),f in oe)return;oe[f]=!0;const g=f.endsWith(".css"),w=g?'[rel="stylesheet"]':"";if(s)for(let A=l.length-1;A>=0;A--){const E=l[A];if(E.href===f&&(!g||E.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${f}"]${w}`))return;const b=document.createElement("link");if(b.rel=g?"stylesheet":He,g||(b.as="script"),b.crossOrigin="",b.href=f,v&&b.setAttribute("nonce",v),document.head.appendChild(b),g)return new Promise((A,E)=>{b.addEventListener("load",A),b.addEventListener("error",()=>E(new Error(`Unable to preload CSS for ${f}`)))})}))}function o(l){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=l,window.dispatchEvent(r),!r.defaultPrevented)throw l}return a.then(l=>{for(const r of l||[])r.status==="rejected"&&o(r.reason);return t().catch(o)})};let re="",ce=0;const ze=new Set([408,425,429,500,502,503,504]),We=1,Je=300;function B(e,t,n=1500){const s=Date.now();e===re&&s-ce=We)return!1;const s=String(e?.code||"");if(s==="ECONNABORTED"||s==="ERR_NETWORK")return!0;const a=Number(e?.response?.status||0);return ze.has(a)}function et(e){return new Promise(t=>{window.setTimeout(t,Math.max(0,Number(e||0)))})}async function tt(e,t){const n=e?.config||{},s=Number(n.__retry_count||0);n.__retry_count=s+1;const a=Je*(s+1);return await et(a),t.request(n)}const p=qe.create({baseURL:"/yuyx/api",timeout:3e4,withCredentials:!0});let D=null;async function nt(){return D||(D=ie.prompt("请输入管理员密码进行二次确认","安全确认",{inputType:"password",inputPlaceholder:"管理员密码",confirmButtonText:"确认",cancelButtonText:"取消",inputValidator:e=>!!String(e||"").trim(),inputErrorMessage:"密码不能为空"}).then(async e=>{const t=String(e.value||"").trim();await p.post("/admin/reauth",{password:t}),le.success("已通过安全确认")}).finally(()=>{D=null}),D)}p.interceptors.request.use(e=>{const t=String(e?.method||"GET").toUpperCase();if(!["GET","HEAD","OPTIONS"].includes(t)){const n=Xe("csrf_token");n&&(e.headers=e.headers||{},e.headers["X-CSRF-Token"]=n)}return e});p.interceptors.response.use(e=>e,async e=>{const t=e?.response?.status,n=e?.response?.data,s=n?.error||n?.message||e?.message||"请求失败",a=!!e?.config?.__silent;if(n?.code==="reauth_required"&&e?.config&&!e.config.__reauth_retry)try{return e.config.__reauth_retry=!0,await nt(),p.request(e.config)}catch{return Promise.reject(e)}return Ze(e)?tt(e,p):(t===401?(a||B("401",s,3e3),(window.location?.pathname||"").startsWith("/yuyx")||(window.location.href="/yuyx")):t===403?a||B("403",s,5e3):t?a||B(`http:${t}:${s}`,s):e?.code==="ECONNABORTED"?a||B("timeout","请求超时",3e3):a||B(`net:${s}`,s,3e3),Promise.reject(e))});function de(e,t=0){let n=!1,s=null,a=0,o=null;async function l(v={}){const T=!!v?.force,f=Date.now();return!T&&n&&fe()).then(g=>{s=g,n=!0;const w=Math.max(0,Number(t)||0);return a=Date.now()+w,g}).finally(()=>{o=null})),o)}function r(){n=!1,s=null,a=0,o=null}return{run:l,clear:r}}const at=1e4,fe=de(async()=>{const{data:e}=await p.get("/feedbacks",{params:{limit:1,offset:0}});return e?.stats},at);async function Ut(e=""){const{data:t}=await p.get("/feedbacks",{params:e?{status:e}:{}});return t}async function st(e={}){return fe.run(e)}function q(){fe.clear()}async function Gt(e,t){const{data:n}=await p.post(`/feedbacks/${e}/reply`,{reply:t});return q(),n}async function jt(e){const{data:t}=await p.post(`/feedbacks/${e}/close`);return q(),t}async function Ht(e){const{data:t}=await p.delete(`/feedbacks/${e}`);return q(),t}const ot=15e3,rt=de(async()=>{const{data:e}=await p.get("/stats");return e},ot);async function ct(e={}){return rt.run(e)}async function it(e={},t={}){const{data:n}=await p.get("/kdocs/status",{params:e,...t});return n}async function Yt(e={}){const t={force:!0,...e},{data:n}=await p.post("/kdocs/qr",t);return n}async function zt(){const{data:e}=await p.post("/kdocs/clear-login",{});return e}const U="admin:kdocs:status:v1",me=300*1e3;let P=null,L=0,R=null;function G(){return Date.now()}function F(e){return!e||typeof e!="object"?{}:e}function lt(){try{const e=window.sessionStorage.getItem(U);if(!e)return null;const t=JSON.parse(e);if(!t||typeof t!="object")return null;const n=Number(t.updated_at||0),s=F(t.status);return n?{status:s,updatedAt:n}:null}catch{return null}}function ut(e,t){try{window.sessionStorage.setItem(U,JSON.stringify({status:F(e),updated_at:Number(t||G())}))}catch{}}function dt(){if(P!==null)return;const e=lt();e&&(P=e.status,L=e.updatedAt)}function pe(e){return P=F(e),L=G(),ut(P,L),P}function ft(e){if(P===null||!L)return!1;const t=Number(e);return!Number.isFinite(t)||t<0?!0:G()-L<=t}function mt(e={}){dt();const t=e.maxAgeMs??me;return ft(t)?F(P):null}function Wt(e){return pe(e)}function pt(){P=null,L=0,R=null;try{window.sessionStorage.removeItem(U)}catch{}}async function _t(e={}){const{force:t=!1,maxAgeMs:n=me,silent:s=!0,live:a=0}=e;if(!t){const r=mt({maxAgeMs:n});if(r)return r}return R||(R=it(a?{live:1}:{},{__silent:!!s,__no_retry:!0,timeout:8e3}).then(r=>pe(r||{})).finally(()=>{R=null}),R)}const ht={class:"menu-label"},yt={key:1,class:"menu-label"},gt={class:"header-left"},bt={class:"header-right"},vt={class:"admin-name"},wt={class:"main-shell"},Et={class:"menu-label"},St={key:1,class:"menu-label"},kt=6e4,Tt=18e4,At={__name:"AdminLayout",setup(e){const t=Se(),n=ke(),s=O({}),a=Z(()=>s.value?.admin_username||"");async function o(d={}){s.value=await ct(d)}const l=O(!1),r=O(0);let v=null;async function T(d=null){if(d&&typeof d=="object"){Object.prototype.hasOwnProperty.call(d,"pendingFeedbacks")&&(r.value=Number(d.pendingFeedbacks||0));return}if(!l.value){l.value=!0;try{const c=await st();r.value=Number(c?.pending||0)}finally{l.value=!1}}}function f(){return typeof document>"u"?!1:document.visibilityState==="hidden"}function g(){return f()?Tt:kt}function w(){v&&(window.clearTimeout(v),v=null)}function b(){w(),v=window.setTimeout(async()=>{v=null,await T().catch(()=>{}),b()},g())}function A(){b()}$("refreshStats",o),$("adminStats",s),$("refreshNavBadges",T);const E=O(!1),x=O(!1);let M;function K(){E.value=!!M?.matches,E.value||(x.value=!1)}Te(async()=>{M=window.matchMedia("(max-width: 768px)"),M.addEventListener?.("change",K),K(),_t({maxAgeMs:6e4,silent:!0}).catch(()=>{}),await o(),await T(),b(),window.addEventListener("visibilitychange",A)}),Ae(()=>{M?.removeEventListener?.("change",K),w(),window.removeEventListener("visibilitychange",A)});const j=[{path:"/reports",label:"报表",icon:Ne},{path:"/users",label:"用户",icon:Oe},{path:"/feedbacks",label:"反馈",icon:Ce,badgeKey:"feedbacks"},{path:"/logs",label:"任务日志",icon:Be},{path:"/announcements",label:"公告",icon:De},{path:"/email",label:"邮件",icon:Me},{path:"/security",label:"安全防护",icon:Ve},{path:"/system",label:"系统配置",icon:Ie},{path:"/settings",label:"设置",icon:Fe}],H=Z(()=>t.path);function V(d){return d?.badgeKey&&d.badgeKey==="feedbacks"?Number(r.value||0):0}async function _e(){let d=!1;try{await ie.confirm("确定退出管理员登录吗?","退出登录",{confirmButtonText:"退出",cancelButtonText:"取消",type:"warning"}),d=!0}catch(c){const N=String(c||"").toLowerCase();if(N==="cancel"||N==="close")return;try{d=window.confirm("确定退出管理员登录吗?")}catch{d=!1}}if(d)try{await p.post("/logout")}finally{pt(),window.location.href="/yuyx"}}async function Y(d){await n.push(d),x.value=!1}return(d,c)=>{const N=h("el-icon"),z=h("el-badge"),W=h("el-menu-item"),J=h("el-menu"),he=h("el-aside"),X=h("el-button"),ye=h("el-header"),ge=h("RouterView"),be=h("el-skeleton"),ve=h("el-card"),we=h("el-main"),Q=h("el-container"),Ee=h("el-drawer");return y(),S(Q,{class:"layout-root"},{default:i(()=>[E.value?ee("",!0):(y(),S(he,{key:0,width:"220px",class:"layout-aside"},{default:i(()=>[c[2]||(c[2]=m("div",{class:"brand"},[m("div",{class:"brand-title"},"后台管理"),m("div",{class:"brand-sub app-muted"},"知识管理平台")],-1)),_(J,{"default-active":H.value,class:"aside-menu",router:"",onSelect:Y},{default:i(()=>[(y(),I(te,null,ne(j,u=>_(W,{key:u.path,index:u.path},{default:i(()=>[_(N,null,{default:i(()=>[(y(),S(ae(u.icon)))]),_:2},1024),V(u)>0?(y(),S(z,{key:0,value:V(u),max:99,class:"menu-badge"},{default:i(()=>[m("span",ht,C(u.label),1)]),_:2},1032,["value"])):(y(),I("span",yt,C(u.label),1))]),_:2},1032,["index"])),64))]),_:1},8,["default-active"])]),_:1})),_(Q,null,{default:i(()=>[_(ye,{class:"layout-header"},{default:i(()=>[m("div",gt,[E.value?(y(),S(X,{key:0,text:"",class:"header-menu-btn",onClick:c[0]||(c[0]=u=>x.value=!0)},{default:i(()=>[...c[3]||(c[3]=[se(" 菜单 ",-1)])]),_:1})):ee("",!0),c[4]||(c[4]=m("div",{class:"header-title"},"后台管理系统",-1))]),m("div",bt,[m("div",vt,[c[5]||(c[5]=m("span",{class:"app-muted"},"管理员",-1)),m("strong",null,C(a.value||"-"),1)]),_(X,{type:"primary",plain:"",class:"logout-btn",onClick:_e},{default:i(()=>[...c[6]||(c[6]=[se("退出",-1)])]),_:1})])]),_:1}),_(we,{class:"layout-main"},{default:i(()=>[m("div",wt,[(y(),S(Pe,null,{default:i(()=>[_(ge)]),fallback:i(()=>[_(ve,{shadow:"never","body-style":{padding:"16px"},class:"fallback-card"},{default:i(()=>[_(be,{rows:5,animated:""})]),_:1})]),_:1}))])]),_:1})]),_:1}),_(Ee,{modelValue:x.value,"onUpdate:modelValue":c[1]||(c[1]=u=>x.value=u),size:"min(82vw, 280px)",direction:"ltr","with-header":!1},{default:i(()=>[c[7]||(c[7]=m("div",{class:"drawer-brand"},[m("div",{class:"brand-title"},"后台管理"),m("div",{class:"brand-sub app-muted"},"知识管理平台")],-1)),_(J,{"default-active":H.value,class:"aside-menu",router:"",onSelect:Y},{default:i(()=>[(y(),I(te,null,ne(j,u=>_(W,{key:u.path,index:u.path},{default:i(()=>[_(N,null,{default:i(()=>[(y(),S(ae(u.icon)))]),_:2},1024),V(u)>0?(y(),S(z,{key:0,value:V(u),max:99,class:"menu-badge"},{default:i(()=>[m("span",Et,C(u.label),1)]),_:2},1032,["value"])):(y(),I("span",St,C(u.label),1))]),_:2},1032,["index"])),64))]),_:1},8,["default-active"])]),_:1},8,["modelValue"])]),_:1})}}},Pt=ue(At,[["__scopeId","data-v-c6ed865d"]]),Rt=()=>k(()=>import("./ReportPage-fzVH-d9u.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11]),import.meta.url),Lt=()=>k(()=>import("./UsersPage-DJZUCpfb.js"),__vite__mapDeps([12,13,1,2,3,4,10,14]),import.meta.url),xt=()=>k(()=>import("./FeedbacksPage-BAnFKHSL.js"),__vite__mapDeps([15,8,2,9,1,3,4,10,16]),import.meta.url),Nt=()=>k(()=>import("./LogsPage-DFPeq0bL.js"),__vite__mapDeps([17,13,6,1,2,3,4,10,18]),import.meta.url),Ot=()=>k(()=>import("./AnnouncementsPage-C6UgwLIT.js"),__vite__mapDeps([19,1,2,3,4,10,20]),import.meta.url),Ct=()=>k(()=>import("./EmailPage-CATruPK6.js"),__vite__mapDeps([21,5,8,2,9,1,3,4,10,22]),import.meta.url),Bt=()=>k(()=>import("./SecurityPage-xwMQfhuh.js"),__vite__mapDeps([23,8,2,9,1,3,4,10,24]),import.meta.url),Dt=()=>k(()=>import("./SystemPage-D3eBPCNe.js"),__vite__mapDeps([25,7,1,2,3,4,10,26]),import.meta.url),Mt=()=>k(()=>import("./SettingsPage-DRqlQLxJ.js"),__vite__mapDeps([27,1,2,3,4,10,28]),import.meta.url),Vt=[{path:"/",component:Pt,children:[{path:"",redirect:"/reports"},{path:"/pending",redirect:"/reports"},{path:"/stats",redirect:"/reports"},{path:"/reports",name:"reports",component:Rt},{path:"/users",name:"users",component:Lt},{path:"/feedbacks",name:"feedbacks",component:xt},{path:"/logs",name:"logs",component:Nt},{path:"/announcements",name:"announcements",component:Ot},{path:"/email",name:"email",component:Ct},{path:"/security",name:"security",component:Bt},{path:"/system",name:"system",component:Dt},{path:"/settings",name:"settings",component:Mt}]}],It=Re({history:Le(),routes:Vt});xe(je).use(It).use(Ke,{locale:$e}).mount("#app");export{ue as _,p as a,Ut as b,de as c,jt as d,Ht as e,st as f,mt as g,Yt as h,zt as i,it as j,_t as p,Gt as r,Wt as u}; diff --git a/static/admin/assets/index-CVq5QfHO.css b/static/admin/assets/index-Dh0BbqTX.css similarity index 66% rename from static/admin/assets/index-CVq5QfHO.css rename to static/admin/assets/index-Dh0BbqTX.css index c21d1ce..1929763 100644 --- a/static/admin/assets/index-CVq5QfHO.css +++ b/static/admin/assets/index-Dh0BbqTX.css @@ -1 +1 @@ -.layout-root[data-v-93b2fed7]{height:100%}.layout-aside[data-v-93b2fed7]{background:linear-gradient(180deg,#fffffffa,#f8fafcf0);border-right:1px solid var(--app-border);box-shadow:4px 0 16px #0f172a0a}.brand[data-v-93b2fed7],.drawer-brand[data-v-93b2fed7]{padding:18px 16px 14px}.brand[data-v-93b2fed7]{border-bottom:1px solid rgba(15,23,42,.06)}.brand-title[data-v-93b2fed7]{font-size:16px;font-weight:800;letter-spacing:.2px}.brand-sub[data-v-93b2fed7]{margin-top:4px;font-size:12px}.aside-menu[data-v-93b2fed7]{border-right:none;padding:8px;background:transparent}.aside-menu[data-v-93b2fed7] .el-menu-item{height:42px;line-height:42px;margin:3px 0;border-radius:10px;color:#334155;font-weight:600}.aside-menu[data-v-93b2fed7] .el-menu-item .el-icon{margin-right:10px}.aside-menu[data-v-93b2fed7] .el-menu-item:hover{background:#3b82f614;color:#1d4ed8}.aside-menu[data-v-93b2fed7] .el-menu-item.is-active{background:linear-gradient(135deg,#2563eb1f,#7c3aed1a);color:#1e40af}.menu-label[data-v-93b2fed7]{display:inline-flex;align-items:center;min-width:0}.menu-badge[data-v-93b2fed7]{display:inline-flex;align-items:center}.fallback-card[data-v-93b2fed7]{min-height:160px;border-radius:var(--app-radius-lg);border:1px solid var(--app-border)}.layout-header[data-v-93b2fed7]{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:space-between;gap:12px;height:58px;padding:0 18px;background:#ffffffc7;-webkit-backdrop-filter:saturate(180%) blur(10px);backdrop-filter:saturate(180%) blur(10px);border-bottom:1px solid var(--app-border)}.header-left[data-v-93b2fed7]{display:flex;align-items:center;gap:10px;min-width:0}.header-title[data-v-93b2fed7]{font-size:15px;font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.header-menu-btn[data-v-93b2fed7]{padding-left:0;padding-right:0}.header-right[data-v-93b2fed7]{display:flex;align-items:center;gap:12px}.admin-name[data-v-93b2fed7]{display:flex;align-items:baseline;gap:8px;font-size:13px;color:#334155}.admin-name strong[data-v-93b2fed7]{color:#0f172a;font-weight:800}.logout-btn[data-v-93b2fed7]{min-width:74px}.layout-main[data-v-93b2fed7]{padding:18px}.main-shell[data-v-93b2fed7]{width:100%;max-width:1600px;margin:0 auto}@media(max-width:768px){.layout-header[data-v-93b2fed7]{flex-wrap:wrap;height:auto;padding:10px 12px}.header-right[data-v-93b2fed7]{width:100%;justify-content:flex-end}.admin-name .app-muted[data-v-93b2fed7],.admin-name strong[data-v-93b2fed7]{display:none}.layout-main[data-v-93b2fed7]{padding:12px}}:root{--app-bg: #f4f6fb;--app-text: #111827;--app-muted: #6b7280;--app-border: rgba(15, 23, 42, .1);--app-border-strong: rgba(15, 23, 42, .14);--app-radius: 12px;--app-radius-lg: 14px;--app-shadow-soft: 0 8px 24px rgba(15, 23, 42, .05);--app-shadow: 0 12px 30px rgba(15, 23, 42, .08);--app-card-bg: rgba(255, 255, 255, .94);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Arial,sans-serif;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}html,body,#app{height:100%}*{box-sizing:border-box}body{margin:0;color:var(--app-text);background:radial-gradient(1200px 500px at -10% -10%,rgba(59,130,246,.12),transparent 55%),radial-gradient(1000px 420px at 110% 0%,rgba(139,92,246,.1),transparent 50%),var(--app-bg)}a{color:inherit;text-decoration:none}.app-page-title{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 14px}.app-page-title h2{margin:0;font-size:19px;font-weight:800;letter-spacing:.2px}.app-muted{color:var(--app-muted)}.page-stack{display:flex;flex-direction:column;gap:14px;min-width:0}.el-card{border-radius:var(--app-radius-lg);border:1px solid var(--app-border);background:var(--app-card-bg);box-shadow:var(--app-shadow-soft)}.el-button{border-radius:10px;font-weight:600}.el-input__wrapper,.el-textarea__inner,.el-select__wrapper,.el-input-number,.el-picker__wrapper{border-radius:10px}.el-table{border-radius:10px;overflow:hidden}.el-table th.el-table__cell{background:#f8fafc;color:#334155;font-weight:700}.el-table td.el-table__cell,.el-table th.el-table__cell{padding-top:11px;padding-bottom:11px}.el-table .el-table__row:hover>td.el-table__cell{background:#f8fbff}.el-tag{border-radius:999px}.el-dialog{border-radius:var(--app-radius-lg)}@media(max-width:768px){.app-page-title{flex-wrap:wrap;align-items:flex-start}.app-page-title h2{font-size:17px}.el-dialog{max-width:92vw}.el-form-item{flex-direction:column;align-items:stretch}.el-form-item__label{width:auto!important;justify-content:flex-start!important;padding:0 0 6px!important;line-height:1.4;text-align:left!important}.el-form-item__content{margin-left:0!important;width:100%}}.section-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.section-title{margin:0;font-size:15px;font-weight:800;letter-spacing:.2px}.toolbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.table-wrap{overflow-x:auto;border-radius:10px;border:1px solid var(--app-border);background:#fff}.pagination{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px;flex-wrap:wrap}.page-hint{font-size:12px}.el-tabs__item{font-weight:700}.el-form-item{margin-bottom:18px}@media(max-width:768px){.pagination{justify-content:flex-start}}@media(max-width:900px){.toolbar{width:100%}.toolbar>*{min-width:0}}@media(max-width:768px){.app-page-title>div{width:100%}.app-page-title .toolbar{width:100%}.toolbar>*{flex:1 1 calc(50% - 6px)}.toolbar .el-button,.toolbar .el-select,.toolbar .el-input,.toolbar .el-input-number{width:100%!important}.section-head{align-items:flex-start}.section-head>*{width:100%}.table-wrap{-webkit-overflow-scrolling:touch}.table-wrap .el-table{min-width:700px}.el-pagination{width:100%;justify-content:flex-start}}@media(max-width:520px){.toolbar>*{flex-basis:100%}.table-wrap .el-table{min-width:620px}} +.layout-root[data-v-c6ed865d]{height:100%}.layout-aside[data-v-c6ed865d]{background:linear-gradient(180deg,#fffffffa,#f8fafcf0);border-right:1px solid var(--app-border);box-shadow:4px 0 16px #0f172a0a}.brand[data-v-c6ed865d],.drawer-brand[data-v-c6ed865d]{padding:18px 16px 14px}.brand[data-v-c6ed865d]{border-bottom:1px solid rgba(15,23,42,.06)}.brand-title[data-v-c6ed865d]{font-size:16px;font-weight:800;letter-spacing:.2px}.brand-sub[data-v-c6ed865d]{margin-top:4px;font-size:12px}.aside-menu[data-v-c6ed865d]{border-right:none;padding:8px;background:transparent}.aside-menu[data-v-c6ed865d] .el-menu-item{height:42px;line-height:42px;margin:3px 0;border-radius:10px;color:#334155;font-weight:600}.aside-menu[data-v-c6ed865d] .el-menu-item .el-icon{margin-right:10px}.aside-menu[data-v-c6ed865d] .el-menu-item:hover{background:#3b82f614;color:#1d4ed8}.aside-menu[data-v-c6ed865d] .el-menu-item.is-active{background:linear-gradient(135deg,#2563eb1f,#7c3aed1a);color:#1e40af}.menu-label[data-v-c6ed865d]{display:inline-flex;align-items:center;min-width:0}.menu-badge[data-v-c6ed865d]{display:inline-flex;align-items:center}.fallback-card[data-v-c6ed865d]{min-height:160px;border-radius:var(--app-radius-lg);border:1px solid var(--app-border)}.layout-header[data-v-c6ed865d]{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:space-between;gap:12px;height:58px;padding:0 18px;background:#ffffffc7;-webkit-backdrop-filter:saturate(180%) blur(10px);backdrop-filter:saturate(180%) blur(10px);border-bottom:1px solid var(--app-border)}.header-left[data-v-c6ed865d]{display:flex;align-items:center;gap:10px;min-width:0}.header-title[data-v-c6ed865d]{font-size:15px;font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.header-menu-btn[data-v-c6ed865d]{padding-left:0;padding-right:0}.header-right[data-v-c6ed865d]{display:flex;align-items:center;gap:12px}.admin-name[data-v-c6ed865d]{display:flex;align-items:baseline;gap:8px;font-size:13px;color:#334155}.admin-name strong[data-v-c6ed865d]{color:#0f172a;font-weight:800}.logout-btn[data-v-c6ed865d]{min-width:74px}.layout-main[data-v-c6ed865d]{padding:18px}.main-shell[data-v-c6ed865d]{width:100%;max-width:1600px;margin:0 auto}@media(max-width:768px){.layout-header[data-v-c6ed865d]{flex-wrap:wrap;height:auto;padding:10px 12px}.header-right[data-v-c6ed865d]{width:100%;justify-content:flex-end}.admin-name .app-muted[data-v-c6ed865d],.admin-name strong[data-v-c6ed865d]{display:none}.layout-main[data-v-c6ed865d]{padding:12px}}:root{--app-bg: #f4f6fb;--app-text: #111827;--app-muted: #6b7280;--app-border: rgba(15, 23, 42, .1);--app-border-strong: rgba(15, 23, 42, .14);--app-radius: 12px;--app-radius-lg: 14px;--app-shadow-soft: 0 8px 24px rgba(15, 23, 42, .05);--app-shadow: 0 12px 30px rgba(15, 23, 42, .08);--app-card-bg: rgba(255, 255, 255, .94);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Arial,sans-serif;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}html,body,#app{height:100%}*{box-sizing:border-box}body{margin:0;color:var(--app-text);background:radial-gradient(1200px 500px at -10% -10%,rgba(59,130,246,.12),transparent 55%),radial-gradient(1000px 420px at 110% 0%,rgba(139,92,246,.1),transparent 50%),var(--app-bg)}a{color:inherit;text-decoration:none}.app-page-title{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 14px}.app-page-title h2{margin:0;font-size:19px;font-weight:800;letter-spacing:.2px}.app-muted{color:var(--app-muted)}.page-stack{display:flex;flex-direction:column;gap:14px;min-width:0}.el-card{border-radius:var(--app-radius-lg);border:1px solid var(--app-border);background:var(--app-card-bg);box-shadow:var(--app-shadow-soft)}.el-button{border-radius:10px;font-weight:600}.el-input__wrapper,.el-textarea__inner,.el-select__wrapper,.el-input-number,.el-picker__wrapper{border-radius:10px}.el-table{border-radius:10px;overflow:hidden}.el-table th.el-table__cell{background:#f8fafc;color:#334155;font-weight:700}.el-table td.el-table__cell,.el-table th.el-table__cell{padding-top:11px;padding-bottom:11px}.el-table .el-table__row:hover>td.el-table__cell{background:#f8fbff}.el-tag{border-radius:999px}.el-dialog{border-radius:var(--app-radius-lg)}@media(max-width:768px){.app-page-title{flex-wrap:wrap;align-items:flex-start}.app-page-title h2{font-size:17px}.el-dialog{max-width:92vw}.el-form-item{flex-direction:column;align-items:stretch}.el-form-item__label{width:auto!important;justify-content:flex-start!important;padding:0 0 6px!important;line-height:1.4;text-align:left!important}.el-form-item__content{margin-left:0!important;width:100%}}.section-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.section-title{margin:0;font-size:15px;font-weight:800;letter-spacing:.2px}.toolbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.table-wrap{overflow-x:auto;border-radius:10px;border:1px solid var(--app-border);background:#fff}.pagination{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px;flex-wrap:wrap}.page-hint{font-size:12px}.el-tabs__item{font-weight:700}.el-form-item{margin-bottom:18px}@media(max-width:768px){.pagination{justify-content:flex-start}}@media(max-width:900px){.toolbar{width:100%}.toolbar>*{min-width:0}}@media(max-width:768px){.app-page-title>div{width:100%}.app-page-title .toolbar{width:100%}.toolbar>*{flex:1 1 calc(50% - 6px)}.toolbar .el-button,.toolbar .el-select,.toolbar .el-input,.toolbar .el-input-number{width:100%!important}.section-head{align-items:flex-start}.section-head>*{width:100%}.table-wrap{-webkit-overflow-scrolling:touch}.table-wrap .el-table{min-width:700px}.el-pagination{width:100%;justify-content:flex-start}}@media(max-width:520px){.toolbar>*{flex-basis:100%}.table-wrap .el-table{min-width:620px}} diff --git a/static/admin/assets/system-CiDlQnoe.js b/static/admin/assets/system-ZDPnxnIu.js similarity index 76% rename from static/admin/assets/system-CiDlQnoe.js rename to static/admin/assets/system-ZDPnxnIu.js index b5ad840..25fa192 100644 --- a/static/admin/assets/system-CiDlQnoe.js +++ b/static/admin/assets/system-ZDPnxnIu.js @@ -1 +1 @@ -import{c as s,a}from"./index-BNslg8wp.js";const e=s(async()=>{const{data:t}=await a.get("/system/config");return t},15e3);async function o(t={}){return e.run(t)}async function r(t){const{data:n}=await a.post("/system/config",t);return e.clear(),n}export{o as f,r as u}; +import{c as s,a}from"./index-C1f9ticl.js";const e=s(async()=>{const{data:t}=await a.get("/system/config");return t},15e3);async function o(t={}){return e.run(t)}async function r(t){const{data:n}=await a.post("/system/config",t);return e.clear(),n}export{o as f,r as u}; diff --git a/static/admin/assets/tasks-DqqATNe_.js b/static/admin/assets/tasks-Bep0SUyu.js similarity index 93% rename from static/admin/assets/tasks-DqqATNe_.js rename to static/admin/assets/tasks-Bep0SUyu.js index 097350e..0aa11cf 100644 --- a/static/admin/assets/tasks-DqqATNe_.js +++ b/static/admin/assets/tasks-Bep0SUyu.js @@ -1 +1 @@ -import{c as s,a}from"./index-BNslg8wp.js";const c=s(async()=>{const{data:t}=await a.get("/server/info");return t},3e4),o=s(async()=>{const{data:t}=await a.get("/docker_stats");return t},8e3),u=s(async()=>{const{data:t}=await a.get("/request_metrics");return t},1e4),i=s(async()=>{const{data:t}=await a.get("/slow_sql_metrics");return t},1e4),e=s(async()=>{const{data:t}=await a.get("/task/stats");return t},4e3),r=s(async()=>{const{data:t}=await a.get("/task/running");return t},2e3);async function g(t={}){return c.run(t)}async function y(t={}){return o.run(t)}async function d(t={}){return u.run(t)}async function k(t={}){return i.run(t)}async function l(t={}){return e.run(t)}async function w(t={}){return r.run(t)}async function _(t){const{data:n}=await a.get("/task/logs",{params:t});return n}async function h(t){const{data:n}=await a.post("/task/logs/clear",{days:t});return e.clear(),r.clear(),n}export{w as a,g as b,y as c,d,k as e,l as f,_ as g,h}; +import{c as s,a}from"./index-C1f9ticl.js";const c=s(async()=>{const{data:t}=await a.get("/server/info");return t},3e4),o=s(async()=>{const{data:t}=await a.get("/docker_stats");return t},8e3),u=s(async()=>{const{data:t}=await a.get("/request_metrics");return t},1e4),i=s(async()=>{const{data:t}=await a.get("/slow_sql_metrics");return t},1e4),e=s(async()=>{const{data:t}=await a.get("/task/stats");return t},4e3),r=s(async()=>{const{data:t}=await a.get("/task/running");return t},2e3);async function g(t={}){return c.run(t)}async function y(t={}){return o.run(t)}async function d(t={}){return u.run(t)}async function k(t={}){return i.run(t)}async function l(t={}){return e.run(t)}async function w(t={}){return r.run(t)}async function _(t){const{data:n}=await a.get("/task/logs",{params:t});return n}async function h(t){const{data:n}=await a.post("/task/logs/clear",{days:t});return e.clear(),r.clear(),n}export{w as a,g as b,y as c,d,k as e,l as f,_ as g,h}; diff --git a/static/admin/assets/users-BowyvQzr.js b/static/admin/assets/users-te9ySk34.js similarity index 90% rename from static/admin/assets/users-BowyvQzr.js rename to static/admin/assets/users-te9ySk34.js index e540ea0..35d9d92 100644 --- a/static/admin/assets/users-BowyvQzr.js +++ b/static/admin/assets/users-te9ySk34.js @@ -1 +1 @@ -import{a as t}from"./index-BNslg8wp.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-C1f9ticl.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 b1fca9e..8727cf6 100644 --- a/static/admin/index.html +++ b/static/admin/index.html @@ -5,13 +5,13 @@ 后台管理 - 知识管理平台 - + - +