fix: CPU显示修复 + 报表面板添加浏览器池状态
1. CPU 显示修复: - routes/admin_api/core.py: 新增 _get_server_cpu_percent() - 首次调用使用 interval=0.1 避免返回 0.0 - 后续调用使用缓存,TTL 1秒 2. 报表面板浏览器池状态: - admin-frontend/src/api/browser_pool.js: 新增 API 调用 - ReportPage.vue: 添加浏览器池状态卡片 - 显示总/活跃/空闲 Worker 数和队列等待数 - Worker 表格带状态颜色标签(活跃/空闲/异常) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
7
admin-frontend/src/api/browser_pool.js
Normal file
7
admin-frontend/src/api/browser_pool.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { api } from './client'
|
||||||
|
|
||||||
|
export async function fetchBrowserPoolStats() {
|
||||||
|
const { data } = await api.get('/browser_pool/stats')
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
import { fetchFeedbackStats } from '../api/feedbacks'
|
import { fetchFeedbackStats } from '../api/feedbacks'
|
||||||
import { fetchEmailStats } from '../api/email'
|
import { fetchEmailStats } from '../api/email'
|
||||||
import { fetchDockerStats, fetchRunningTasks, fetchServerInfo, fetchTaskStats } from '../api/tasks'
|
import { fetchDockerStats, fetchRunningTasks, fetchServerInfo, fetchTaskStats } from '../api/tasks'
|
||||||
|
import { fetchBrowserPoolStats } from '../api/browser_pool'
|
||||||
import { fetchSystemConfig } from '../api/system'
|
import { fetchSystemConfig } from '../api/system'
|
||||||
|
|
||||||
const refreshStats = inject('refreshStats', null)
|
const refreshStats = inject('refreshStats', null)
|
||||||
@@ -32,6 +33,7 @@ const emailStats = ref(null)
|
|||||||
const feedbackStats = ref(null)
|
const feedbackStats = ref(null)
|
||||||
const serverInfo = ref(null)
|
const serverInfo = ref(null)
|
||||||
const dockerStats = ref(null)
|
const dockerStats = ref(null)
|
||||||
|
const browserPoolStats = ref(null)
|
||||||
const systemConfig = ref(null)
|
const systemConfig = ref(null)
|
||||||
const queueTab = ref('running')
|
const queueTab = ref('running')
|
||||||
|
|
||||||
@@ -97,6 +99,40 @@ const queuingTaskList = computed(() => runningTasks.value?.queuing || [])
|
|||||||
const runningCount = computed(() => normalizeCount(runningTasks.value?.running_count))
|
const runningCount = computed(() => normalizeCount(runningTasks.value?.running_count))
|
||||||
const queuingCount = computed(() => normalizeCount(runningTasks.value?.queuing_count))
|
const queuingCount = computed(() => normalizeCount(runningTasks.value?.queuing_count))
|
||||||
|
|
||||||
|
const browserPoolWorkers = computed(() => {
|
||||||
|
const workers = browserPoolStats.value?.workers
|
||||||
|
if (!Array.isArray(workers)) return []
|
||||||
|
return [...workers].sort((a, b) => normalizeCount(a?.worker_id) - normalizeCount(b?.worker_id))
|
||||||
|
})
|
||||||
|
|
||||||
|
const browserPoolTotalWorkers = computed(() => normalizeCount(browserPoolStats.value?.total_workers))
|
||||||
|
const browserPoolActiveWorkers = computed(() => browserPoolWorkers.value.filter((w) => Boolean(w?.has_browser)).length)
|
||||||
|
const browserPoolIdleWorkers = computed(() => normalizeCount(browserPoolStats.value?.idle_workers))
|
||||||
|
const browserPoolQueueSize = computed(() => normalizeCount(browserPoolStats.value?.queue_size))
|
||||||
|
const browserPoolBusyWorkers = computed(() => normalizeCount(browserPoolStats.value?.active_workers))
|
||||||
|
|
||||||
|
function workerPoolStatusType(worker) {
|
||||||
|
if (!worker?.thread_alive) return 'danger'
|
||||||
|
if (worker?.has_browser) return 'success'
|
||||||
|
return 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
function workerPoolStatusLabel(worker) {
|
||||||
|
if (!worker?.thread_alive) return '异常'
|
||||||
|
if (worker?.has_browser) return '活跃'
|
||||||
|
return '空闲'
|
||||||
|
}
|
||||||
|
|
||||||
|
function workerRunTagType(worker) {
|
||||||
|
if (!worker?.thread_alive) return 'danger'
|
||||||
|
return worker?.idle ? 'info' : 'warning'
|
||||||
|
}
|
||||||
|
|
||||||
|
function workerRunLabel(worker) {
|
||||||
|
if (!worker?.thread_alive) return '停止'
|
||||||
|
return worker?.idle ? '空闲' : '忙碌'
|
||||||
|
}
|
||||||
|
|
||||||
const taskTodaySuccessRate = computed(() => {
|
const taskTodaySuccessRate = computed(() => {
|
||||||
const success = normalizeCount(taskToday.value.success_tasks)
|
const success = normalizeCount(taskToday.value.success_tasks)
|
||||||
const failed = normalizeCount(taskToday.value.failed_tasks)
|
const failed = normalizeCount(taskToday.value.failed_tasks)
|
||||||
@@ -156,6 +192,7 @@ async function refreshAll() {
|
|||||||
feedbackResult,
|
feedbackResult,
|
||||||
serverResult,
|
serverResult,
|
||||||
dockerResult,
|
dockerResult,
|
||||||
|
browserPoolResult,
|
||||||
configResult,
|
configResult,
|
||||||
] = await Promise.allSettled([
|
] = await Promise.allSettled([
|
||||||
fetchTaskStats(),
|
fetchTaskStats(),
|
||||||
@@ -164,6 +201,7 @@ async function refreshAll() {
|
|||||||
fetchFeedbackStats(),
|
fetchFeedbackStats(),
|
||||||
fetchServerInfo(),
|
fetchServerInfo(),
|
||||||
fetchDockerStats(),
|
fetchDockerStats(),
|
||||||
|
fetchBrowserPoolStats(),
|
||||||
fetchSystemConfig(),
|
fetchSystemConfig(),
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -173,6 +211,7 @@ async function refreshAll() {
|
|||||||
feedbackStats.value = feedbackResult.status === 'fulfilled' ? feedbackResult.value : null
|
feedbackStats.value = feedbackResult.status === 'fulfilled' ? feedbackResult.value : null
|
||||||
serverInfo.value = serverResult.status === 'fulfilled' ? serverResult.value : null
|
serverInfo.value = serverResult.status === 'fulfilled' ? serverResult.value : null
|
||||||
dockerStats.value = dockerResult.status === 'fulfilled' ? dockerResult.value : null
|
dockerStats.value = dockerResult.status === 'fulfilled' ? dockerResult.value : null
|
||||||
|
browserPoolStats.value = browserPoolResult.status === 'fulfilled' ? browserPoolResult.value : null
|
||||||
systemConfig.value = configResult.status === 'fulfilled' ? configResult.value : null
|
systemConfig.value = configResult.status === 'fulfilled' ? configResult.value : null
|
||||||
|
|
||||||
await refreshStats?.()
|
await refreshStats?.()
|
||||||
@@ -548,6 +587,67 @@ onUnmounted(() => {
|
|||||||
<el-descriptions-item label="内存">{{ dockerStats?.memory_usage || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="内存">{{ dockerStats?.memory_usage || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="内存占比">{{ dockerStats?.memory_percent || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="内存占比">{{ dockerStats?.memory_percent || '-' }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<div class="panel-head">
|
||||||
|
<div class="head-left">
|
||||||
|
<div class="head-text">
|
||||||
|
<div class="panel-title">浏览器池</div>
|
||||||
|
<div class="panel-sub app-muted">
|
||||||
|
活跃(有浏览器){{ browserPoolActiveWorkers }} · 忙碌 {{ browserPoolBusyWorkers }} · 队列 {{ browserPoolQueueSize }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-tag v-if="browserPoolStats?.server_time_cst" effect="light" type="info">{{ browserPoolStats.server_time_cst }}</el-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tile-grid tile-grid--4">
|
||||||
|
<div class="tile">
|
||||||
|
<div class="tile-v">{{ browserPoolTotalWorkers }}</div>
|
||||||
|
<div class="tile-k app-muted">总 Worker</div>
|
||||||
|
</div>
|
||||||
|
<div class="tile">
|
||||||
|
<div class="tile-v ok">{{ browserPoolActiveWorkers }}</div>
|
||||||
|
<div class="tile-k app-muted">活跃(有浏览器)</div>
|
||||||
|
</div>
|
||||||
|
<div class="tile">
|
||||||
|
<div class="tile-v">{{ browserPoolIdleWorkers }}</div>
|
||||||
|
<div class="tile-k app-muted">空闲(无任务)</div>
|
||||||
|
</div>
|
||||||
|
<div class="tile">
|
||||||
|
<div class="tile-v warn">{{ browserPoolQueueSize }}</div>
|
||||||
|
<div class="tile-k app-muted">队列等待</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<div class="table-wrap">
|
||||||
|
<el-table :data="browserPoolWorkers" size="small" border>
|
||||||
|
<el-table-column prop="worker_id" label="Worker" width="90" />
|
||||||
|
<el-table-column label="状态" width="90">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="workerPoolStatusType(row)" effect="light">{{ workerPoolStatusLabel(row) }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="执行" width="90">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="workerRunTagType(row)" effect="light">{{ workerRunLabel(row) }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="任务" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span>{{ normalizeCount(row?.total_tasks) }}</span>
|
||||||
|
<span class="app-muted"> / </span>
|
||||||
|
<span :class="normalizeCount(row?.failed_tasks) ? 'err' : 'app-muted'">{{ normalizeCount(row?.failed_tasks) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="browser_use_count" label="复用" width="90" />
|
||||||
|
<el-table-column prop="last_active_at" label="最近活跃" min-width="160" />
|
||||||
|
<el-table-column prop="browser_created_at" label="浏览器创建" min-width="160" />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
@@ -901,6 +1001,10 @@ onUnmounted(() => {
|
|||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tile-grid--4 {
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
.tile {
|
.tile {
|
||||||
border: 1px solid rgba(17, 24, 39, 0.08);
|
border: 1px solid rgba(17, 24, 39, 0.08);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
@@ -1072,6 +1176,10 @@ onUnmounted(() => {
|
|||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tile-grid--4 {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
.resource-grid {
|
.resource-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,36 @@ from services.time_utils import BEIJING_TZ, get_beijing_now
|
|||||||
logger = get_logger("app")
|
logger = get_logger("app")
|
||||||
config = get_config()
|
config = get_config()
|
||||||
|
|
||||||
|
_server_cpu_percent_lock = threading.Lock()
|
||||||
|
_server_cpu_percent_last: float | None = None
|
||||||
|
_server_cpu_percent_last_ts = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def _get_server_cpu_percent() -> float:
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
global _server_cpu_percent_last, _server_cpu_percent_last_ts
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
with _server_cpu_percent_lock:
|
||||||
|
if _server_cpu_percent_last is not None and (now - _server_cpu_percent_last_ts) < 0.5:
|
||||||
|
return _server_cpu_percent_last
|
||||||
|
|
||||||
|
try:
|
||||||
|
if _server_cpu_percent_last is None:
|
||||||
|
cpu_percent = float(psutil.cpu_percent(interval=0.1))
|
||||||
|
else:
|
||||||
|
cpu_percent = float(psutil.cpu_percent(interval=None))
|
||||||
|
except Exception:
|
||||||
|
cpu_percent = float(_server_cpu_percent_last or 0.0)
|
||||||
|
|
||||||
|
if cpu_percent < 0:
|
||||||
|
cpu_percent = 0.0
|
||||||
|
|
||||||
|
_server_cpu_percent_last = cpu_percent
|
||||||
|
_server_cpu_percent_last_ts = now
|
||||||
|
return cpu_percent
|
||||||
|
|
||||||
|
|
||||||
def _admin_reauth_required() -> bool:
|
def _admin_reauth_required() -> bool:
|
||||||
try:
|
try:
|
||||||
@@ -738,7 +768,7 @@ def get_server_info_api():
|
|||||||
"""获取服务器信息"""
|
"""获取服务器信息"""
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
cpu_percent = psutil.cpu_percent(interval=1)
|
cpu_percent = _get_server_cpu_percent()
|
||||||
|
|
||||||
memory = psutil.virtual_memory()
|
memory = psutil.virtual_memory()
|
||||||
memory_total = f"{memory.total / (1024**3):.1f}GB"
|
memory_total = f"{memory.total / (1024**3):.1f}GB"
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
{
|
{
|
||||||
"_email-DSz2K4-y.js": {
|
"_email-JmyL2jV4.js": {
|
||||||
"file": "assets/email-DSz2K4-y.js",
|
"file": "assets/email-JmyL2jV4.js",
|
||||||
"name": "email",
|
"name": "email",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_system-C_4M4EtK.js": {
|
"_system-DIc9L1cQ.js": {
|
||||||
"file": "assets/system-C_4M4EtK.js",
|
"file": "assets/system-DIc9L1cQ.js",
|
||||||
"name": "system",
|
"name": "system",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_tasks-yIlAy2Ne.js": {
|
"_tasks-DILta43B.js": {
|
||||||
"file": "assets/tasks-yIlAy2Ne.js",
|
"file": "assets/tasks-DILta43B.js",
|
||||||
"name": "tasks",
|
"name": "tasks",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_users-BNPg4OEj.js": {
|
"_users-BxS2U-Si.js": {
|
||||||
"file": "assets/users-BNPg4OEj.js",
|
"file": "assets/users-BxS2U-Si.js",
|
||||||
"name": "users",
|
"name": "users",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"index.html": {
|
"index.html": {
|
||||||
"file": "assets/index-Dx-1XhY8.js",
|
"file": "assets/index-B9zsNTKQ.js",
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"src": "index.html",
|
"src": "index.html",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/AnnouncementsPage.vue": {
|
"src/pages/AnnouncementsPage.vue": {
|
||||||
"file": "assets/AnnouncementsPage-DP-v4_4f.js",
|
"file": "assets/AnnouncementsPage-C3uZDqOR.js",
|
||||||
"name": "AnnouncementsPage",
|
"name": "AnnouncementsPage",
|
||||||
"src": "src/pages/AnnouncementsPage.vue",
|
"src": "src/pages/AnnouncementsPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
@@ -60,12 +60,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/EmailPage.vue": {
|
"src/pages/EmailPage.vue": {
|
||||||
"file": "assets/EmailPage-4etTfx9H.js",
|
"file": "assets/EmailPage-DmFgLrFm.js",
|
||||||
"name": "EmailPage",
|
"name": "EmailPage",
|
||||||
"src": "src/pages/EmailPage.vue",
|
"src": "src/pages/EmailPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_email-DSz2K4-y.js",
|
"_email-JmyL2jV4.js",
|
||||||
"index.html"
|
"index.html"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/FeedbacksPage.vue": {
|
"src/pages/FeedbacksPage.vue": {
|
||||||
"file": "assets/FeedbacksPage-BlN6FFbD.js",
|
"file": "assets/FeedbacksPage-BEa--2pl.js",
|
||||||
"name": "FeedbacksPage",
|
"name": "FeedbacksPage",
|
||||||
"src": "src/pages/FeedbacksPage.vue",
|
"src": "src/pages/FeedbacksPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
@@ -85,13 +85,13 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/LogsPage.vue": {
|
"src/pages/LogsPage.vue": {
|
||||||
"file": "assets/LogsPage-adLViVmd.js",
|
"file": "assets/LogsPage-CQsGwFRH.js",
|
||||||
"name": "LogsPage",
|
"name": "LogsPage",
|
||||||
"src": "src/pages/LogsPage.vue",
|
"src": "src/pages/LogsPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_users-BNPg4OEj.js",
|
"_users-BxS2U-Si.js",
|
||||||
"_tasks-yIlAy2Ne.js",
|
"_tasks-DILta43B.js",
|
||||||
"index.html"
|
"index.html"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
@@ -99,22 +99,22 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/ReportPage.vue": {
|
"src/pages/ReportPage.vue": {
|
||||||
"file": "assets/ReportPage-DxDL6AXa.js",
|
"file": "assets/ReportPage-nXQwTJlk.js",
|
||||||
"name": "ReportPage",
|
"name": "ReportPage",
|
||||||
"src": "src/pages/ReportPage.vue",
|
"src": "src/pages/ReportPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html",
|
"index.html",
|
||||||
"_email-DSz2K4-y.js",
|
"_email-JmyL2jV4.js",
|
||||||
"_tasks-yIlAy2Ne.js",
|
"_tasks-DILta43B.js",
|
||||||
"_system-C_4M4EtK.js"
|
"_system-DIc9L1cQ.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"assets/ReportPage-CW7RwLmI.css"
|
"assets/ReportPage-D7trjjOv.css"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/SecurityPage.vue": {
|
"src/pages/SecurityPage.vue": {
|
||||||
"file": "assets/SecurityPage-BkxWxQhW.js",
|
"file": "assets/SecurityPage-Czxm2GJx.js",
|
||||||
"name": "SecurityPage",
|
"name": "SecurityPage",
|
||||||
"src": "src/pages/SecurityPage.vue",
|
"src": "src/pages/SecurityPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/SettingsPage.vue": {
|
"src/pages/SettingsPage.vue": {
|
||||||
"file": "assets/SettingsPage-D_cVneyv.js",
|
"file": "assets/SettingsPage-IGV4in6c.js",
|
||||||
"name": "SettingsPage",
|
"name": "SettingsPage",
|
||||||
"src": "src/pages/SettingsPage.vue",
|
"src": "src/pages/SettingsPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
@@ -138,12 +138,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/SystemPage.vue": {
|
"src/pages/SystemPage.vue": {
|
||||||
"file": "assets/SystemPage-Cph4odbt.js",
|
"file": "assets/SystemPage-BPHukDdR.js",
|
||||||
"name": "SystemPage",
|
"name": "SystemPage",
|
||||||
"src": "src/pages/SystemPage.vue",
|
"src": "src/pages/SystemPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_system-C_4M4EtK.js",
|
"_system-DIc9L1cQ.js",
|
||||||
"index.html"
|
"index.html"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
@@ -151,12 +151,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/UsersPage.vue": {
|
"src/pages/UsersPage.vue": {
|
||||||
"file": "assets/UsersPage-19tzoQBx.js",
|
"file": "assets/UsersPage-DLsmihq2.js",
|
||||||
"name": "UsersPage",
|
"name": "UsersPage",
|
||||||
"src": "src/pages/UsersPage.vue",
|
"src": "src/pages/UsersPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_users-BNPg4OEj.js",
|
"_users-BxS2U-Si.js",
|
||||||
"index.html"
|
"index.html"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
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-CQsGwFRH.js
Normal file
1
static/admin/assets/LogsPage-CQsGwFRH.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
File diff suppressed because one or more lines are too long
1
static/admin/assets/ReportPage-D7trjjOv.css
Normal file
1
static/admin/assets/ReportPage-D7trjjOv.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/ReportPage-nXQwTJlk.js
Normal file
1
static/admin/assets/ReportPage-nXQwTJlk.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 +1 @@
|
|||||||
import{P as m,_ as B,r as p,e as u,f as T,g as P,h as r,j as a,w as l,p as x,K as i,J as b}from"./index-Dx-1XhY8.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-12a26d11"]]);export{M as default};
|
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,q as x,L as i,K as b}from"./index-B9zsNTKQ.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-12a26d11"]]);export{M 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
static/admin/assets/UsersPage-DLsmihq2.js
Normal file
1
static/admin/assets/UsersPage-DLsmihq2.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
import{P as n}from"./index-Dx-1XhY8.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-B9zsNTKQ.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 +0,0 @@
|
|||||||
import{P as e}from"./index-Dx-1XhY8.js";async function s(){const{data:t}=await e.get("/system/config");return t}async function c(t){const{data:a}=await e.post("/system/config",t);return a}async function o(){const{data:t}=await e.post("/schedule/execute",{});return t}export{o as e,s as f,c as u};
|
|
||||||
1
static/admin/assets/system-DIc9L1cQ.js
Normal file
1
static/admin/assets/system-DIc9L1cQ.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{a}from"./index-B9zsNTKQ.js";async function s(){const{data:t}=await a.get("/system/config");return t}async function c(t){const{data:e}=await a.post("/system/config",t);return e}async function o(){const{data:t}=await a.post("/schedule/execute",{});return t}export{o as e,s as f,c as u};
|
||||||
1
static/admin/assets/tasks-DILta43B.js
Normal file
1
static/admin/assets/tasks-DILta43B.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{a}from"./index-B9zsNTKQ.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 +0,0 @@
|
|||||||
import{P as a}from"./index-Dx-1XhY8.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{P as t}from"./index-Dx-1XhY8.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-B9zsNTKQ.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" />
|
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>后台管理 - 知识管理平台</title>
|
<title>后台管理 - 知识管理平台</title>
|
||||||
<script type="module" crossorigin src="./assets/index-Dx-1XhY8.js"></script>
|
<script type="module" crossorigin src="./assets/index-B9zsNTKQ.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="./assets/index-_5Ec1Hmd.css">
|
<link rel="stylesheet" crossorigin href="./assets/index-_5Ec1Hmd.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Reference in New Issue
Block a user