feat(admin): compact mobile cards for report center
This commit is contained in:
@@ -63,6 +63,10 @@ function parsePercent(value) {
|
||||
return n
|
||||
}
|
||||
|
||||
function percentText(value) {
|
||||
return `${Math.round(parsePercent(value))}%`
|
||||
}
|
||||
|
||||
function sourceLabel(source) {
|
||||
const raw = String(source ?? '').trim()
|
||||
if (!raw) return '手动'
|
||||
@@ -227,6 +231,119 @@ const runningCountsLabel = computed(() => {
|
||||
return `运行中 ${runningCount} / 排队 ${queuingCount} / 并发上限 ${maxGlobal || maxConcurrentGlobal.value || '-'}`
|
||||
})
|
||||
|
||||
const taskModuleItems = computed(() => [
|
||||
{ label: '今日总任务', value: normalizeCount(taskToday.value.total_tasks) },
|
||||
{ label: '今日成功', value: normalizeCount(taskToday.value.success_tasks) },
|
||||
{ label: '今日失败', value: normalizeCount(taskToday.value.failed_tasks) },
|
||||
{ label: '今日成功率', value: `${taskTodaySuccessRate.value}%` },
|
||||
{ label: '累计任务', value: normalizeCount(taskTotal.value.total_tasks) },
|
||||
{ label: '累计成功', value: normalizeCount(taskTotal.value.success_tasks) },
|
||||
])
|
||||
|
||||
const queueModuleItems = computed(() => [
|
||||
{ label: '运行中', value: runningCount.value },
|
||||
{ label: '排队中', value: queuingCount.value },
|
||||
{ label: '并发上限', value: normalizeCount(runningTasks.value?.max_concurrent) || maxConcurrentGlobal.value || '-' },
|
||||
{ label: '排队首条来源', value: sourceLabel(queuingTaskList.value[0]?.source) },
|
||||
{ label: '排队首条状态', value: queuingTaskList.value[0]?.detail_status || queuingTaskList.value[0]?.status || '-' },
|
||||
{ label: '最长等待', value: queuingTaskList.value[0]?.elapsed_display || '-' },
|
||||
])
|
||||
|
||||
const emailModuleItems = computed(() => [
|
||||
{ label: '总发送', value: normalizeCount(emailStats.value?.total_sent) },
|
||||
{ label: '成功', value: normalizeCount(emailStats.value?.total_success) },
|
||||
{ label: '失败', value: normalizeCount(emailStats.value?.total_failed) },
|
||||
{ label: '成功率', value: `${emailSuccessRate.value}%` },
|
||||
{ label: '注册验证', value: normalizeCount(emailStats.value?.register_sent) },
|
||||
{ label: '重置密码', value: normalizeCount(emailStats.value?.reset_sent) },
|
||||
])
|
||||
|
||||
const feedbackModuleItems = computed(() => [
|
||||
{ label: '总反馈', value: normalizeCount(feedbackStats.value?.total) },
|
||||
{ label: '待处理', value: normalizeCount(feedbackStats.value?.pending) },
|
||||
{ label: '已回复', value: normalizeCount(feedbackStats.value?.replied) },
|
||||
])
|
||||
|
||||
const resourceModuleItems = computed(() => [
|
||||
{ label: 'CPU', value: percentText(serverInfo.value?.cpu_percent) },
|
||||
{ label: '内存', value: percentText(serverInfo.value?.memory_percent) },
|
||||
{ label: '磁盘', value: percentText(serverInfo.value?.disk_percent) },
|
||||
{ label: '容器状态', value: dockerStats.value?.status || '-' },
|
||||
{ label: '容器名', value: dockerStats.value?.container_name || '-' },
|
||||
{ label: '容器运行', value: dockerStats.value?.uptime || '-' },
|
||||
])
|
||||
|
||||
const workerModuleItems = computed(() => [
|
||||
{ label: '总 Worker', value: browserPoolTotalWorkers.value },
|
||||
{ label: '活跃 Worker', value: browserPoolActiveWorkers.value },
|
||||
{ label: '忙碌 Worker', value: browserPoolBusyWorkers.value },
|
||||
{ label: '空闲 Worker', value: browserPoolIdleWorkers.value },
|
||||
{ label: '任务队列', value: browserPoolQueueSize.value },
|
||||
])
|
||||
|
||||
const configModuleItems = computed(() => [
|
||||
{ label: '定时任务', value: scheduleEnabled.value ? '启用' : '关闭' },
|
||||
{ label: '执行时间', value: scheduleTime.value || '-' },
|
||||
{ label: '浏览类型', value: scheduleBrowseType.value || '-' },
|
||||
{ label: '代理', value: proxyEnabled.value ? '启用' : '关闭' },
|
||||
{ label: '代理有效期', value: proxyExpireMinutes.value ? `${proxyExpireMinutes.value} 分钟` : '-' },
|
||||
{ label: '全局并发', value: maxConcurrentGlobal.value || '-' },
|
||||
{ label: '单账号并发', value: maxConcurrentPerAccount.value || '-' },
|
||||
{ label: '截图并发', value: maxScreenshotConcurrent.value || '-' },
|
||||
])
|
||||
|
||||
const mobileModules = computed(() => [
|
||||
{
|
||||
key: 'task',
|
||||
title: '任务概览',
|
||||
desc: normalizeCount(taskToday.value.total_tasks) > 0 ? `今日成功率 ${taskTodaySuccessRate.value}%` : '今日暂无任务',
|
||||
tone: 'purple',
|
||||
items: taskModuleItems.value,
|
||||
},
|
||||
{
|
||||
key: 'queue',
|
||||
title: '队列监控',
|
||||
desc: runningCountsLabel.value,
|
||||
tone: 'blue',
|
||||
items: queueModuleItems.value,
|
||||
},
|
||||
{
|
||||
key: 'email',
|
||||
title: '邮件报表',
|
||||
desc: `成功率 ${emailSuccessRate.value}%`,
|
||||
tone: 'cyan',
|
||||
items: emailModuleItems.value,
|
||||
},
|
||||
{
|
||||
key: 'feedback',
|
||||
title: '反馈概览',
|
||||
desc: `待处理 ${normalizeCount(feedbackStats.value?.pending)} 条`,
|
||||
tone: 'orange',
|
||||
items: feedbackModuleItems.value,
|
||||
},
|
||||
{
|
||||
key: 'resource',
|
||||
title: '系统资源',
|
||||
desc: serverInfo.value?.uptime ? `运行 ${serverInfo.value.uptime}` : '运行状态获取中',
|
||||
tone: 'green',
|
||||
items: resourceModuleItems.value,
|
||||
},
|
||||
{
|
||||
key: 'worker',
|
||||
title: '截图线程池',
|
||||
desc: `活跃 ${browserPoolActiveWorkers.value} · 忙碌 ${browserPoolBusyWorkers.value}`,
|
||||
tone: 'cyan',
|
||||
items: workerModuleItems.value,
|
||||
},
|
||||
{
|
||||
key: 'config',
|
||||
title: '配置概览',
|
||||
desc: '并发 / 代理 / 定时任务',
|
||||
tone: 'red',
|
||||
items: configModuleItems.value,
|
||||
},
|
||||
])
|
||||
|
||||
async function refreshAll(options = {}) {
|
||||
const showLoading = options.showLoading ?? true
|
||||
if (refreshing.value) return
|
||||
@@ -307,6 +424,29 @@ onUnmounted(() => {
|
||||
<MetricGrid :items="overviewCards" :loading="loading" :min-width="165" />
|
||||
</section>
|
||||
|
||||
<section class="mobile-report">
|
||||
<el-card
|
||||
v-for="module in mobileModules"
|
||||
:key="module.key"
|
||||
shadow="never"
|
||||
class="mobile-module-card"
|
||||
:class="`mobile-tone-${module.tone}`"
|
||||
:body-style="{ padding: '12px' }"
|
||||
>
|
||||
<div class="mobile-module-head">
|
||||
<div class="mobile-module-title">{{ module.title }}</div>
|
||||
<div class="mobile-module-desc app-muted">{{ module.desc }}</div>
|
||||
</div>
|
||||
<div class="mobile-metrics">
|
||||
<div v-for="item in module.items" :key="`${module.key}-${item.label}`" class="mobile-metric-item">
|
||||
<div class="mobile-metric-label app-muted">{{ item.label }}</div>
|
||||
<div class="mobile-metric-value">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</section>
|
||||
|
||||
<div class="desktop-report">
|
||||
<el-row :gutter="12">
|
||||
<el-col :xs="24" :lg="12">
|
||||
<el-card shadow="never" class="panel" :body-style="{ padding: '16px' }">
|
||||
@@ -636,6 +776,7 @@ onUnmounted(() => {
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -695,6 +836,102 @@ onUnmounted(() => {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.mobile-report {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-module-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(17, 24, 39, 0.12);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: var(--app-shadow-soft);
|
||||
}
|
||||
|
||||
.mobile-module-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: var(--mobile-accent, #3b82f6);
|
||||
}
|
||||
|
||||
.mobile-tone-blue {
|
||||
--mobile-accent: linear-gradient(90deg, #3b82f6, #06b6d4);
|
||||
}
|
||||
|
||||
.mobile-tone-cyan {
|
||||
--mobile-accent: linear-gradient(90deg, #06b6d4, #3b82f6);
|
||||
}
|
||||
|
||||
.mobile-tone-purple {
|
||||
--mobile-accent: linear-gradient(90deg, #8b5cf6, #ec4899);
|
||||
}
|
||||
|
||||
.mobile-tone-orange {
|
||||
--mobile-accent: linear-gradient(90deg, #f59e0b, #f97316);
|
||||
}
|
||||
|
||||
.mobile-tone-green {
|
||||
--mobile-accent: linear-gradient(90deg, #10b981, #22c55e);
|
||||
}
|
||||
|
||||
.mobile-tone-red {
|
||||
--mobile-accent: linear-gradient(90deg, #ef4444, #f43f5e);
|
||||
}
|
||||
|
||||
.mobile-module-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.mobile-module-title {
|
||||
font-size: 13px;
|
||||
font-weight: 900;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.mobile-module-desc {
|
||||
min-width: 0;
|
||||
max-width: 68%;
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.mobile-metrics {
|
||||
margin-top: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mobile-metric-item {
|
||||
padding: 8px 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(17, 24, 39, 0.08);
|
||||
background: rgba(248, 250, 252, 0.9);
|
||||
}
|
||||
|
||||
.mobile-metric-label {
|
||||
font-size: 11px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.mobile-metric-value {
|
||||
margin-top: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
line-height: 1.3;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-radius: 18px;
|
||||
border: 1px solid rgba(17, 24, 39, 0.1);
|
||||
@@ -895,8 +1132,42 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.desktop-report {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-report {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.report-hero {
|
||||
border-radius: 14px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.hero-main h2 {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.hero-meta {
|
||||
margin-top: 4px;
|
||||
gap: 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.resource-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.mobile-metrics {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mobile-module-desc {
|
||||
max-width: 62%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"_MetricGrid-DZUrF_Un.js": {
|
||||
"file": "assets/MetricGrid-DZUrF_Un.js",
|
||||
"_MetricGrid-CO_q83yh.js": {
|
||||
"file": "assets/MetricGrid-CO_q83yh.js",
|
||||
"name": "MetricGrid",
|
||||
"imports": [
|
||||
"index.html"
|
||||
@@ -13,36 +13,36 @@
|
||||
"file": "assets/MetricGrid-yP_dkP6X.css",
|
||||
"src": "_MetricGrid-yP_dkP6X.css"
|
||||
},
|
||||
"_email-CvVAilYN.js": {
|
||||
"file": "assets/email-CvVAilYN.js",
|
||||
"_email-DXFDTcFR.js": {
|
||||
"file": "assets/email-DXFDTcFR.js",
|
||||
"name": "email",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_system-VhyLCmjS.js": {
|
||||
"file": "assets/system-VhyLCmjS.js",
|
||||
"_system-DoHHS63d.js": {
|
||||
"file": "assets/system-DoHHS63d.js",
|
||||
"name": "system",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_tasks-D7g7NShX.js": {
|
||||
"file": "assets/tasks-D7g7NShX.js",
|
||||
"_tasks-DZN3Iufx.js": {
|
||||
"file": "assets/tasks-DZN3Iufx.js",
|
||||
"name": "tasks",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_users-BEod_y0N.js": {
|
||||
"file": "assets/users-BEod_y0N.js",
|
||||
"_users-COdHnepZ.js": {
|
||||
"file": "assets/users-COdHnepZ.js",
|
||||
"name": "users",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"index.html": {
|
||||
"file": "assets/index-BTIe-LzI.js",
|
||||
"file": "assets/index-CpTjoI5H.js",
|
||||
"name": "index",
|
||||
"src": "index.html",
|
||||
"isEntry": true,
|
||||
@@ -62,7 +62,7 @@
|
||||
]
|
||||
},
|
||||
"src/pages/AnnouncementsPage.vue": {
|
||||
"file": "assets/AnnouncementsPage-BI2YHYxD.js",
|
||||
"file": "assets/AnnouncementsPage-S4Vp-C2C.js",
|
||||
"name": "AnnouncementsPage",
|
||||
"src": "src/pages/AnnouncementsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
@@ -74,40 +74,40 @@
|
||||
]
|
||||
},
|
||||
"src/pages/EmailPage.vue": {
|
||||
"file": "assets/EmailPage-BbHeWCVl.js",
|
||||
"file": "assets/EmailPage-QNoD0vM8.js",
|
||||
"name": "EmailPage",
|
||||
"src": "src/pages/EmailPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_email-CvVAilYN.js",
|
||||
"_email-DXFDTcFR.js",
|
||||
"index.html",
|
||||
"_MetricGrid-DZUrF_Un.js"
|
||||
"_MetricGrid-CO_q83yh.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/EmailPage-BmPCDPYC.css"
|
||||
]
|
||||
},
|
||||
"src/pages/FeedbacksPage.vue": {
|
||||
"file": "assets/FeedbacksPage-DHEEAjuM.js",
|
||||
"file": "assets/FeedbacksPage-c1mEklN7.js",
|
||||
"name": "FeedbacksPage",
|
||||
"src": "src/pages/FeedbacksPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_MetricGrid-DZUrF_Un.js"
|
||||
"_MetricGrid-CO_q83yh.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/FeedbacksPage-mrXjCiV2.css"
|
||||
]
|
||||
},
|
||||
"src/pages/LogsPage.vue": {
|
||||
"file": "assets/LogsPage-B8VOeIJO.js",
|
||||
"file": "assets/LogsPage-D81ZpiBx.js",
|
||||
"name": "LogsPage",
|
||||
"src": "src/pages/LogsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_users-BEod_y0N.js",
|
||||
"_tasks-D7g7NShX.js",
|
||||
"_users-COdHnepZ.js",
|
||||
"_tasks-DZN3Iufx.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
@@ -115,36 +115,36 @@
|
||||
]
|
||||
},
|
||||
"src/pages/ReportPage.vue": {
|
||||
"file": "assets/ReportPage-C-_600qN.js",
|
||||
"file": "assets/ReportPage-Cgvv4cbM.js",
|
||||
"name": "ReportPage",
|
||||
"src": "src/pages/ReportPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_email-CvVAilYN.js",
|
||||
"_tasks-D7g7NShX.js",
|
||||
"_system-VhyLCmjS.js",
|
||||
"_MetricGrid-DZUrF_Un.js"
|
||||
"_email-DXFDTcFR.js",
|
||||
"_tasks-DZN3Iufx.js",
|
||||
"_system-DoHHS63d.js",
|
||||
"_MetricGrid-CO_q83yh.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/ReportPage-HC4elboH.css"
|
||||
"assets/ReportPage-MS8zJmyZ.css"
|
||||
]
|
||||
},
|
||||
"src/pages/SecurityPage.vue": {
|
||||
"file": "assets/SecurityPage-Ce7j6UO4.js",
|
||||
"file": "assets/SecurityPage-BLF3zGyy.js",
|
||||
"name": "SecurityPage",
|
||||
"src": "src/pages/SecurityPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_MetricGrid-DZUrF_Un.js"
|
||||
"_MetricGrid-CO_q83yh.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/SecurityPage-DN76ndc_.css"
|
||||
]
|
||||
},
|
||||
"src/pages/SettingsPage.vue": {
|
||||
"file": "assets/SettingsPage-nj1AULlf.js",
|
||||
"file": "assets/SettingsPage-kou4GC5e.js",
|
||||
"name": "SettingsPage",
|
||||
"src": "src/pages/SettingsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
@@ -156,12 +156,12 @@
|
||||
]
|
||||
},
|
||||
"src/pages/SystemPage.vue": {
|
||||
"file": "assets/SystemPage-i6_qDLAX.js",
|
||||
"file": "assets/SystemPage-DZnURk26.js",
|
||||
"name": "SystemPage",
|
||||
"src": "src/pages/SystemPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_system-VhyLCmjS.js",
|
||||
"_system-DoHHS63d.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
@@ -169,12 +169,12 @@
|
||||
]
|
||||
},
|
||||
"src/pages/UsersPage.vue": {
|
||||
"file": "assets/UsersPage-BbP8F1bH.js",
|
||||
"file": "assets/UsersPage-7SCtUJvO.js",
|
||||
"name": "UsersPage",
|
||||
"src": "src/pages/UsersPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_users-BEod_y0N.js",
|
||||
"_users-COdHnepZ.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/assets/EmailPage-QNoD0vM8.js
Normal file
1
static/admin/assets/EmailPage-QNoD0vM8.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/assets/FeedbacksPage-c1mEklN7.js
Normal file
1
static/admin/assets/FeedbacksPage-c1mEklN7.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/assets/LogsPage-D81ZpiBx.js
Normal file
1
static/admin/assets/LogsPage-D81ZpiBx.js
Normal file
File diff suppressed because one or more lines are too long
1
static/admin/assets/MetricGrid-CO_q83yh.js
Normal file
1
static/admin/assets/MetricGrid-CO_q83yh.js
Normal file
@@ -0,0 +1 @@
|
||||
import{_ as m,f as c,g as s,h as t,F as l,q as u,D as p,j as o,n as r,m as y,w as h,B as i,T as v,p as n,x as k,U as f}from"./index-CpTjoI5H.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,w)=>{const d=c("el-icon"),_=c("el-skeleton");return t(),s("div",{class:"metric-grid",style:f({"--metric-min":`${a.minWidth}px`})},[(t(!0),s(l,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)])):r("",!0),o("div",g,n(e?.label||"-"),1)]),o("div",B,[a.loading?(t(),i(_,{key:0,rows:1,animated:""})):(t(),s(l,{key:1},[k(n(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",C,n(e?.hint||e?.sub),1)):r("",!0)],2))),128))],4)}}},M=m(N,[["__scopeId","data-v-00e217d4"]]);export{M};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as m,f as c,g as s,h as t,F as l,K as u,C as p,j as o,n as r,m as y,w as h,A as i,T as v,p as n,v as k,U as f}from"./index-BTIe-LzI.js";const b={class:"metric-top"},g={key:0,class:"metric-icon"},x={class:"metric-label"},C={class:"metric-value"},B={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,w)=>{const d=c("el-icon"),_=c("el-skeleton");return t(),s("div",{class:"metric-grid",style:f({"--metric-min":`${a.minWidth}px`})},[(t(!0),s(l,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",g,[y(d,null,{default:h(()=>[(t(),i(v(e.icon)))]),_:2},1024)])):r("",!0),o("div",x,n(e?.label||"-"),1)]),o("div",C,[a.loading?(t(),i(_,{key:0,rows:1,animated:""})):(t(),s(l,{key:1},[k(n(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",B,n(e?.hint||e?.sub),1)):r("",!0)],2))),128))],4)}}},z=m(N,[["__scopeId","data-v-00e217d4"]]);export{z as M};
|
||||
File diff suppressed because one or more lines are too long
1
static/admin/assets/ReportPage-Cgvv4cbM.js
Normal file
1
static/admin/assets/ReportPage-Cgvv4cbM.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.page-stack[data-v-e84bdbad]{display:flex;flex-direction:column;gap:14px}.report-hero[data-v-e84bdbad]{position:relative;overflow:hidden;border-radius:18px;border:1px solid rgba(17,24,39,.1);background:radial-gradient(circle at 10% 10%,rgba(59,130,246,.18),transparent 48%),radial-gradient(circle at 80% 0%,rgba(236,72,153,.16),transparent 45%),radial-gradient(circle at 90% 90%,rgba(16,185,129,.14),transparent 42%),#ffffffb8;box-shadow:0 14px 40px #0f172a14;-webkit-backdrop-filter:saturate(180%) blur(12px);backdrop-filter:saturate(180%) blur(12px);padding:16px}.hero-head[data-v-e84bdbad]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:14px}.hero-main h2[data-v-e84bdbad]{margin:0;font-size:19px;font-weight:900;letter-spacing:.2px}.hero-meta[data-v-e84bdbad]{margin-top:6px;font-size:12px;display:flex;align-items:center;flex-wrap:wrap;gap:8px}.hero-dot[data-v-e84bdbad]{opacity:.65}.hero-actions[data-v-e84bdbad]{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.panel[data-v-e84bdbad]{border-radius:18px;border:1px solid rgba(17,24,39,.1);background:#ffffffb8;box-shadow:var(--app-shadow);-webkit-backdrop-filter:saturate(180%) blur(10px);backdrop-filter:saturate(180%) blur(10px)}.panel-head[data-v-e84bdbad]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:14px}.head-left[data-v-e84bdbad]{display:flex;align-items:center;gap:12px;min-width:0}.head-text[data-v-e84bdbad]{min-width:0}.head-icon[data-v-e84bdbad]{width:40px;height:40px;border-radius:16px;display:flex;align-items:center;justify-content:center;border:1px solid rgba(17,24,39,.08);flex:0 0 auto}.tone-blue[data-v-e84bdbad]{background:#3b82f61f;color:#1d4ed8}.tone-cyan[data-v-e84bdbad]{background:#22d3ee1f;color:#0369a1}.tone-purple[data-v-e84bdbad]{background:#8b5cf61f;color:#6d28d9}.tone-orange[data-v-e84bdbad]{background:#f59e0b1f;color:#b45309}.tone-green[data-v-e84bdbad]{background:#10b9811f;color:#047857}.tone-red[data-v-e84bdbad]{background:#ef44441f;color:#b91c1c}.panel-title[data-v-e84bdbad]{font-size:14px;font-weight:900}.panel-sub[data-v-e84bdbad]{margin-top:4px;font-size:12px;color:var(--app-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.metrics-block[data-v-e84bdbad]{display:flex;flex-direction:column;gap:10px}.block-title[data-v-e84bdbad]{font-size:13px;font-weight:900;letter-spacing:.2px}.divider[data-v-e84bdbad]{height:1px;background:linear-gradient(90deg,transparent,rgba(17,24,39,.12),transparent);margin:14px 0}.queue-tabs[data-v-e84bdbad] .el-tabs__header{margin:0 0 10px}.tab-label[data-v-e84bdbad]{display:inline-flex;align-items:center;gap:6px}.table-wrap[data-v-e84bdbad]{overflow-x:auto;border-radius:10px;border:1px solid var(--app-border);background:#fff}.help[data-v-e84bdbad]{margin-top:10px;font-size:12px}.resource-grid[data-v-e84bdbad]{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.resource-item[data-v-e84bdbad]{border:1px solid rgba(17,24,39,.08);border-radius:16px;padding:12px;background:#ffffffb3}.resource-k[data-v-e84bdbad]{font-size:12px;margin-bottom:8px}.resource-sub[data-v-e84bdbad]{margin-top:8px;font-size:12px}.config-grid[data-v-e84bdbad]{display:grid;grid-template-columns:1fr;gap:10px}.config-item[data-v-e84bdbad]{border:1px solid rgba(17,24,39,.08);border-radius:16px;padding:12px;background:#ffffffb3}.config-k[data-v-e84bdbad]{font-size:12px}.config-v[data-v-e84bdbad]{margin-top:8px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}.config-inline[data-v-e84bdbad]{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.config-split[data-v-e84bdbad]{opacity:.65}.config-sub[data-v-e84bdbad]{margin-top:8px;font-size:12px}.err[data-v-e84bdbad]{color:#b91c1c}[data-v-e84bdbad] .el-table{--el-table-border-color: rgba(17, 24, 39, .08);--el-table-header-bg-color: rgba(246, 247, 251, .8)}[data-v-e84bdbad] .el-table th.el-table__cell{background:#f6f7fbcc}@media(max-width:768px){.resource-grid[data-v-e84bdbad]{grid-template-columns:1fr}}
|
||||
1
static/admin/assets/ReportPage-MS8zJmyZ.css
Normal file
1
static/admin/assets/ReportPage-MS8zJmyZ.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/assets/SettingsPage-kou4GC5e.js
Normal file
1
static/admin/assets/SettingsPage-kou4GC5e.js
Normal file
@@ -0,0 +1 @@
|
||||
import{a as m,_ as B,r as p,f as u,g as T,h as P,j as r,m as a,w as l,x,K as i,J as b}from"./index-CpTjoI5H.js";async function C(o){const{data:s}=await m.put("/admin/username",{new_username:o});return s}async function S(o){const{data:s}=await m.put("/admin/password",{new_password:o});return s}async function U(){const{data:o}=await m.post("/logout");return o}const A={class:"page-stack"},E={__name:"SettingsPage",setup(o){const s=p(""),d=p(""),n=p(!1);function k(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:e.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function f(){try{await U()}catch{}finally{window.location.href="/yuyx"}}async function V(){const t=s.value.trim();if(!t){i.error("请输入新用户名");return}try{await b.confirm(`确定将管理员用户名修改为「${t}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await C(t),i.success("用户名修改成功,请重新登录"),s.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function h(){const t=d.value;if(!t){i.error("请输入新密码");return}const e=k(t);if(!e.ok){i.error(e.message);return}try{await b.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await S(t),i.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(t,e)=>{const g=u("el-input"),w=u("el-form-item"),v=u("el-form"),y=u("el-button"),_=u("el-card");return P(),T("div",A,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(v,{"label-width":"120px"},{default:l(()=>[a(w,{label:"新用户名"},{default:l(()=>[a(g,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=c=>s.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:V},{default:l(()=>[...e[2]||(e[2]=[x("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(v,{"label-width":"120px"},{default:l(()=>[a(w,{label:"新密码"},{default:l(()=>[a(g,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:h},{default:l(()=>[...e[4]||(e[4]=[x("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1})])}}},M=B(E,[["__scopeId","data-v-83d3840a"]]);export{M as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{a as m,_ as B,r as p,f as u,g as T,h as P,j as r,m as a,w as l,v as x,J as i,I as b}from"./index-BTIe-LzI.js";async function C(o){const{data:s}=await m.put("/admin/username",{new_username:o});return s}async function S(o){const{data:s}=await m.put("/admin/password",{new_password:o});return s}async function U(){const{data:o}=await m.post("/logout");return o}const A={class:"page-stack"},E={__name:"SettingsPage",setup(o){const s=p(""),d=p(""),n=p(!1);function k(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:e.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function f(){try{await U()}catch{}finally{window.location.href="/yuyx"}}async function V(){const t=s.value.trim();if(!t){i.error("请输入新用户名");return}try{await b.confirm(`确定将管理员用户名修改为「${t}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await C(t),i.success("用户名修改成功,请重新登录"),s.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function h(){const t=d.value;if(!t){i.error("请输入新密码");return}const e=k(t);if(!e.ok){i.error(e.message);return}try{await b.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await S(t),i.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(t,e)=>{const g=u("el-input"),v=u("el-form-item"),w=u("el-form"),y=u("el-button"),_=u("el-card");return P(),T("div",A,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(w,{"label-width":"120px"},{default:l(()=>[a(v,{label:"新用户名"},{default:l(()=>[a(g,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=c=>s.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:V},{default:l(()=>[...e[2]||(e[2]=[x("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(w,{"label-width":"120px"},{default:l(()=>[a(v,{label:"新密码"},{default:l(()=>[a(g,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:h},{default:l(()=>[...e[4]||(e[4]=[x("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1})])}}},I=B(E,[["__scopeId","data-v-83d3840a"]]);export{I as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
import{a as n}from"./index-BTIe-LzI.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};
|
||||
import{a as n}from"./index-CpTjoI5H.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
import{a}from"./index-BTIe-LzI.js";async function o(){const{data:t}=await a.get("/system/config");return t}async function e(t){const{data:n}=await a.post("/system/config",t);return n}export{o as f,e as u};
|
||||
import{a}from"./index-CpTjoI5H.js";async function o(){const{data:t}=await a.get("/system/config");return t}async function e(t){const{data:n}=await a.post("/system/config",t);return n}export{o as f,e as u};
|
||||
@@ -1 +1 @@
|
||||
import{a}from"./index-BTIe-LzI.js";async function c(){const{data:t}=await a.get("/server/info");return t}async function e(){const{data:t}=await a.get("/docker_stats");return t}async function o(){const{data:t}=await a.get("/task/stats");return t}async function r(){const{data:t}=await a.get("/task/running");return t}async function i(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function f(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{r as a,c as b,e as c,i as d,f as e,o as f};
|
||||
import{a}from"./index-CpTjoI5H.js";async function c(){const{data:t}=await a.get("/server/info");return t}async function e(){const{data:t}=await a.get("/docker_stats");return t}async function o(){const{data:t}=await a.get("/task/stats");return t}async function r(){const{data:t}=await a.get("/task/running");return t}async function i(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function f(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{r as a,c as b,e as c,i as d,f as e,o as f};
|
||||
@@ -1 +1 @@
|
||||
import{a as t}from"./index-BTIe-LzI.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-CpTjoI5H.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};
|
||||
@@ -5,7 +5,7 @@
|
||||
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>后台管理 - 知识管理平台</title>
|
||||
<script type="module" crossorigin src="./assets/index-BTIe-LzI.js"></script>
|
||||
<script type="module" crossorigin src="./assets/index-CpTjoI5H.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-DRsk2q1y.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user